class Puppet::Interface::Action
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
Attributes
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]
Public Class Methods
@api private
# File lib/puppet/interface/action.rb 14 def initialize(face, name) 15 raise "#{name.inspect} is an invalid action name" unless name.to_s =~ /^[a-z]\w*$/ 16 @face = face 17 @name = name.to_sym 18 19 # The few bits of documentation we actually demand. The default license 20 # is a favour to our end users; if you happen to get that in a core face 21 # report it as a bug, please. --daniel 2011-04-26 22 @authors = [] 23 @license = 'All Rights Reserved' 24 25 # @options collects the added options in the order they're declared. 26 # @options_hash collects the options keyed by alias for quick lookups. 27 @options = [] 28 @display_global_options = [] 29 @options_hash = {} 30 @when_rendering = {} 31 end
Public Instance Methods
@return [void] @api private
# File lib/puppet/interface/action.rb 39 def __dup_and_rebind_to(to) 40 bound_version = self.dup 41 bound_version.instance_variable_set(:@face, to) 42 return bound_version 43 end
# File lib/puppet/interface/action.rb 310 def add_display_global_options(*args) 311 @display_global_options ||= [] 312 [args].flatten.each do |refopt| 313 unless Puppet.settings.include? refopt 314 #TRANSLATORS 'Puppet.settings' should not be translated 315 raise ArgumentError, _("Global option %{option} does not exist in Puppet.settings") % { option: refopt } 316 end 317 @display_global_options << refopt 318 end 319 @display_global_options.uniq! 320 @display_global_options 321 end
# File lib/puppet/interface/action.rb 278 def add_option(option) 279 option.aliases.each do |name| 280 conflict = get_option(name) 281 if conflict 282 raise ArgumentError, _("Option %{option} conflicts with existing option %{conflict}") % 283 { option: option, conflict: conflict } 284 else 285 conflict = @face.get_option(name) 286 if conflict 287 raise ArgumentError, _("Option %{option} conflicts with existing option %{conflict} on %{face}") % 288 { option: option, conflict: conflict, face: @face } 289 end 290 end 291 end 292 293 @options << option.name 294 295 option.aliases.each do |name| 296 @options_hash[name] = option 297 end 298 299 option 300 end
# File lib/puppet/interface/action.rb 59 def default? 60 !!@default 61 end
@api private @return [void]
# File lib/puppet/interface/action.rb 151 def deprecate 152 @deprecated = true 153 end
@api private @return [Boolean]
# File lib/puppet/interface/action.rb 157 def deprecated? 158 @deprecated 159 end
# File lib/puppet/interface/action.rb 323 def display_global_options(*args) 324 args ? add_display_global_options(args) : @display_global_options + @face.display_global_options 325 end
# File lib/puppet/interface/action.rb 328 def get_option(name, with_inherited_options = true) 329 option = @options_hash[name.to_sym] 330 if option.nil? and with_inherited_options 331 option = @face.get_option(name) 332 end 333 option 334 end
# File lib/puppet/interface/action.rb 302 def option?(name) 303 @options_hash.include? name.to_sym 304 end
# File lib/puppet/interface/action.rb 306 def options 307 @face.options + @options 308 end
# File lib/puppet/interface/action.rb 145 def render_as=(value) 146 @render_as = value.to_sym 147 end
@api private
# File lib/puppet/interface/action.rb 92 def set_rendering_method_for(type, proc) 93 unless proc.is_a? Proc 94 msg = if proc.nil? 95 #TRANSLATORS 'set_rendering_method_for' and 'Proc' should not be translated 96 _("The second argument to set_rendering_method_for must be a Proc") 97 else 98 #TRANSLATORS 'set_rendering_method_for' and 'Proc' should not be translated 99 _("The second argument to set_rendering_method_for must be a Proc, not %{class_name}") % 100 { class_name: proc.class.name } 101 end 102 raise ArgumentError, msg 103 end 104 105 if proc.arity != 1 and proc.arity != (@positional_arg_count + 1) 106 msg = if proc.arity < 0 then 107 #TRANSLATORS 'when_rendering', 'when_invoked' are method names and should not be translated 108 _("The when_rendering method for the %{face} face %{name} action takes either just one argument,"\ 109 " the result of when_invoked, or the result plus the %{arg_count} arguments passed to the"\ 110 " when_invoked block, not a variable number") % 111 { face: @face.name, name: name, arg_count: @positional_arg_count } 112 else 113 #TRANSLATORS 'when_rendering', 'when_invoked' are method names and should not be translated 114 _("The when_rendering method for the %{face} face %{name} action takes either just one argument,"\ 115 " the result of when_invoked, or the result plus the %{arg_count} arguments passed to the"\ 116 " when_invoked block, not %{string}") % 117 { face: @face.name, name: name, arg_count: @positional_arg_count, string: proc.arity.to_s } 118 end 119 raise ArgumentError, msg 120 end 121 unless type.is_a? Symbol 122 raise ArgumentError, _("The rendering format must be a symbol, not %{class_name}") % { class_name: type.class.name } 123 end 124 if @when_rendering.has_key? type then 125 raise ArgumentError, _("You can't define a rendering method for %{type} twice") % { type: type } 126 end 127 # Now, the ugly bit. We add the method to our interface object, and 128 # retrieve it, to rotate through the dance of getting a suitable method 129 # object out of the whole process. --daniel 2011-04-18 130 @when_rendering[type] = 131 @face.__send__( :__add_method, __render_method_name_for(type), proc) 132 end
# File lib/puppet/interface/action.rb 67 def synopsis 68 build_synopsis(@face.name, default? ? nil : name, arguments) 69 end
# File lib/puppet/interface/action.rb 45 def to_s() "#{@face}##{@name}" end
# File lib/puppet/interface/action.rb 336 def validate_and_clean(original) 337 # The final set of arguments; effectively a hand-rolled shallow copy of 338 # the original, which protects the caller from the surprises they might 339 # get if they passed us a hash and we mutated it... 340 result = {} 341 342 # Check for multiple aliases for the same option, and canonicalize the 343 # name of the argument while we are about it. 344 overlap = Hash.new do |h, k| h[k] = [] end 345 unknown = [] 346 original.keys.each do |name| 347 option = get_option(name) 348 if option 349 canonical = option.name 350 if result.has_key? canonical 351 overlap[canonical] << name 352 else 353 result[canonical] = original[name] 354 end 355 elsif Puppet.settings.include? name 356 result[name] = original[name] 357 else 358 unknown << name 359 end 360 end 361 362 unless overlap.empty? 363 overlap_list = overlap.map {|k, v| "(#{k}, #{v.sort.join(', ')})" }.join(", ") 364 raise ArgumentError, _("Multiple aliases for the same option passed: %{overlap_list}") % 365 { overlap_list: overlap_list } 366 end 367 368 unless unknown.empty? 369 unknown_list = unknown.sort.join(", ") 370 raise ArgumentError, _("Unknown options passed: %{unknown_list}") % { unknown_list: unknown_list } 371 end 372 373 # Inject default arguments and check for missing mandating options. 374 missing = [] 375 options.map {|x| get_option(x) }.each do |option| 376 name = option.name 377 next if result.has_key? name 378 379 if option.has_default? 380 result[name] = option.default 381 elsif option.required? 382 missing << name 383 end 384 end 385 386 unless missing.empty? 387 missing_list = missing.sort.join(', ') 388 raise ArgumentError, _("The following options are required: %{missing_list}") % { missing_list: missing_list } 389 end 390 391 # All done. 392 return result 393 end
# File lib/puppet/interface/action.rb 224 def when_invoked=(block) 225 226 internal_name = "#{@name} implementation, required on Ruby 1.8".to_sym 227 228 arity = @positional_arg_count = block.arity 229 if arity == 0 then 230 # This will never fire on 1.8.7, which treats no arguments as "*args", 231 # but will on 1.9.2, which treats it as "no arguments". Which bites, 232 # because this just begs for us to wind up in the horrible situation 233 # where a 1.8 vs 1.9 error bites our end users. --daniel 2011-04-19 234 #TRANSLATORS 'when_invoked' should not be translated 235 raise ArgumentError, _("when_invoked requires at least one argument (options) for action %{name}") % { name: @name } 236 elsif arity > 0 then 237 range = Range.new(1, arity - 1) 238 decl = range.map { |x| "arg#{x}" } << "options = {}" 239 optn = "" 240 args = "[" + (range.map { |x| "arg#{x}" } << "options").join(", ") + "]" 241 else 242 range = Range.new(1, arity.abs - 1) 243 decl = range.map { |x| "arg#{x}" } << "*rest" 244 optn = "rest << {} unless rest.last.is_a?(Hash)" 245 if arity == -1 then 246 args = "rest" 247 else 248 args = "[" + range.map { |x| "arg#{x}" }.join(", ") + "] + rest" 249 end 250 end 251 252 file = __FILE__ + "+eval[wrapper]" 253 line = __LINE__ + 2 # <== points to the same line as 'def' in the wrapper. 254 wrapper = <<WRAPPER 255 def #{@name}(#{decl.join(", ")}) 256 #{optn} 257 args = #{args} 258 action = get_action(#{name.inspect}) 259 args << action.validate_and_clean(args.pop) 260 __invoke_decorations(:before, action, args, args.last) 261 rval = self.__send__(#{internal_name.inspect}, *args) 262 __invoke_decorations(:after, action, args, args.last) 263 return rval 264 end 265 WRAPPER 266 267 if @face.is_a?(Class) 268 @face.class_eval do eval wrapper, nil, file, line end 269 @face.send(:define_method, internal_name, &block) 270 @when_invoked = @face.instance_method(name) 271 else 272 @face.instance_eval do eval wrapper, nil, file, line end 273 @face.meta_def(internal_name, &block) 274 @when_invoked = @face.method(name).unbind 275 end 276 end
@api private
# File lib/puppet/interface/action.rb 76 def when_rendering(type) 77 unless type.is_a? Symbol 78 raise ArgumentError, _("The rendering format must be a symbol, not %{class_name}") % { class_name: type.class.name } 79 end 80 # Do we have a rendering hook for this name? 81 return @when_rendering[type].bind(@face) if @when_rendering.has_key? type 82 83 # How about by another name? 84 alt = type.to_s.sub(/^to_/, '').to_sym 85 return @when_rendering[alt].bind(@face) if @when_rendering.has_key? alt 86 87 # Guess not, nothing to run. 88 return nil 89 end
Private Instance Methods
@return [void] @api private
# File lib/puppet/interface/action.rb 401 def __add_method(name, proc) 402 @face.__send__ :__add_method, name, proc 403 end
@return [void] @api private
# File lib/puppet/interface/action.rb 136 def __render_method_name_for(type) 137 :"#{name}_when_rendering_#{type}" 138 end