[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Nick Lewis nick at puppetlabs.com
Tue May 10 08:06:52 UTC 2011


The following commit has been merged in the experimental branch:
commit e3d24865c89bccd0221f3d6d475d350f577ed3fb
Author: Nick Lewis <nick at puppetlabs.com>
Date:   Tue Mar 22 12:54:52 2011 -0700

    (#6814) Create a dedicated Action class
    
    This class will represents an action, and allows us to store metadata for an
    action, and programmatically introspect and invoke them. A helper class
    ActionBuilder represents the DSL for defining an action.
    
    Also defined an "invoke" DSL method to handle the functionality of defining the
    method for an action.
    
    Reviewed-By: Daniel Pittman

diff --git a/lib/puppet/interface/action.rb b/lib/puppet/interface/action.rb
new file mode 100644
index 0000000..e4c2a46
--- /dev/null
+++ b/lib/puppet/interface/action.rb
@@ -0,0 +1,16 @@
+require 'puppet/interface'
+
+class Puppet::Interface::Action
+  attr_reader :name
+
+  def initialize(interface, name)
+    name = name.to_s
+    raise "'#{name}' is an invalid action name" unless name =~ /^[a-z]\w*$/
+    @interface = interface
+    @name = name
+  end
+
+  def invoke(*args, &block)
+    @interface.method(name).call(*args,&block)
+  end
+end
diff --git a/lib/puppet/interface/action_builder.rb b/lib/puppet/interface/action_builder.rb
new file mode 100644
index 0000000..777fcaf
--- /dev/null
+++ b/lib/puppet/interface/action_builder.rb
@@ -0,0 +1,29 @@
+require 'puppet/interface'
+
+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
+  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
+  end
+end
diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb
index 27a9829..8629b4c 100644
--- a/lib/puppet/interface/action_manager.rb
+++ b/lib/puppet/interface/action_manager.rb
@@ -1,32 +1,36 @@
+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 ||= []
+    @actions ||= {}
     name = name.to_s.downcase.to_sym
+
     raise "Action #{name} already defined for #{self}" if action?(name)
 
-    @actions << name
-    if self.is_a?(Class)
-      define_method(name, &block)
-    else
-      meta_def(name, &block)
-    end
+    action = Puppet::Interface::ActionBuilder.build(self, name, &block)
+
+    @actions[name] = action
   end
 
   def actions
-    @actions ||= []
-    result = @actions.dup
+    @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 { |a,b| a.to_s <=> b.to_s }
+    result.sort
+  end
+
+  def get_action(name)
+    @actions[name].dup
   end
 
   def action?(name)
-    actions.include?(name.to_sym)
+    actions.include?(name)
   end
 end
diff --git a/lib/puppet/interface/catalog.rb b/lib/puppet/interface/catalog.rb
index f99d088..6c235e2 100644
--- a/lib/puppet/interface/catalog.rb
+++ b/lib/puppet/interface/catalog.rb
@@ -1,36 +1,40 @@
 require 'puppet/interface/indirector'
 
 Puppet::Interface::Indirector.new(:catalog) do
-  action(:apply) do |catalog|
-    report = Puppet::Transaction::Report.new("apply")
-    report.configuration_version = catalog.version
+  action(:apply) do
+    invoke do |catalog|
+      report = Puppet::Transaction::Report.new("apply")
+      report.configuration_version = catalog.version
 
-    Puppet::Util::Log.newdestination(report)
+      Puppet::Util::Log.newdestination(report)
 
-    begin
-      benchmark(:notice, "Finished catalog run") do
-        catalog.apply(:report => report)
+      begin
+        benchmark(:notice, "Finished catalog run") do
+          catalog.apply(:report => report)
+        end
+      rescue => detail
+        puts detail.backtrace if Puppet[:trace]
+        Puppet.err "Failed to apply catalog: #{detail}"
       end
-    rescue => detail
-      puts detail.backtrace if Puppet[:trace]
-      Puppet.err "Failed to apply catalog: #{detail}"
-    end
 
-    report.finalize_report
-    report
+      report.finalize_report
+      report
+    end
   end
 
-  action(:download) do |certname,facts|
-    Puppet::Resource::Catalog.terminus_class = :rest
-    facts_to_upload = {:facts_format => :b64_zlib_yaml, :facts => CGI.escape(facts.render(:b64_zlib_yaml))}
-    catalog = nil
-    retrieval_duration = thinmark do
-      catalog = Puppet::Interface::Catalog.find(certname, facts_to_upload)
+  action(:download) do
+    invoke do |certname,facts|
+      Puppet::Resource::Catalog.terminus_class = :rest
+      facts_to_upload = {:facts_format => :b64_zlib_yaml, :facts => CGI.escape(facts.render(:b64_zlib_yaml))}
+      catalog = nil
+      retrieval_duration = thinmark do
+        catalog = Puppet::Interface::Catalog.find(certname, facts_to_upload)
+      end
+      catalog = catalog.to_ral
+      catalog.finalize
+      catalog.retrieval_duration = retrieval_duration
+      catalog.write_class_file
+      catalog
     end
-    catalog = catalog.to_ral
-    catalog.finalize
-    catalog.retrieval_duration = retrieval_duration
-    catalog.write_class_file
-    catalog
   end
 end
diff --git a/lib/puppet/interface/catalog/select.rb b/lib/puppet/interface/catalog/select.rb
index 4bb4931..349d9c5 100644
--- a/lib/puppet/interface/catalog/select.rb
+++ b/lib/puppet/interface/catalog/select.rb
@@ -1,8 +1,10 @@
 # Select and show a list of resources of a given type.
-Puppet::Interface::Catalog.action :select do |*args|
-  host = args.shift
-  type = args.shift
-  catalog = Puppet::Resource::Catalog.indirection.find(host)
+Puppet::Interface::Catalog.action :select do
+  invoke do |*args|
+    host = args.shift
+    type = args.shift
+    catalog = Puppet::Resource::Catalog.indirection.find(host)
 
-  catalog.resources.reject { |res| res.type != type }.each { |res| puts res }
+    catalog.resources.reject { |res| res.type != type }.each { |res| puts res }
+  end
 end
diff --git a/lib/puppet/interface/config.rb b/lib/puppet/interface/config.rb
index 501099a..0aecc26 100644
--- a/lib/puppet/interface/config.rb
+++ b/lib/puppet/interface/config.rb
@@ -1,10 +1,10 @@
 require 'puppet/interface'
 
 Puppet::Interface.new(:config) do
-  action(:print) do |*args|
-    if name
+  action(:print) do
+    invoke do |*args|
       Puppet.settings[:configprint] = args.join(",")
+      Puppet.settings.print_config_options
     end
-    Puppet.settings.print_config_options
   end
 end
diff --git a/lib/puppet/interface/configurer.rb b/lib/puppet/interface/configurer.rb
index 42e950f..36953ba 100644
--- a/lib/puppet/interface/configurer.rb
+++ b/lib/puppet/interface/configurer.rb
@@ -1,13 +1,15 @@
 require 'puppet/interface'
 
 Puppet::Interface.new(:configurer) do
-  action(:synchronize) do |certname|
-    facts = Puppet::Interface::Facts.find(certname)
+  action(:synchronize) do
+    invoke do |certname|
+      facts = Puppet::Interface::Facts.find(certname)
 
-    catalog = Puppet::Interface::Catalog.download(certname, facts)
+      catalog = Puppet::Interface::Catalog.download(certname, facts)
 
-    report = Puppet::Interface::Catalog.apply(catalog)
+      report = Puppet::Interface::Catalog.apply(catalog)
 
-    report
+      report
+    end
   end
 end
diff --git a/lib/puppet/interface/facts.rb b/lib/puppet/interface/facts.rb
index 3262745..8843d29 100644
--- a/lib/puppet/interface/facts.rb
+++ b/lib/puppet/interface/facts.rb
@@ -5,12 +5,14 @@ Puppet::Interface::Indirector.new(:facts) do
   set_default_format :yaml
 
   # Upload our facts to the server
-  action(:upload) do |*args|
-    Puppet::Node::Facts.indirection.terminus_class = :facter
-    facts = Puppet::Node::Facts.indirection.find(Puppet[:certname])
-    Puppet::Node::Facts.indirection.terminus_class = :rest
-    Puppet::Node::Facts.indirection.save(facts)
-    Puppet.notice "Uploaded facts for '#{Puppet[:certname]}'"
-    nil
+  action(:upload) do
+    invoke do |*args|
+      Puppet::Node::Facts.indirection.terminus_class = :facter
+      facts = Puppet::Node::Facts.indirection.find(Puppet[:certname])
+      Puppet::Node::Facts.indirection.terminus_class = :rest
+      Puppet::Node::Facts.indirection.save(facts)
+      Puppet.notice "Uploaded facts for '#{Puppet[:certname]}'"
+      nil
+    end
   end
 end
diff --git a/lib/puppet/interface/indirector.rb b/lib/puppet/interface/indirector.rb
index 9c26cc3..485af47 100644
--- a/lib/puppet/interface/indirector.rb
+++ b/lib/puppet/interface/indirector.rb
@@ -10,28 +10,30 @@ class Puppet::Interface::Indirector < Puppet::Interface
     Puppet::Indirector::Terminus.terminus_classes(indirection.to_sym).collect { |t| t.to_s }.sort
   end
 
-  action :destroy do |*args|
-    call_indirection_method(:destroy, *args)
+  action :destroy do
+    invoke { |*args| call_indirection_method(:destroy, *args) }
   end
 
-  action :find do |*args|
-    call_indirection_method(:find, *args)
+  action :find do
+    invoke { |*args| call_indirection_method(:find, *args) }
   end
 
-  action :save do |*args|
-    call_indirection_method(:save, *args)
+  action :save do
+    invoke { |*args| call_indirection_method(:save, *args) }
   end
 
-  action :search do |*args|
-    call_indirection_method(:search, *args)
+  action :search do
+    invoke { |*args| call_indirection_method(:search, *args) }
   end
 
   # Print the configuration for the current terminus class
-  action :info do |*args|
-    if t = indirection.terminus_class
-      puts "Run mode '#{Puppet.run_mode.name}': #{t}"
-    else
-      $stderr.puts "No default terminus class for run mode '#{Puppet.run_mode.name}'"
+  action :info do
+    invoke do |*args|
+      if t = indirection.terminus_class
+        puts "Run mode '#{Puppet.run_mode.name}': #{t}"
+      else
+        $stderr.puts "No default terminus class for run mode '#{Puppet.run_mode.name}'"
+      end
     end
   end
 
diff --git a/lib/puppet/interface/report.rb b/lib/puppet/interface/report.rb
index 4923a4b..e785ae2 100644
--- a/lib/puppet/interface/report.rb
+++ b/lib/puppet/interface/report.rb
@@ -1,13 +1,15 @@
 require 'puppet/interface/indirector'
 
 Puppet::Interface::Indirector.new(:report) do
-  action(:submit) do |report|
-    begin
-      Puppet::Transaction::Report.terminus_class = :rest
-      report.save
-    rescue => detail
-      puts detail.backtrace if Puppet[:trace]
-      Puppet.err "Could not send report: #{detail}"
+  action(:submit) do
+    invoke do |report|
+      begin
+        Puppet::Transaction::Report.terminus_class = :rest
+        report.save
+      rescue => detail
+        puts detail.backtrace if Puppet[:trace]
+        Puppet.err "Could not send report: #{detail}"
+      end
     end
   end
 end
diff --git a/spec/unit/interface/action_builder_spec.rb b/spec/unit/interface/action_builder_spec.rb
new file mode 100644
index 0000000..39b2386
--- /dev/null
+++ b/spec/unit/interface/action_builder_spec.rb
@@ -0,0 +1,30 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
+require 'puppet/interface/action_builder'
+
+describe Puppet::Interface::ActionBuilder do
+  describe "::build" do
+    it "should build an action" do
+      action = Puppet::Interface::ActionBuilder.build(nil,:foo) do
+      end
+      action.should be_a(Puppet::Interface::Action)
+      action.name.should == "foo"
+    end
+
+    it "should define a method on the interface which invokes the action" do
+      interface = Puppet::Interface.new(:action_builder_test_interface)
+      action = Puppet::Interface::ActionBuilder.build(interface, :foo) do
+        invoke do
+          "invoked the method"
+        end
+      end
+
+      interface.foo.should == "invoked the method"
+    end
+
+    it "should require a block" do
+      lambda { Puppet::Interface::ActionBuilder.build(nil,:foo) }.should raise_error("Action 'foo' must specify a block")
+    end
+  end
+end
diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb
index b71aeca..0b12db3 100644
--- a/spec/unit/interface/action_manager_spec.rb
+++ b/spec/unit/interface/action_manager_spec.rb
@@ -16,19 +16,28 @@ describe Puppet::Interface::ActionManager do
 
   describe "when included in a class" do
     it "should be able to define an action" do
-      @tester.action(:foo) { "something "}
+      @tester.action(:foo) do
+        invoke { "something "}
+      end
     end
 
     it "should be able to list defined actions" do
-      @tester.action(:foo) { "something" }
-      @tester.action(:bar) { "something" }
+      @tester.action(:foo) do
+        invoke { "something" }
+      end
+      @tester.action(:bar) do
+        invoke { "something" }
+      end
 
-      @tester.actions.should be_include(:bar)
-      @tester.actions.should be_include(:foo)
+      @tester.actions.should include(:bar)
+      @tester.actions.should include(:foo)
     end
 
     it "should be able to indicate when an action is defined" do
-      @tester.action(:foo) { "something" }
+      @tester.action(:foo) do
+        invoke { "something" }
+      end
+
       @tester.should be_action(:foo)
     end
   end
@@ -40,15 +49,21 @@ describe Puppet::Interface::ActionManager do
     end
 
     it "should be able to define an action" do
-      @tester.action(:foo) { "something "}
+      @tester.action(:foo) do
+        invoke { "something "}
+      end
     end
 
     it "should be able to list defined actions" do
-      @tester.action(:foo) { "something" }
-      @tester.action(:bar) { "something" }
+      @tester.action(:foo) do
+        invoke { "something" }
+      end
+      @tester.action(:bar) do
+        invoke { "something" }
+      end
 
-      @tester.actions.should be_include(:bar)
-      @tester.actions.should be_include(:foo)
+      @tester.actions.should include(:bar)
+      @tester.actions.should include(:foo)
     end
 
     it "should be able to indicate when an action is defined" do
@@ -67,54 +82,78 @@ describe Puppet::Interface::ActionManager do
     end
 
     it "should be able to define an action at the class level" do
-      @klass.action(:foo) { "something "}
+      @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) { "something" }
+      @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) { "something "}
+      @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) { "something" }
+      @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) { "something" }
-      @klass.action(:bar) { "something" }
+      @klass.action(:foo) do
+        invoke { "something" }
+      end
+      @klass.action(:bar) do
+        invoke { "something" }
+      end
 
