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