Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat#2] Nested singleton handling #4

Merged
merged 1 commit into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/decors.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'decors/decorator_base'
require 'decors/decorator_definition'
require 'decors/method_added_listener'
require 'decors/method_added'

module Decors
VERSION = '0.1.0'
Expand Down
11 changes: 8 additions & 3 deletions lib/decors/decorator_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ def define_decorator(decorator_name, decorator_class, mixin: false)
method_definer = mixin ? :define_method : :define_singleton_method

send(method_definer, decorator_name) do |*params, &blk|
ctx = self.singleton_class? ? ObjectSpace.each_object(self).first : self
ctx.send(:extend, MethodAddedListener)
ctx.declared_decorators << [decorator_class, params, blk]
if singleton_class?
ObjectSpace.each_object(self).first.send(:extend, ::Decors::MethodAdded::ForwardToSingletonListener)
extend(::Decors::MethodAdded::SingletonListener)
else
extend(::Decors::MethodAdded::StandardListener)
end

declared_decorators << [decorator_class, params, blk]
end
end
end
Expand Down
56 changes: 56 additions & 0 deletions lib/decors/method_added.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module Decors
module MethodAdded
module Handler
private

def declared_decorators
@declared_decorators ||= []
end

# method addition handling is the same for singleton and instance method
def handle_method_addition(clazz, method_name)
# @_ignore_additions allows to temporarily disable the hook
return if @_ignore_additions || declared_decorators.empty?
decorator_class, params, blk = declared_decorators.pop

decorated_method = clazz.instance_method(method_name)

@_ignore_additions = true
decorator = decorator_class.new(clazz, decorated_method, *params, &blk)
@_ignore_additions = false

clazz.send(:define_method, method_name) { |*args, &block| decorator.call(self, *args, &block) }
end
end

module StandardListener
include ::Decors::MethodAdded::Handler

def method_added(meth)
super
handle_method_addition(self, meth)
end

def singleton_method_added(meth)
super
handle_method_addition(singleton_class, meth)
end
end

module ForwardToSingletonListener
def singleton_method_added(meth)
super
singleton_class.send(:handle_method_addition, singleton_class, meth)
end
end

module SingletonListener
include ::Decors::MethodAdded::Handler

def singleton_method_added(meth)
super
handle_method_addition(singleton_class, meth)
end
end
end
end
35 changes: 0 additions & 35 deletions lib/decors/method_added_listener.rb

This file was deleted.

43 changes: 42 additions & 1 deletion spec/decors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,48 @@ def self.test_method
after { TestClass.test_method }
end

context 'when mixin extended on the class (singleton method in singleton class)' do
before {
TestClass.class_eval {
class << self
extend Decors::DecoratorDefinition

define_decorator :Deco, Deco

Deco()
def self.test_method
:ok
end
end
}
}

it { expect(Spy).to receive(:called) }
after { TestClass.singleton_class.test_method }
end

context 'when mixin extended on the class (method in singleton class of singleton class)' do
before {
TestClass.class_eval {
class << self
class << self
extend Decors::DecoratorDefinition

define_decorator :Deco, Deco

Deco()
def test_method
:ok
end
end
end
}
}

it { expect(Spy).to receive(:called) }
after { TestClass.singleton_class.test_method }
end

context 'when mixin extended on the class (method in singleton class)' do
before {
TestClass.class_eval {
Expand Down Expand Up @@ -366,4 +408,3 @@ def untest_method__in_singleton
end
end
end