-      @klass.actions.should be_include(:bar)
-      @klass.actions.should be_include(:foo)
+      @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) { "something" }
-      @instance.action(:bar) { "something" }
+      @instance.action(:foo) do
+        invoke { "something" }
+      end
+      @instance.action(:bar) do
+        invoke { "something" }
+      end
 
-      @instance.actions.should be_include(:bar)
-      @instance.actions.should be_include(:foo)
+      @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) { "something" }
-      @instance.action(:bar) { "something" }
+      @klass.action(:foo) do
+        invoke { "something" }
+      end
+      @instance.action(:bar) do
+        invoke { "something" }
+      end
 
-      @instance.actions.should be_include(:bar)
-      @instance.actions.should be_include(:foo)
+      @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) { "something" }
+      @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) { "something" }
+      @klass.action(:foo) do
+        invoke { "something" }
+      end
       @instance.should be_action(:foo)
     end
 
@@ -122,9 +161,15 @@ describe Puppet::Interface::ActionManager do
       @subclass = Class.new(@klass)
       @instance = @subclass.new
 
-      @klass.action(:parent) { "a" }
-      @subclass.action(:sub) { "a" }
-      @instance.action(:instance) { "a" }
+      @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)
@@ -135,7 +180,9 @@ describe Puppet::Interface::ActionManager do
       @subclass = Class.new(@klass)
       @instance = @subclass.new
 
