This represents an action that is attached to a face. Actions should be constructed by calling {Puppet::Interface::ActionManager#action}, which is available on {Puppet::Interface}, and then calling methods of {Puppet::Interface::ActionBuilder} in the supplied block. @api private
Whether this is the default action for the face @return [Boolean] @api private
The face this action is attached to @return [Puppet::Interface]
The name of this action @return [Symbol]
The arity of the action @return [Integer]
@api private @return [Symbol]
The block that is executed when the action is invoked @return [block]
@api private
# File lib/puppet/interface/action.rb, line 17 def initialize(face, name, attrs = {}) raise "#{name.inspect} is an invalid action name" unless name.to_s =~ /^[a-z]\w*$/ @face = face @name = name.to_sym # The few bits of documentation we actually demand. The default license # is a favour to our end users; if you happen to get that in a core face # report it as a bug, please. --daniel 2011-04-26 @authors = [] @license = 'All Rights Reserved' set_options(attrs) # @options collects the added options in the order they're declared. # @options_hash collects the options keyed by alias for quick lookups. @options = [] @display_global_options = [] @options_hash = {} @when_rendering = {} end
@return [void] @api private
# File lib/puppet/interface/action.rb, line 44 def __dup_and_rebind_to(to) bound_version = self.dup bound_version.instance_variable_set(:@face, to) return bound_version end
# File lib/puppet/interface/action.rb, line 287 def add_display_global_options(*args) @display_global_options ||= [] [args].flatten.each do |refopt| raise ArgumentError, "Global option #{refopt} does not exist in Puppet.settings" unless Puppet.settings.include? refopt @display_global_options << refopt end @display_global_options.uniq! @display_global_options end
# File lib/puppet/interface/action.rb, line 261 def add_option(option) option.aliases.each do |name| if conflict = get_option(name) then raise ArgumentError, "Option #{option} conflicts with existing option #{conflict}" elsif conflict = @face.get_option(name) then raise ArgumentError, "Option #{option} conflicts with existing option #{conflict} on #{@face}" end end @options << option.name option.aliases.each do |name| @options_hash[name] = option end option end
# File lib/puppet/interface/action.rb, line 64 def default? !!@default end
# File lib/puppet/interface/action.rb, line 297 def display_global_options(*args) args ? add_display_global_options(args) : @display_global_options + @face.display_global_options end
# File lib/puppet/interface/action.rb, line 302 def get_option(name, with_inherited_options = true) option = @options_hash[name.to_sym] if option.nil? and with_inherited_options option = @face.get_option(name) end option end
# File lib/puppet/interface/action.rb, line 279 def option?(name) @options_hash.include? name.to_sym end
# File lib/puppet/interface/action.rb, line 283 def options @face.options + @options end
# File lib/puppet/interface/action.rb, line 140 def render_as=(value) @render_as = value.to_sym end
@api private
# File lib/puppet/interface/action.rb, line 97 def set_rendering_method_for(type, proc) unless proc.is_a? Proc msg = "The second argument to set_rendering_method_for must be a Proc" msg += ", not #{proc.class.name}" unless proc.nil? raise ArgumentError, msg end if proc.arity != 1 and proc.arity != (@positional_arg_count + 1) msg = "the when_rendering method for the #{@face.name} face #{name} action " msg += "takes either just one argument, the result of when_invoked, " msg += "or the result plus the #{@positional_arg_count} arguments passed " msg += "to the when_invoked block, not " if proc.arity < 0 then msg += "a variable number" else msg += proc.arity.to_s end raise ArgumentError, msg end unless type.is_a? Symbol raise ArgumentError, "The rendering format must be a symbol, not #{type.class.name}" end if @when_rendering.has_key? type then raise ArgumentError, "You can't define a rendering method for #{type} twice" end # Now, the ugly bit. We add the method to our interface object, and # retrieve it, to rotate through the dance of getting a suitable method # object out of the whole process. --daniel 2011-04-18 @when_rendering[type] = @face.__send__( :__add_method, __render_method_name_for(type), proc) end
# File lib/puppet/interface/action.rb, line 72 def synopsis build_synopsis(@face.name, default? ? nil : name, arguments) end
# File lib/puppet/interface/action.rb, line 50 def to_s() "#{@face}##{@name}" end
# File lib/puppet/interface/action.rb, line 310 def validate_and_clean(original) # The final set of arguments; effectively a hand-rolled shallow copy of # the original, which protects the caller from the surprises they might # get if they passed us a hash and we mutated it... result = {} # Check for multiple aliases for the same option, and canonicalize the # name of the argument while we are about it. overlap = Hash.new do |h, k| h[k] = [] end unknown = [] original.keys.each do |name| if option = get_option(name) then canonical = option.name if result.has_key? canonical overlap[canonical] << name else result[canonical] = original[name] end elsif Puppet.settings.include? name result[name] = original[name] else unknown << name end end unless overlap.empty? msg = overlap.map {|k, v| "(#{k}, #{v.sort.join(', ')})" }.join(", ") raise ArgumentError, "Multiple aliases for the same option passed: #{msg}" end unless unknown.empty? msg = unknown.sort.join(", ") raise ArgumentError, "Unknown options passed: #{msg}" end # Inject default arguments and check for missing mandating options. missing = [] options.map {|x| get_option(x) }.each do |option| name = option.name next if result.has_key? name if option.has_default? result[name] = option.default elsif option.required? missing << name end end unless missing.empty? msg = missing.sort.join(', ') raise ArgumentError, "The following options are required: #{msg}" end # All done. return result end
# File lib/puppet/interface/action.rb, line 208 def when_invoked=(block) internal_name = "#{@name} implementation, required on Ruby 1.8".to_sym arity = @positional_arg_count = block.arity if arity == 0 then # This will never fire on 1.8.7, which treats no arguments as "*args", # but will on 1.9.2, which treats it as "no arguments". Which bites, # because this just begs for us to wind up in the horrible situation # where a 1.8 vs 1.9 error bites our end users. --daniel 2011-04-19 raise ArgumentError, "when_invoked requires at least one argument (options) for action #{@name}" elsif arity > 0 then range = Range.new(1, arity - 1) decl = range.map { |x| "arg#{x}" } << "options = {}" optn = "" args = "[" + (range.map { |x| "arg#{x}" } << "options").join(", ") + "]" else range = Range.new(1, arity.abs - 1) decl = range.map { |x| "arg#{x}" } << "*rest" optn = "rest << {} unless rest.last.is_a?(Hash)" if arity == -1 then args = "rest" else args = "[" + range.map { |x| "arg#{x}" }.join(", ") + "] + rest" end end file = __FILE__ + "+eval[wrapper]" line = __LINE__ + 2 # <== points to the same line as 'def' in the wrapper. wrapper = <<WRAPPER def #{@name}(#{decl.join(", ")}) #{optn} args = #{args} action = get_action(#{name.inspect}) args << action.validate_and_clean(args.pop) __invoke_decorations(:before, action, args, args.last) rval = self.__send__(#{internal_name.inspect}, *args) __invoke_decorations(:after, action, args, args.last) return rval end WRAPPER if @face.is_a?(Class) @face.class_eval do eval wrapper, nil, file, line end @face.send(:define_method, internal_name, &block) @when_invoked = @face.instance_method(name) else @face.instance_eval do eval wrapper, nil, file, line end @face.meta_def(internal_name, &block) @when_invoked = @face.method(name).unbind end end
@api private
# File lib/puppet/interface/action.rb, line 81 def when_rendering(type) unless type.is_a? Symbol raise ArgumentError, "The rendering format must be a symbol, not #{type.class.name}" end # Do we have a rendering hook for this name? return @when_rendering[type].bind(@face) if @when_rendering.has_key? type # How about by another name? alt = type.to_s.sub(/^to_/, '').to_sym return @when_rendering[alt].bind(@face) if @when_rendering.has_key? alt # Guess not, nothing to run. return nil end