From: Nick Lewis <[email protected]>

Callbacks are intended to be used by Dashboard's modular plug-ins to
modify core functionality.  Core functionality will call
Registry.each_callback to invoke callbacks, and plug-ins will call
Registry.add_callback to make their presence known to the core.

Reviewed-by: Paul Berry <[email protected]> and Max Martin 
<[email protected]>

Signed-off-by: Paul Berry <[email protected]>
---
Local-branch: maint/next/add-registry
 config/environment.rb     |    2 +
 lib/registry.rb           |   44 +++++++++++++++++++++
 spec/lib/registry_spec.rb |   92 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+), 0 deletions(-)
 create mode 100644 lib/registry.rb
 create mode 100644 spec/lib/registry_spec.rb

diff --git a/config/environment.rb b/config/environment.rb
index 6eaf249..e0084a5 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -7,6 +7,8 @@ RAILS_GEM_VERSION = '2.3.4' unless defined? RAILS_GEM_VERSION
 require File.join(File.dirname(__FILE__), 'boot')
 require 'active_support'
 
+require 'registry'
+
 Rails::Initializer.run do |config|
   config.gem 'rack'
   config.gem 'haml'
diff --git a/lib/registry.rb b/lib/registry.rb
new file mode 100644
index 0000000..a1f60f3
--- /dev/null
+++ b/lib/registry.rb
@@ -0,0 +1,44 @@
+class Registry
+  class << self
+    delegate :add_callback, :each_callback, :find_first_callback, :to => 
:instance
+  end
+
+  def self.instance
+    @instance ||= new
+  end
+
+  def add_callback( feature_name, hook_name, callback_name, value = nil, 
&block )
+    if block and value
+      raise "Cannot pass both a value and a block to add_callback"
+    elsif @registry[feature_name][hook_name][callback_name]
+      raise "Cannot redefine callback 
[#{feature_name.inspect},#{hook_name.inspect},#{callback_name}]"
+    end
+
+    @registry[feature_name][hook_name][callback_name] = value || block
+  end
+
+  def each_callback( feature_name, hook_name )
+    hook = @registry[feature_name][hook_name]
+    hook.sort.each do |callback_name,callback|
+      yield( callback )
+    end
+    nil
+  end
+
+  def find_first_callback(feature_name, hook_name)
+    self.each_callback(feature_name, hook_name) do |thing|
+      if result = yield(thing)
+        return result
+      end
+    end
+    nil
+  end
+
+  def initialize
+    @registry = Hash.new do |registry, feature_name|
+      registry[feature_name] = Hash.new do |hooks, hook_name|
+        hooks[hook_name] = Hash.new
+      end
+    end
+  end
+end
diff --git a/spec/lib/registry_spec.rb b/spec/lib/registry_spec.rb
new file mode 100644
index 0000000..ecb3020
--- /dev/null
+++ b/spec/lib/registry_spec.rb
@@ -0,0 +1,92 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe @registry do
+  before :each do
+    @registry = Registry.new
+  end
+
+  describe "#add_callback" do
+    it "does not allow multiple callbacks with the same name" do
+      @registry.add_callback(:test, :hook, "test_callback", "value1")
+      lambda { @registry.add_callback(:test, :hook, "test_callback", "value2") 
}.should raise_error(/Cannot redefine callback/)
+
+      callbacks = []
+      @registry.each_callback(:test, :hook) do |callback|
+        callbacks << callback
+      end
+      callbacks.should == ["value1"]
+    end
+
+    it "does not allow both a value and a block to be specified" do
+      lambda { @registry.add_callback(:test, :hook, "test_callback", 
"inline_value") { "block_value" } }.should raise_error(/Cannot pass both a 
value and a block/)
+      callbacks = []
+      @registry.each_callback(:test, :hook) do |callback|
+        callbacks << callback
+      end
+      callbacks.should be_empty
+    end
+
+    it "adds the given callback to the registry" do
+      @registry.add_callback(:test, :hook, "0_block_callback") { "my block" }
+      @registry.add_callback(:test, :hook, "1_value_callback", "foo bar baz")
+
+      callbacks = []
+      @registry.each_callback(:test, :hook) do |callback|
+        callbacks << callback
+      end
+      callbacks.first.should be_a(Proc)
+      callbacks.first.call.should == "my block"
+      callbacks.last.should == "foo bar baz"
+    end
+  end
+
+  describe "#each_callback" do
+    it "does nothing if the hook has no callbacks" do
+      callbacks = []
+      @registry.each_callback(:test, :nonexistent) do |callback|
+        callbacks << callback
+      end
+      callbacks.should be_empty
+    end
+
+    it "yields each callback in order" do
+      @registry.add_callback(:test, :hook, "2_callback", "second")
+      @registry.add_callback(:test, :hook, "3_callback", "third")
+      @registry.add_callback(:test, :hook, "1_callback", "first")
+
+      values = []
+
+      @registry.each_callback(:test, :hook) do |value|
+        values << value
+      end
+
+      values.should == ["first", "second", "third"]
+    end
+
+    it "yields procs intact, not their values" do
+      @registry.add_callback(:test, :hook, "my_callback") { 
"my_callback_value" }
+      @registry.add_callback(:test, :hook, "my_other_callback") { 
"my_other_callback_value" }
+
+      blocks = []
+      @registry.each_callback(:test, :hook) do |block|
+        blocks << block
+      end
+
+      blocks.map(&:class).should == [Proc, Proc]
+      blocks.map(&:call).should == ["my_callback_value", 
"my_other_callback_value"]
+    end
+  end
+
+  describe "#find_first_callback" do
+    it "returns the value returned by the first callback which returns a 
value" do
+      @registry.add_callback(:test, :hook, "0_callback") { 0 }
+      @registry.add_callback(:test, :hook, "1_callback") { 1 }
+      @registry.add_callback(:test, :hook, "2_callback") { 2 }
+
+      @registry.find_first_callback(:test, :hook) do |callback|
+        val = callback.call
+        val.odd? ? val.ordinalize : nil
+      end.should == "1st"
+    end
+  end
+end
-- 
1.7.2

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to