class EcoRake module Options module Forwarding # Collection of forwarding rules. # @note rules keep associated with the option `name` (not their `short`) # * The above means that changing the name of an exisint option will brake the link with # a forwarding rule (as they do not auto-update on option name change) class Proxy include Enumerable attr_reader :parent # @param parent [RakeCommander::Options::Class] def initialize(parent) pklass = parent.is_a?(Class)? parent : parent.class msg = "Expecting parent to be RakeCommander::Options::Class. Given: #{pklass}" raise ArgumentError, msg unless parent.is_a?(Class) && parent <= RakeCommander::Options @parent = parent @rules = {} end def each(&block) return to_enum(:each) unless block_given? @rules.values.each(&block) end # @return [EcoRake::Options::Forwarding::Proxy] with a copy of the rules. def dup(parent, override: true, require_option: false) self.class.new(parent).tap do |rely| each {|rule| rely.add_rule(rule.deep_dup, override: override, require_option: require_option)} end end alias_method :deep_dup, :dup # @return [EcoRake::Options::Forwarding::Rule] def [](value) @rules[key(value)] end # Adds a new rule or overrides an existing one. # @see #add_rule def add(sym, rule, override: true, require_option: true) add_rule( EcoRake::Options::Forwarding::Rule.new(key(sym) || sym, rule, parent: self), override: override, require_option: require_option ) end # @param ref [Symbol, EcoRake::Options::Forwarding::Rule, EcoRake::Option] # @return [EcoRake::Options::Forwarding::Rule] the rule that has been deleted. def delete(ref) @rules.delete(key(ref)) end # It applies the rule associated with `sym` to `from` parsed option results. # @param sym [Symbol] the `short` or `name` of an existing option. # @param from [Hash] the parsed option results. # @return [Value] the value that the rule returns. def forward(sym, from:) self[sym]&.apply(from) end protected # @note # 1. It fixes the `rule.sym` to the option `name`, if option exists. # 2. By default, it does not require the target option to exist. # 3. If `override`, it will warn on overriding, but if `false` it will just silently fail # @raise ArgumentError if there isn't an option in `parent` with and `require_option` is `true` # @raise ArgumentError if there `sym` has already a rule and `override` is `false` # @param sym [Symbol] the `short` or `name` of an existing option. # @param rule [String, Proc] the rule associated wtih `sym`. # @param override [Boolean] whether overriding existing rule is allowed. # @param require_option [Boolean] whether an option with `sym` as short or name must exist. # @param warning [Boolean] whether an override should warn. # @return [Boolean, EcoRake::Options::Forwarding::Rule] the added rule if succeeded, and `false` otherwise. def add_rule(rule, override: true, require_option: false, warning: false) msg = "Expecting EcoRake::Options::Forwarding::Rule. Given: #{rule.class}" raise ArgumentError, msg unless rule.is_a?(EcoRake::Options::Forwarding::Rule) msg = "Could not find any defined option associated with '#{rule.sym}'" raise ArgumentError, msg unless (opt = option(rule.sym)) || !require_option rule.sym = opt&.name if existing_rule = self[rule.sym] return false unless override puts "Warning: Overriding forwarding '#{existing_rule.desc}'" if warning end rule.parent = self @rules[rule.sym] = rule end # Retrieves the rule key # 1. The option name of an existing option # 2. The sym of an existing rule # @return [Symbol] def key(value) case value when RakeCommander::Option value.name when Symbol if opt = option(value) key(opt) elsif @rules.key?(value) value end when String key(value.to_sym) when EcoRake::Options::Forwarding::Rule key(value.sym) end end private # Retrieves the option associated to a symbol def option(sym) parent.option_get(sym.to_sym) end end end end end