-      @klass.action(:foo) { "something" }
+      @klass.action(:foo) do
+        invoke { "something" }
+      end
       @instance.foo.should == "something"
     end
   end
diff --git a/spec/unit/interface/action_spec.rb b/spec/unit/interface/action_spec.rb
new file mode 100644
index 0000000..e74fa9f
--- /dev/null
+++ b/spec/unit/interface/action_spec.rb
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
+require 'puppet/interface/action'
+
+describe Puppet::Interface::Action do
+  describe "when validating the action name" do
+    it "should require a name" do
+      lambda { Puppet::Interface::Action.new(nil,nil) }.should raise_error("'' is an invalid action name")
+    end
+
+    it "should not allow empty names" do
+      lambda { Puppet::Interface::Action.new(nil,'') }.should raise_error("'' is an invalid action name")
+    end
+
+    it "should not allow names with whitespace" do
+      lambda { Puppet::Interface::Action.new(nil,'foo bar') }.should raise_error("'foo bar' is an invalid action name")
+    end
+
+    it "should not allow names beginning with dashes" do
+      lambda { Puppet::Interface::Action.new(nil,'-foobar') }.should raise_error("'-foobar' is an invalid action name")
+    end
+  end
+
+  describe "when invoking" do
+    it "should be able to call other actions on the same object" do
+      interface = Puppet::Interface.new(:my_interface) do
+        action(:foo) do
+          invoke { 25 }
+        end
+
+        action(:bar) do
+          invoke { "the value of foo is '#{foo}'" }
+        end
+      end
+      interface.foo.should == 25
+      interface.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
+          invoke { 25 }
+        end
+
+        action(:bar) do
+          invoke { "the value of foo is '#{foo}'" }
+        end
+
+        action(:quux) do
+          invoke { "qux told me #{qux}" }
+        end
+      end
+
+      interface = Puppet::Interface::MyInterfaceBaseClass.new(:my_inherited_interface) do
+        action(:baz) do
+          invoke { "the value of foo in baz is '#{foo}'" }
+        end
+
+        action(:qux) do
+          invoke { baz }
+        end
+      end
+      interface.foo.should  == 25
+      interface.bar.should  == "the value of foo is '25'"
+      interface.quux.should == "qux told me the value of foo in baz is '25'"
+      interface.baz.should  == "the value of foo in baz is '25'"
+      interface.qux.should  == "the value of foo in baz is '25'"
+    end
+  end
+end

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list