diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb index 87906c577..e74e821b5 100644 --- a/lib/puppet/interface/action_manager.rb +++ b/lib/puppet/interface/action_manager.rb @@ -1,101 +1,89 @@ # This class is not actually public API, but the method # {Puppet::Interface::ActionManager#action action} is public when used # as part of the Faces DSL (i.e. from within a # {Puppet::Interface.define define} block). # @api public module Puppet::Interface::ActionManager # Declare that this app can take a specific action, and provide # the code to do so. # Defines a new action. This takes a block to build the action using # the methods on {Puppet::Interface::ActionBuilder}. # @param name [Symbol] The name that will be used to invoke the # action # @overload action(name, {|| block}) # @return [void] # @api public # @dsl Faces def action(name, &block) @actions ||= {} Puppet.warning "Redefining action #{name} for #{self}" if action?(name) action = Puppet::Interface::ActionBuilder.build(self, name, &block) # REVISIT: (#18042) doesn't this mean we can't redefine the default action? -- josh if action.default and current = get_default_action raise "Actions #{current.name} and #{name} cannot both be default" end @actions[action.name] = action end - # Defines an action without using ActionBuilder. The block given is - # the code that will be executed when the action is invoked. - # @api public - # @deprecated - def script(name, &block) - @actions ||= {} - Puppet.warning "Redefining action #{name} for #{self}" if action?(name) - - # REVISIT: (#18048) it's possible to create multiple default actions - @actions[name] = Puppet::Interface::Action.new(self, name, :when_invoked => block) - end - # Returns the list of available actions for this face. # @return [Array] The names of the actions for this face # @api private def actions @actions ||= {} result = @actions.keys if self.is_a?(Class) and superclass.respond_to?(:actions) result += superclass.actions elsif self.class.respond_to?(:actions) result += self.class.actions end # We need to uniq the result, because we duplicate actions when they are # fetched to ensure that they have the correct bindings; they shadow the # parent, and uniq implements that. --daniel 2011-06-01 result.uniq.sort end # Retrieves a named action # @param name [Symbol] The name of the action # @return [Puppet::Interface::Action] The action object # @api private def get_action(name) @actions ||= {} result = @actions[name.to_sym] if result.nil? if self.is_a?(Class) and superclass.respond_to?(:get_action) found = superclass.get_action(name) elsif self.class.respond_to?(:get_action) found = self.class.get_action(name) end if found then # This is not the nicest way to make action equivalent to the Ruby # Method object, rather than UnboundMethod, but it will do for now, # and we only have to make this change in *one* place. --daniel 2011-04-12 result = @actions[name.to_sym] = found.__dup_and_rebind_to(self) end end return result end # Retrieves the default action for the face # @return [Puppet::Interface::Action] # @api private def get_default_action default = actions.map {|x| get_action(x) }.select {|x| x.default } if default.length > 1 raise "The actions #{default.map(&:name).join(", ")} cannot all be default" end default.first end # @api private def action?(name) actions.include?(name.to_sym) end end diff --git a/spec/lib/puppet/face/1.0.0/huzzah.rb b/spec/lib/puppet/face/1.0.0/huzzah.rb index 8a1311704..a11527370 100755 --- a/spec/lib/puppet/face/1.0.0/huzzah.rb +++ b/spec/lib/puppet/face/1.0.0/huzzah.rb @@ -1,8 +1,8 @@ require 'puppet/face' Puppet::Face.define(:huzzah, '1.0.0') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "life is a thing for celebration" - script :obsolete_in_core do |_| "you are in obsolete core now!" end - script :call_newer do |_| method_on_newer end + action(:obsolete_in_core) { when_invoked { |_| "you are in obsolete core now!" } } + action(:call_newer) { when_invoked { |_| method_on_newer } } end diff --git a/spec/lib/puppet/face/huzzah.rb b/spec/lib/puppet/face/huzzah.rb index f3d18a797..a54e9be2a 100755 --- a/spec/lib/puppet/face/huzzah.rb +++ b/spec/lib/puppet/face/huzzah.rb @@ -1,8 +1,8 @@ require 'puppet/face' Puppet::Face.define(:huzzah, '2.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "life is a thing for celebration" - script :bar do |options| "is where beer comes from" end - script :call_older do |_| method_on_older end + action(:bar) { when_invoked { |options| "is where beer comes from" } } + action(:call_older) { when_invoked { |_| method_on_older } } end diff --git a/spec/lib/puppet/face/version_matching.rb b/spec/lib/puppet/face/version_matching.rb index bfdf5e36e..bad35705a 100644 --- a/spec/lib/puppet/face/version_matching.rb +++ b/spec/lib/puppet/face/version_matching.rb @@ -1,12 +1,12 @@ require 'puppet/face' # The set of versions here are used explicitly in the interface_spec; if you # change this you need to ensure that is still correct. --daniel 2011-04-21 ['1.0.0', '1.0.1', '1.1.0', '1.1.1', '2.0.0'].each do |version| Puppet::Face.define(:version_matching, version) do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "version matching face #{version}" - script :version do |options| version end + action(:version) { when_invoked { |options| version } } end end diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb index b1cc89fee..9b310473b 100755 --- a/spec/unit/interface/action_manager_spec.rb +++ b/spec/unit/interface/action_manager_spec.rb @@ -1,280 +1,255 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/interface' class ActionManagerTester include Puppet::Interface::ActionManager end describe Puppet::Interface::ActionManager do subject { ActionManagerTester.new } describe "when included in a class" do it "should be able to define an action" do subject.action(:foo) do when_invoked { |options| "something "} end end - it "should be able to define a 'script' style action" do - subject.script :bar do |options| - "a bar is where beer is found" - end - end - it "should be able to list defined actions" do subject.action(:foo) do when_invoked { |options| "something" } end subject.action(:bar) do when_invoked { |options| "something" } end subject.actions.should =~ [:foo, :bar] end - it "should list 'script' actions" do - subject.script :foo do |options| "foo" end - subject.actions.should =~ [:foo] - end - - it "should list both script and normal actions" do - subject.action :foo do - when_invoked do |options| "foo" end - end - subject.script :bar do |options| "a bar is where beer is found" end - - subject.actions.should =~ [:foo, :bar] - end - it "should be able to indicate when an action is defined" do subject.action(:foo) do when_invoked { |options| "something" } end subject.should be_action(:foo) end - it "should indicate an action is defined for script actions" do - subject.script :foo do |options| "foo" end - subject.should be_action :foo - end - it "should correctly treat action names specified as strings" do subject.action(:foo) do when_invoked { |options| "something" } end subject.should be_action("foo") end end describe "when used to extend a class" do subject { Class.new.extend(Puppet::Interface::ActionManager) } it "should be able to define an action" do subject.action(:foo) do when_invoked { |options| "something "} end end it "should be able to list defined actions" do subject.action(:foo) do when_invoked { |options| "something" } end subject.action(:bar) do when_invoked { |options| "something" } end subject.actions.should include(:bar) subject.actions.should include(:foo) end it "should be able to indicate when an action is defined" do subject.action(:foo) { when_invoked do |options| true end } subject.should be_action(:foo) end end describe "when used both at the class and instance level" do before do @klass = Class.new do include Puppet::Interface::ActionManager extend Puppet::Interface::ActionManager def __invoke_decorations(*args) true end def options() [] end end @instance = @klass.new end it "should be able to define an action at the class level" do @klass.action(:foo) do when_invoked { |options| "something "} end end it "should create an instance method when an action is defined at the class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.foo.should == "something" end it "should be able to define an action at the instance level" do @instance.action(:foo) do when_invoked { |options| "something "} end end it "should create an instance method when an action is defined at the instance level" do @instance.action(:foo) do when_invoked { |options| "something" } end @instance.foo.should == "something" end it "should be able to list actions defined at the class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @klass.action(:bar) do when_invoked { |options| "something" } end @klass.actions.should include(:bar) @klass.actions.should include(:foo) end it "should be able to list actions defined at the instance level" do @instance.action(:foo) do when_invoked { |options| "something" } end @instance.action(:bar) do when_invoked { |options| "something" } end @instance.actions.should include(:bar) @instance.actions.should include(:foo) end it "should be able to list actions defined at both instance and class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.action(:bar) do when_invoked { |options| "something" } end @instance.actions.should include(:bar) @instance.actions.should include(:foo) end it "should be able to indicate when an action is defined at the class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.should be_action(:foo) end it "should be able to indicate when an action is defined at the instance level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.should be_action(:foo) end context "with actions defined in superclass" do before :each do @subclass = Class.new(@klass) @instance = @subclass.new @klass.action(:parent) do when_invoked { |options| "a" } end @subclass.action(:sub) do when_invoked { |options| "a" } end @instance.action(:instance) do when_invoked { |options| "a" } end end it "should list actions defined in superclasses" do @instance.should be_action(:parent) @instance.should be_action(:sub) @instance.should be_action(:instance) end it "should list inherited actions" do @instance.actions.should =~ [:instance, :parent, :sub] end it "should not duplicate instance actions after fetching them (#7699)" do @instance.actions.should =~ [:instance, :parent, :sub] @instance.get_action(:instance) @instance.actions.should =~ [:instance, :parent, :sub] end it "should not duplicate subclass actions after fetching them (#7699)" do @instance.actions.should =~ [:instance, :parent, :sub] @instance.get_action(:sub) @instance.actions.should =~ [:instance, :parent, :sub] end it "should not duplicate superclass actions after fetching them (#7699)" do @instance.actions.should =~ [:instance, :parent, :sub] @instance.get_action(:parent) @instance.actions.should =~ [:instance, :parent, :sub] end end it "should create an instance method when an action is defined in a superclass" do @subclass = Class.new(@klass) @instance = @subclass.new @klass.action(:foo) do when_invoked { |options| "something" } end @instance.foo.should == "something" end end describe "#action" do it 'should add an action' do subject.action(:foo) { when_invoked do |options| true end } subject.get_action(:foo).should be_a Puppet::Interface::Action end it 'should support default actions' do subject.action(:foo) { when_invoked do |options| true end; default } subject.get_default_action.should == subject.get_action(:foo) end it 'should not support more than one default action' do subject.action(:foo) { when_invoked do |options| true end; default } expect { subject.action(:bar) { when_invoked do |options| true end default } }.to raise_error /cannot both be default/ end end describe "#get_action" do let :parent_class do parent_class = Class.new(Puppet::Interface) parent_class.action(:foo) { when_invoked do |options| true end } parent_class end it "should check that we can find inherited actions when we are a class" do Class.new(parent_class).get_action(:foo).name.should == :foo end it "should check that we can find inherited actions when we are an instance" do instance = parent_class.new(:foo, '0.0.0') instance.get_action(:foo).name.should == :foo end end end diff --git a/spec/unit/interface/action_spec.rb b/spec/unit/interface/action_spec.rb index 1eabfc5e1..d21b5963e 100755 --- a/spec/unit/interface/action_spec.rb +++ b/spec/unit/interface/action_spec.rb @@ -1,650 +1,650 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/interface' describe Puppet::Interface::Action do describe "when validating the action name" do [nil, '', 'foo bar', '-foobar'].each do |input| it "should treat #{input.inspect} as an invalid name" do expect { Puppet::Interface::Action.new(nil, input) }.to raise_error(/is an invalid action name/) end end end describe "#when_invoked=" do it "should fail if the block has arity 0" do pending "Ruby 1.8 (painfully) treats argument-free blocks as arity -1" if RUBY_VERSION =~ /^1\.8/ expect { Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked { } end end }.to raise_error ArgumentError, /foo/ end it "should work with arity 1 blocks" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|one| } end end # -1, because we use option defaulting. :( face.method(:foo).arity.should == -1 end it "should work with arity 2 blocks" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|one, two| } end end # -2, because we use option defaulting. :( face.method(:foo).arity.should == -2 end it "should work with arity 1 blocks that collect arguments" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|*one| } end end # -1, because we use only varargs face.method(:foo).arity.should == -1 end it "should work with arity 2 blocks that collect arguments" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|one, *two| } end end # -2, because we take one mandatory argument, and one varargs face.method(:foo).arity.should == -2 end end describe "when invoking" do it "should be able to call other actions on the same object" do face = Puppet::Interface.new(:my_face, '0.0.1') do action(:foo) do when_invoked { |options| 25 } end action(:bar) do when_invoked { |options| "the value of foo is '#{foo}'" } end end face.foo.should == 25 face.bar.should == "the value of foo is '25'" end # bar is a class action calling a class action # quux is a class action calling an instance action # baz is an instance action calling a class action # qux is an instance action calling an instance action it "should be able to call other actions on the same object when defined on a class" do class Puppet::Interface::MyInterfaceBaseClass < Puppet::Interface action(:foo) do when_invoked { |options| 25 } end action(:bar) do when_invoked { |options| "the value of foo is '#{foo}'" } end action(:quux) do when_invoked { |options| "qux told me #{qux}" } end end face = Puppet::Interface::MyInterfaceBaseClass.new(:my_inherited_face, '0.0.1') do action(:baz) do when_invoked { |options| "the value of foo in baz is '#{foo}'" } end action(:qux) do when_invoked { |options| baz } end end face.foo.should == 25 face.bar.should == "the value of foo is '25'" face.quux.should == "qux told me the value of foo in baz is '25'" face.baz.should == "the value of foo in baz is '25'" face.qux.should == "the value of foo in baz is '25'" end context "when calling the Ruby API" do let :face do Puppet::Interface.new(:ruby_api, '1.0.0') do action :bar do option "--bar" when_invoked do |*args| args.last end end end end it "should work when no options are supplied" do options = face.bar options.should == {} end it "should work when options are supplied" do options = face.bar(:bar => "beer") options.should == { :bar => "beer" } end it "should call #validate_and_clean on the action when invoked" do face.get_action(:bar).expects(:validate_and_clean).with({}).returns({}) face.bar 1, :two, 'three' end end end describe "with action-level options" do it "should support options with an empty block" do face = Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| true end option "--bar" do # this line left deliberately blank end end end face.should_not be_option :bar face.get_action(:foo).should be_option :bar end it "should return only action level options when there are no face options" do face = Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| true end option "--bar" end end face.get_action(:foo).options.should =~ [:bar] end describe "option aliases" do let :option do action.get_option :bar end let :action do face.get_action :foo end let :face do Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| options end option "--bar", "--foo", "-b" end end end it "should only list options and not aliases" do action.options.should =~ [:bar] end it "should use the canonical option name when passed aliases" do name = option.name option.aliases.each do |input| face.foo(input => 1).should == { name => 1 } end end end describe "with both face and action options" do let :face do Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| true end ; option "--bar" end action :baz do when_invoked do |options| true end ; option "--bim" end option "--quux" end end it "should return combined face and action options" do face.get_action(:foo).options.should =~ [:bar, :quux] end it "should fetch options that the face inherited" do parent = Class.new(Puppet::Interface) parent.option "--foo" child = parent.new(:inherited_options, '0.0.1') do option "--bar" action :action do when_invoked do |options| true end option "--baz" end end action = child.get_action(:action) action.should be [:baz, :bar, :foo].each do |name| action.get_option(name).should be_an_instance_of Puppet::Interface::Option end end it "should get an action option when asked" do face.get_action(:foo).get_option(:bar). should be_an_instance_of Puppet::Interface::Option end it "should get a face option when asked" do face.get_action(:foo).get_option(:quux). should be_an_instance_of Puppet::Interface::Option end it "should return options only for this action" do face.get_action(:baz).options.should =~ [:bim, :quux] end end it_should_behave_like "things that declare options" do def add_options_to(&block) face = Puppet::Interface.new(:with_options, '0.0.1') do action(:foo) do when_invoked do |options| true end self.instance_eval &block end end face.get_action(:foo) end end it "should fail when a face option duplicates an action option" do expect { Puppet::Interface.new(:action_level_options, '0.0.1') do option "--foo" action :bar do option "--foo" end end }.to raise_error ArgumentError, /Option foo conflicts with existing option foo/i end it "should fail when a required action option is not provided" do face = Puppet::Interface.new(:required_action_option, '0.0.1') do action(:bar) do option('--foo') { required } when_invoked {|options| } end end expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/ end it "should fail when a required face option is not provided" do face = Puppet::Interface.new(:required_face_option, '0.0.1') do option('--foo') { required } action(:bar) { when_invoked {|options| } } end expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/ end end context "with decorators" do context "declared locally" do let :face do Puppet::Interface.new(:action_decorators, '0.0.1') do action :bar do when_invoked do |options| true end end def reported; @reported; end def report(arg) (@reported ||= []) << arg end end end it "should execute before advice on action options in declaration order" do face.action(:boo) do option("--foo") { before_action { |_,_,_| report :foo } } option("--bar", '-b') { before_action { |_,_,_| report :bar } } option("-q", "--quux") { before_action { |_,_,_| report :quux } } option("-f") { before_action { |_,_,_| report :f } } option("--baz") { before_action { |_,_,_| report :baz } } when_invoked {|options| } end face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ] end it "should execute after advice on action options in declaration order" do face.action(:boo) do option("--foo") { after_action { |_,_,_| report :foo } } option("--bar", '-b') { after_action { |_,_,_| report :bar } } option("-q", "--quux") { after_action { |_,_,_| report :quux } } option("-f") { after_action { |_,_,_| report :f } } option("--baz") { after_action { |_,_,_| report :baz } } when_invoked {|options| } end face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ].reverse end it "should execute before advice on face options in declaration order" do face.instance_eval do option("--foo") { before_action { |_,_,_| report :foo } } option("--bar", '-b') { before_action { |_,_,_| report :bar } } option("-q", "--quux") { before_action { |_,_,_| report :quux } } option("-f") { before_action { |_,_,_| report :f } } option("--baz") { before_action { |_,_,_| report :baz } } end - face.script(:boo) {|options| } + face.action(:boo) { when_invoked { |options| } } face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ] end it "should execute after advice on face options in declaration order" do face.instance_eval do option("--foo") { after_action { |_,_,_| report :foo } } option("--bar", '-b') { after_action { |_,_,_| report :bar } } option("-q", "--quux") { after_action { |_,_,_| report :quux } } option("-f") { after_action { |_,_,_| report :f } } option("--baz") { after_action { |_,_,_| report :baz } } end - face.script(:boo) {|options| } + face.action(:boo) { when_invoked { |options| } } face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ].reverse end it "should execute before advice on face options before action options" do face.instance_eval do option("--face-foo") { before_action { |_,_,_| report :face_foo } } option("--face-bar", '-r') { before_action { |_,_,_| report :face_bar } } action(:boo) do option("--action-foo") { before_action { |_,_,_| report :action_foo } } option("--action-bar", '-b') { before_action { |_,_,_| report :action_bar } } option("-q", "--action-quux") { before_action { |_,_,_| report :action_quux } } option("-a") { before_action { |_,_,_| report :a } } option("--action-baz") { before_action { |_,_,_| report :action_baz } } when_invoked {|options| } end option("-u", "--face-quux") { before_action { |_,_,_| report :face_quux } } option("-f") { before_action { |_,_,_| report :f } } option("--face-baz") { before_action { |_,_,_| report :face_baz } } end expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz, :action_foo, :action_bar, :action_quux, :a, :action_baz ] face.boo Hash[ *expected_calls.zip([]).flatten ] face.reported.should == expected_calls end it "should execute after advice on face options in declaration order" do face.instance_eval do option("--face-foo") { after_action { |_,_,_| report :face_foo } } option("--face-bar", '-r') { after_action { |_,_,_| report :face_bar } } action(:boo) do option("--action-foo") { after_action { |_,_,_| report :action_foo } } option("--action-bar", '-b') { after_action { |_,_,_| report :action_bar } } option("-q", "--action-quux") { after_action { |_,_,_| report :action_quux } } option("-a") { after_action { |_,_,_| report :a } } option("--action-baz") { after_action { |_,_,_| report :action_baz } } when_invoked {|options| } end option("-u", "--face-quux") { after_action { |_,_,_| report :face_quux } } option("-f") { after_action { |_,_,_| report :f } } option("--face-baz") { after_action { |_,_,_| report :face_baz } } end expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz, :action_foo, :action_bar, :action_quux, :a, :action_baz ] face.boo Hash[ *expected_calls.zip([]).flatten ] face.reported.should == expected_calls.reverse end it "should not invoke a decorator if the options are empty" do face.option("--foo FOO") { before_action { |_,_,_| report :before_action } } face.expects(:report).never face.bar end context "passing a subset of the options" do before :each do face.option("--foo") { before_action { |_,_,_| report :foo } } face.option("--bar") { before_action { |_,_,_| report :bar } } end it "should invoke only foo's advice when passed only 'foo'" do face.bar(:foo => true) face.reported.should == [ :foo ] end it "should invoke only bar's advice when passed only 'bar'" do face.bar(:bar => true) face.reported.should == [ :bar ] end it "should invoke advice for all passed options" do face.bar(:foo => true, :bar => true) face.reported.should == [ :foo, :bar ] end end end context "and inheritance" do let :parent do Class.new(Puppet::Interface) do - script(:on_parent) {|options| :on_parent } + action(:on_parent) { when_invoked { |options| :on_parent } } def reported; @reported; end def report(arg) (@reported ||= []) << arg end end end let :child do parent.new(:inherited_decorators, '0.0.1') do - script(:on_child) {|options| :on_child } + action(:on_child) { when_invoked { |options| :on_child } } end end context "locally declared face options" do subject do child.option("--foo=") { before_action { |_,_,_| report :child_before } } child end it "should be invoked when calling a child action" do subject.on_child(:foo => true).should == :on_child subject.reported.should == [ :child_before ] end it "should be invoked when calling a parent action" do subject.on_parent(:foo => true).should == :on_parent subject.reported.should == [ :child_before ] end end context "inherited face option decorators" do subject do parent.option("--foo=") { before_action { |_,_,_| report :parent_before } } child end it "should be invoked when calling a child action" do subject.on_child(:foo => true).should == :on_child subject.reported.should == [ :parent_before ] end it "should be invoked when calling a parent action" do subject.on_parent(:foo => true).should == :on_parent subject.reported.should == [ :parent_before ] end end context "with both inherited and local face options" do # Decorations should be invoked in declaration order, according to # inheritance (e.g. parent class options should be handled before # subclass options). subject do child.option "-c" do before_action { |action, args, options| report :c_before } after_action { |action, args, options| report :c_after } end parent.option "-a" do before_action { |action, args, options| report :a_before } after_action { |action, args, options| report :a_after } end child.option "-d" do before_action { |action, args, options| report :d_before } after_action { |action, args, options| report :d_after } end parent.option "-b" do before_action { |action, args, options| report :b_before } after_action { |action, args, options| report :b_after } end - child.script(:decorations) { |options| report :invoked } + child.action(:decorations) { when_invoked { |options| report :invoked } } child end it "should invoke all decorations when calling a child action" do subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1) subject.reported.should == [ :a_before, :b_before, :c_before, :d_before, :invoked, :d_after, :c_after, :b_after, :a_after ] end it "should invoke all decorations when calling a parent action" do subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1) subject.reported.should == [ :a_before, :b_before, :c_before, :d_before, :invoked, :d_after, :c_after, :b_after, :a_after ] end end end end it_should_behave_like "documentation on faces" do subject do face = Puppet::Interface.new(:action_documentation, '0.0.1') do action :documentation do when_invoked do |options| true end end end face.get_action(:documentation) end end context "#when_rendering" do it "should fail if no type is given when_rendering" it "should accept a when_rendering block" it "should accept multiple when_rendering blocks" it "should fail if when_rendering gets a non-symbol identifier" it "should fail if a second block is given for the same type" it "should return the block if asked" end context "#validate_and_clean" do subject do Puppet::Interface.new(:validate_args, '1.0.0') do - script :test do |options| options end + action(:test) { when_invoked { |options| options } } end end it "should fail if a required option is not passed" do subject.option "--foo" do required end expect { subject.test }.to raise_error ArgumentError, /options are required/ end it "should fail if two aliases to one option are passed" do subject.option "--foo", "-f" expect { subject.test :foo => true, :f => true }. to raise_error ArgumentError, /Multiple aliases for the same option/ end it "should fail if an unknown option is passed" do expect { subject.test :unknown => true }. to raise_error ArgumentError, /Unknown options passed: unknown/ end it "should report all the unknown options passed" do expect { subject.test :unknown => true, :unseen => false }. to raise_error ArgumentError, /Unknown options passed: unknown, unseen/ end it "should accept 'global' options from settings" do expect { subject.test(:certname => "true").should == { :certname => "true" } }.not_to raise_error end end context "default option values" do subject do Puppet::Interface.new(:default_option_values, '1.0.0') do action :foo do option "--foo" do end option "--bar" do end when_invoked do |options| options end end end end let :action do subject.get_action :foo end let :option do action.get_option :foo end it "should not add options without defaults" do subject.foo.should == {} end it "should not add options without defaults, if options are given" do subject.foo(:bar => 1).should == { :bar => 1 } end it "should add the option default value when set" do option.default = proc { 12 } subject.foo.should == { :foo => 12 } end it "should add the option default value when set, if other options are given" do option.default = proc { 12 } subject.foo(:bar => 1).should == { :foo => 12, :bar => 1 } end it "should invoke the same default proc every time called" do option.default = proc { @foo ||= {} } subject.foo[:foo].object_id.should == subject.foo[:foo].object_id end [nil, 0, 1, true, false, {}, []].each do |input| it "should not override a passed option (#{input.inspect})" do option.default = proc { :fail } subject.foo(:foo => input).should == { :foo => input } end end end context "runtime manipulations" do subject do Puppet::Interface.new(:runtime_manipulations, '1.0.0') do action :foo do when_invoked do |options| options end end end end let :action do subject.get_action :foo end it "should be the face default action if default is set true" do subject.get_default_action.should be_nil action.default = true subject.get_default_action.should == action end end end