Permalink
Browse files

Support multiple decorators, cleaned up some code

  • Loading branch information...
1 parent f5db096 commit f4b241ba02b09c3131ef6e1d154c30e7ba79135e @fredwu committed Sep 12, 2012
Showing with 62 additions and 33 deletions.
  1. +2 −2 lib/ruby_decorator.rb
  2. +22 −24 lib/ruby_decorators.rb
  3. +0 −7 lib/ruby_decorators/stack.rb
  4. +38 −0 spec/ruby_decorators_spec.rb
@@ -1,9 +1,9 @@
class RubyDecorator
def self.+@
- RubyDecorators::Stack.decorators << self
+ RubyDecorators::Stack.all << self
end
def +@
- RubyDecorators::Stack.decorators << self
+ RubyDecorators::Stack.all << self
end
end
@@ -1,43 +1,41 @@
require "ruby_decorators/version"
require "ruby_decorator"
-require "ruby_decorators/stack"
module RubyDecorators
- def method_added(method_name)
- @__decorated_methods ||= []
+ class Stack
+ def self.all
+ @all ||= []
+ end
+ end
- return if RubyDecorators::Stack.decorators.empty? ||
- method_name.to_s =~ /__undecorated_/ ||
- @__decorated_methods.include?(method_name)
+ def method_added(method_name)
+ super
- current_decorator = RubyDecorators::Stack.decorators.pop
- method_visibility = detect_method_visibility(method_name)
+ @methods ||= {}
+ @decorators ||= {}
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- alias_method :__undecorated_#{method_name}, :#{method_name}
+ return if RubyDecorators::Stack.all.empty?
- @__decorators ||= {}
- @__decorators["#{method_name}"] = current_decorator
+ @methods[method_name] = instance_method(method_name)
+ @decorators[method_name] = RubyDecorators::Stack.all.pop(42)
- #{method_visibility}
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ #{method_visibility_for(method_name)}
def #{method_name}(*args, &blk)
- decorator = #{current_decorator}.new
- decorator ||= self.class.instance_variable_get(:@__decorators)["#{method_name}"]
-
- if args.any?
- decorator.call(method(:__undecorated_#{method_name}), *args, &blk)
- else
- decorator.call(method(:__undecorated_#{method_name}), &blk)
- end
+ decorators = self.class.instance_variable_get(:@decorators)[:#{method_name}]
+ method = self.class.instance_variable_get(:@methods)[:#{method_name}]
+
+ decorators.inject(method.bind(self)) do |method, decorator|
+ decorator = decorator.new if decorator.respond_to?(:new)
+ lambda { |*a, &b| decorator.call(method, *a, &b) }
+ end.call(*args, &blk)
end
RUBY_EVAL
-
- @__decorated_methods << method_name
end
private
- def detect_method_visibility(method_name)
+ def method_visibility_for(method_name)
if private_method_defined?(method_name)
:private
elsif protected_method_defined?(method_name)
@@ -1,7 +0,0 @@
-module RubyDecorators
- class Stack
- def self.decorators
- @decorators ||= []
- end
- end
-end
@@ -7,6 +7,12 @@ def call(this)
end
end
+ class Hi < RubyDecorator
+ def call(this, *args, &blk)
+ this.call(*args, &blk).sub('hello', 'hi')
+ end
+ end
+
class Batman < RubyDecorator
def call(this, *args, &blk)
this.call(*args, &blk).sub('world', 'batman')
@@ -23,6 +29,15 @@ def call(this, *args, &blk)
end
end
+ class DummyClass2
+ extend RubyDecorators
+
+ +Hi
+ def hi
+ 'hello'
+ end
+ end
+
class DummyClass
extend RubyDecorators
@@ -68,6 +83,18 @@ def hello_super_catwoman
@greeting
end
+ +Hi
+ +Batman
+ def hi_batman
+ @greeting
+ end
+
+ +Hi
+ +Catwoman.new('super', 'catwoman')
+ def hi_super_catwoman(arg1)
+ "#{@greeting}#{arg1}"
+ end
+
protected
+Batman
@@ -86,6 +113,7 @@ def hello_private
subject { DummyClass.new }
it "#hello_world" do
+ DummyClass2.new.hi.must_equal 'hi'
subject.hello_world.must_equal 'hello world'
end
@@ -126,4 +154,14 @@ def hello_private
subject.hello_super_catwoman.must_equal 'hello super catwoman'
end
end
+
+ describe "multiple decorators" do
+ it "decorates a simple method" do
+ subject.hi_batman.must_equal 'hi batman'
+ end
+
+ it "decorates a method with args" do
+ subject.hi_super_catwoman('!').must_equal 'hi super catwoman!'
+ end
+ end
end

0 comments on commit f4b241b

Please sign in to comment.