Commit 1abce18

mo khan <mo@mokhan.ca>
2013-05-22 04:30:48
add support to register multiple interceptors on a single target
1 parent bbf9a91
lib/spank/container.rb
@@ -4,25 +4,31 @@ module Spank
       @message = message
     end
   end
+
   class Container
     def initialize
       @items = {}
       register(:container) { self }
     end
+
     def register(key, &block)
       component = Component.new(key, &block)
       components_for(key).push(component)
       component
     end
+
     def resolve(key)
       instantiate(components_for(key).first, key)
     end
+
     def resolve_all(key)
       components_for(key).map {|item| instantiate(item, key) }
     end
+
     def build(type)
       try("I could not create: #{type}"){ build!(type) }
     end
+
     def build!(type)
       constructor = type.instance_method('initialize')
       parameters = constructor.parameters.map do |req, parameter|
@@ -37,10 +43,12 @@ module Spank
       @items[key] = [] unless @items[key]
       @items[key]
     end
+
     def instantiate(component, key)
       raise ContainerError.new("#{key} is not a registered component.") unless component
       component.create(self)
     end
+
     def try(error = nil, &lambda)
       begin
         lambda.call
lib/spank/interceptor_chain.rb
@@ -0,0 +1,13 @@
+  class InterceptorChain
+    def initialize(interceptors = [])
+      @interceptors = interceptors
+    end
+
+    def push(interceptor)
+      @interceptors.push(interceptor)
+    end
+
+    def each(&block)
+      @interceptors.each(&block)
+    end
+  end
lib/spank/interceptor_registration.rb
@@ -2,15 +2,23 @@ module Spank
   class InterceptorRegistration
     def initialize(method_symbol)
       @method = method_symbol
+      @interceptors = []
     end
 
     def with(interceptor)
-      @interceptor = interceptor
+      @interceptors.push(interceptor)
+      self
+    end
+
+    def and(interceptor)
+      with(interceptor)
     end
 
     def intercept(instance)
-      proxy= Proxy.new(instance)
-      proxy.add_interceptor(@method, @interceptor)
+      proxy = Proxy.new(instance)
+      @interceptors.each do |interceptor|
+        proxy.add_interceptor(@method, interceptor)
+      end
       proxy
     end
   end
lib/spank/proxy.rb
@@ -1,11 +1,13 @@
 module Spank
   class Proxy
-    def initialize(target)
+    def initialize(target, interceptor_chain = InterceptorChain.new)
       @target = target
+      @interceptor_chain = interceptor_chain
     end
 
     def add_interceptor(method, interceptor)
-      self.extend(create_module_for(method, interceptor))
+      @interceptor_chain.push(interceptor)
+      self.extend(create_module_for(method))
       self
     end
 
@@ -23,11 +25,13 @@ module Spank
       end
     end
 
-    def create_module_for(method, interceptor)
+    def create_module_for(method)
       Module.new do
         define_method(method.to_sym) do |*args, &block|
           invocation = create_invocation_for(method, args, block)
-          interceptor.intercept(invocation)
+          @interceptor_chain.each do |interceptor|
+            interceptor.intercept(invocation)
+          end
           invocation.result
         end
       end
lib/spank.rb
@@ -5,4 +5,5 @@ require 'spank/interceptor_registration.rb'
 require 'spank/invocation.rb'
 require 'spank/ioc.rb'
 require 'spank/proxy.rb'
-require "spank/version"
+require 'spank/interceptor_chain'
+require 'spank/version'
spec/unit/container_spec.rb
@@ -108,9 +108,13 @@ module Spank
       end
     end
 
-    context "when registering an interceptor" do
+    context "when registering interceptors" do
       class TestInterceptor
-        attr_reader :called
+        attr_reader :called, :name
+        def initialize(name)
+          @name = name
+          @called = false
+        end
         def intercept(invocation)
           @called = true
           invocation.proceed
@@ -126,17 +130,23 @@ module Spank
       end
 
       let(:command) { TestCommand.new }
-      let(:interceptor) { TestInterceptor.new }
+      let(:interceptor) { TestInterceptor.new("first") }
+      let(:other_interceptor) { TestInterceptor.new("second") }
 
       before :each do
-        sut.register(:command) { command }.intercept(:run).with(interceptor)
+        sut.register(:command) { command }.intercept(:run).with(interceptor).and(other_interceptor)
+        sut.register(:single_command) { command }.intercept(:run).with(interceptor)
         sut.resolve(:command).run("hi")
       end
 
-      it "should allow the interceptor to intercept calls to the target" do
+      it "should allow the first interceptor to intercept calls to the target" do
         interceptor.called.should be_true
       end
 
+      it "should allow the second interceptor to intercept calls to the target" do
+        other_interceptor.called.should be_true
+      end
+
       it "should forward the args to the command" do
         command.called.should be_true
         command.received.should == ['hi']