diff --git a/lib/puppet/interface/action.rb b/lib/puppet/interface/action.rb index e4c2a4666..1c19bd08c 100644 --- a/lib/puppet/interface/action.rb +++ b/lib/puppet/interface/action.rb @@ -1,16 +1,26 @@ require 'puppet/interface' class Puppet::Interface::Action attr_reader :name - def initialize(interface, name) + def initialize(interface, name, attrs = {}) name = name.to_s raise "'#{name}' is an invalid action name" unless name =~ /^[a-z]\w*$/ + + attrs.each do |k,v| send("#{k}=", v) end @interface = interface @name = name end def invoke(*args, &block) @interface.method(name).call(*args,&block) end + + def invoke=(block) + if @interface.is_a?(Class) + @interface.define_method(@name, &block) + else + @interface.meta_def(@name, &block) + end + end end diff --git a/lib/puppet/interface/action_builder.rb b/lib/puppet/interface/action_builder.rb index e76fb1c6e..e389ea3ea 100644 --- a/lib/puppet/interface/action_builder.rb +++ b/lib/puppet/interface/action_builder.rb @@ -1,30 +1,27 @@ require 'puppet/interface' require 'puppet/interface/action' class Puppet::Interface::ActionBuilder attr_reader :action def self.build(interface, name, &block) name = name.to_s raise "Action '#{name}' must specify a block" unless block builder = new(interface, name, &block) builder.action end def initialize(interface, name, &block) @interface = interface @action = Puppet::Interface::Action.new(interface, name) instance_eval(&block) end # Ideally the method we're defining here would be added to the action, and a - # method on the interface would defer to it + # method on the interface would defer to it, but we can't get scope correct, + # so we stick with this. --daniel 2011-03-24 def invoke(&block) raise "Invoke called on an ActionBuilder with no corresponding Action" unless @action - if @interface.is_a?(Class) - @interface.define_method(@action.name, &block) - else - @interface.meta_def(@action.name, &block) - end + @action.invoke = block end end diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb index 0db82d612..8b2944bb1 100644 --- a/lib/puppet/interface/action_manager.rb +++ b/lib/puppet/interface/action_manager.rb @@ -1,36 +1,45 @@ require 'puppet/interface/action_builder' module Puppet::Interface::ActionManager # Declare that this app can take a specific action, and provide # the code to do so. def action(name, &block) @actions ||= {} name = name.to_s.downcase.to_sym raise "Action #{name} already defined for #{self}" if action?(name) action = Puppet::Interface::ActionBuilder.build(self, name, &block) @actions[name] = action end + # This is the short-form of an action definition; it doesn't use the + # builder, just creates the action directly from the block. + def script(name, &block) + @actions ||= {} + name = name.to_s.downcase.to_sym + raise "Action #{name} already defined for #{self}" if action?(name) + @actions[name] = Puppet::Interface::Action.new(self, name, :invoke => block) + end + 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 result.sort end def get_action(name) @actions[name].dup end def action?(name) actions.include?(name.to_sym) end end diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb old mode 100644 new mode 100755 index d1a7e31be..3aff7ac11 --- a/spec/unit/interface/action_manager_spec.rb +++ b/spec/unit/interface/action_manager_spec.rb @@ -1,192 +1,216 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb') # This is entirely an internal class for Interface, so we have to load it instead of our class. 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 invoke { "something "} end end + it "should be able to define a 'script' style action" do + subject.script :bar do + "a bar is where beer is found" + end + end + it "should be able to list defined actions" do subject.action(:foo) do invoke { "something" } end subject.action(:bar) do invoke { "something" } end - subject.actions.should include(:bar) - subject.actions.should include(:foo) + subject.actions.should =~ [:foo, :bar] + end + + it "should list 'script' actions" do + subject.script :foo do "foo" end + subject.actions.should =~ [:foo] + end + + it "should list both script and normal actions" do + subject.action :foo do + invoke do "foo" end + end + subject.script :bar do "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 invoke { "something" } end subject.should be_action(:foo) end + it "should indicate an action is defined for script actions" do + subject.script :foo do "foo" end + subject.should be_action :foo + end + it "should correctly treat action names specified as strings" do subject.action(:foo) do invoke { "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 invoke { "something "} end end it "should be able to list defined actions" do subject.action(:foo) do invoke { "something" } end subject.action(:bar) do invoke { "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) { "something" } 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 end @instance = @klass.new end it "should be able to define an action at the class level" do @klass.action(:foo) do invoke { "something "} end end it "should create an instance method when an action is defined at the class level" do @klass.action(:foo) do invoke { "something" } end @instance.foo.should == "something" end it "should be able to define an action at the instance level" do @instance.action(:foo) do invoke { "something "} end end it "should create an instance method when an action is defined at the instance level" do @instance.action(:foo) do invoke { "something" } end @instance.foo.should == "something" end it "should be able to list actions defined at the class level" do @klass.action(:foo) do invoke { "something" } end @klass.action(:bar) do invoke { "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 invoke { "something" } end @instance.action(:bar) do invoke { "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 invoke { "something" } end @instance.action(:bar) do invoke { "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 invoke { "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 invoke { "something" } end @instance.should be_action(:foo) end it "should list actions defined in superclasses" do @subclass = Class.new(@klass) @instance = @subclass.new @klass.action(:parent) do invoke { "a" } end @subclass.action(:sub) do invoke { "a" } end @instance.action(:instance) do invoke { "a" } end @instance.should be_action(:parent) @instance.should be_action(:sub) @instance.should be_action(:instance) 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 invoke { "something" } end @instance.foo.should == "something" end end end