decors 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/CHANGELOG.md +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +20 -22
- data/decors.gemspec +17 -15
- data/lib/decors.rb +3 -1
- data/lib/decors/decorator_base.rb +22 -20
- data/lib/decors/decorator_definition.rb +13 -11
- data/lib/decors/method_added.rb +61 -53
- data/lib/decors/utils.rb +15 -13
- data/spec/decors_spec.rb +391 -389
- data/spec/spec_helper.rb +14 -12
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d902a52f74b8a1de84fe0a26df7130072acfb771ee854a5d6382ce4920979a43
|
4
|
+
data.tar.gz: eca2c708367d25e3172ca5fa8e948c97e8b18119e29740c1272a102c267ea6e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01c57e83cd0f5b2c3d543de6c06f97bcfd247c2478474d655a2ed0e9b05571ed85fce56e5590cd8a05b92bc16c25542fd21ffeb208fa0e3ff7f4577603d206f8
|
7
|
+
data.tar.gz: 182efe4fb2899bb5e58019249e4105966ce2b7dbdcff729dfb14e82e78e5561d6c0b0fa424900537d6b7ac522a1837f4fc5d7eb415598ec932469fa1e1248f70
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7.1
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Decors changelog
|
2
2
|
|
3
|
+
## Version 0.4 (Released June 16, 2019)
|
4
|
+
- (compatibility) remove warning for ruby 2.7
|
5
|
+
|
3
6
|
## Version 0.3 (Released August 24, 2017)
|
4
7
|
|
5
8
|
- (feature) expose decorated method before and after decoration (https://github.com/getbannerman/decors/issues/10)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,43 +1,41 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
decors (0.
|
4
|
+
decors (0.4.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
coderay (1.1.
|
9
|
+
coderay (1.1.3)
|
10
10
|
diff-lcs (1.3)
|
11
|
-
method_source (0.
|
12
|
-
pry (0.
|
13
|
-
coderay (~> 1.1
|
14
|
-
method_source (~> 0
|
15
|
-
|
16
|
-
|
17
|
-
rspec-
|
18
|
-
rspec-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
rspec-expectations (3.5.0)
|
11
|
+
method_source (1.0.0)
|
12
|
+
pry (0.13.1)
|
13
|
+
coderay (~> 1.1)
|
14
|
+
method_source (~> 1.0)
|
15
|
+
rspec (3.9.0)
|
16
|
+
rspec-core (~> 3.9.0)
|
17
|
+
rspec-expectations (~> 3.9.0)
|
18
|
+
rspec-mocks (~> 3.9.0)
|
19
|
+
rspec-core (3.9.2)
|
20
|
+
rspec-support (~> 3.9.3)
|
21
|
+
rspec-expectations (3.9.2)
|
23
22
|
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
-
rspec-support (~> 3.
|
25
|
-
rspec-mocks (3.
|
23
|
+
rspec-support (~> 3.9.0)
|
24
|
+
rspec-mocks (3.9.1)
|
26
25
|
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
-
rspec-support (~> 3.
|
28
|
-
rspec-support (3.
|
29
|
-
slop (3.6.0)
|
26
|
+
rspec-support (~> 3.9.0)
|
27
|
+
rspec-support (3.9.3)
|
30
28
|
|
31
29
|
PLATFORMS
|
32
30
|
ruby
|
33
31
|
|
34
32
|
DEPENDENCIES
|
35
33
|
decors!
|
36
|
-
pry!
|
34
|
+
pry (~> 0)!
|
37
35
|
rspec (~> 3)!
|
38
36
|
|
39
37
|
RUBY VERSION
|
40
|
-
ruby 2.
|
38
|
+
ruby 2.7.1p83
|
41
39
|
|
42
40
|
BUNDLED WITH
|
43
|
-
1.
|
41
|
+
2.1.4
|
data/decors.gemspec
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
|
4
6
|
require 'decors'
|
5
7
|
|
6
8
|
Gem::Specification.new do |gem|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
gem.name = 'decors'
|
10
|
+
gem.version = ::Decors::VERSION
|
11
|
+
gem.licenses = ['MIT']
|
12
|
+
gem.authors = ['Vivien Meyet']
|
13
|
+
gem.email = ['vivien@getbannerman.com']
|
14
|
+
gem.description = 'Ruby implementation of Python method decorators / Java annotations'
|
15
|
+
gem.summary = gem.description
|
16
|
+
gem.homepage = 'https://github.com/getbannerman/decors'
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^spec/})
|
21
|
+
gem.require_paths = ['lib']
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
gem.add_development_dependency 'pry', '~> 0'
|
24
|
+
gem.add_development_dependency 'rspec', '~> 3'
|
23
25
|
end
|
data/lib/decors.rb
CHANGED
@@ -1,27 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Decors
|
2
|
-
|
3
|
-
|
4
|
-
|
4
|
+
class DecoratorBase
|
5
|
+
attr_reader :decorated_class, :undecorated_method, :decorated_method, :decorator_args,
|
6
|
+
:decorator_kwargs, :decorator_block
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
def initialize(decorated_class, undecorated_method, decorated_method, *args, **kwargs, &block)
|
9
|
+
@decorated_class = decorated_class
|
10
|
+
@undecorated_method = undecorated_method
|
11
|
+
@decorated_method = decorated_method
|
12
|
+
@decorator_args = args
|
13
|
+
@decorator_kwargs = kwargs
|
14
|
+
@decorator_block = block
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
def call(instance, *args, **kwargs, &block)
|
18
|
+
undecorated_call(instance, *args, **kwargs, &block)
|
19
|
+
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
def undecorated_call(instance, *args, **kwargs, &block)
|
22
|
+
undecorated_method.bind(instance).call(*args, **kwargs, &block)
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
25
|
+
def decorated_method_name
|
26
|
+
decorated_method.name
|
26
27
|
end
|
28
|
+
end
|
27
29
|
end
|
@@ -1,17 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Decors
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
4
|
+
module DecoratorDefinition
|
5
|
+
def define_mixin_decorator(decorator_name, decorator_class)
|
6
|
+
define_decorator(decorator_name, decorator_class, mixin: true)
|
7
|
+
end
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
def define_decorator(decorator_name, decorator_class, mixin: false)
|
10
|
+
method_definer = mixin ? :define_method : :define_singleton_method
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
send(method_definer, decorator_name) do |*args, **kwargs, &blk|
|
13
|
+
extend(singleton_class? ? Decors::MethodAdded::SingletonListener : Decors::MethodAdded::StandardListener)
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
15
|
+
declared_decorators << [decorator_class, args, kwargs, blk]
|
16
|
+
end
|
16
17
|
end
|
18
|
+
end
|
17
19
|
end
|
data/lib/decors/method_added.rb
CHANGED
@@ -1,67 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'decors/utils'
|
2
4
|
|
3
5
|
module Decors
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
def declared_decorators
|
13
|
-
@declared_decorators ||= []
|
14
|
-
end
|
15
|
-
|
16
|
-
# method addition handling is the same for singleton and instance method
|
17
|
-
def handle_method_addition(clazz, method_name)
|
18
|
-
# @_ignore_additions allows to temporarily disable the hook
|
19
|
-
return if @_ignore_additions || declared_decorators.empty?
|
20
|
-
decorator_class, params, blk = declared_decorators.pop
|
21
|
-
|
22
|
-
undecorated_method = clazz.instance_method(method_name)
|
23
|
-
decorator = METHOD_CALLED_TOO_EARLY_HANDLER
|
24
|
-
clazz.send(:define_method, method_name) { |*args, &block| decorator.call(self, *args, &block) }
|
25
|
-
decorated_method = clazz.instance_method(method_name)
|
26
|
-
@_ignore_additions = true
|
27
|
-
decorator = decorator_class.new(clazz, undecorated_method, decorated_method, *params, &blk)
|
28
|
-
@_ignore_additions = false
|
29
|
-
clazz.send(Decors::Utils.method_visibility(undecorated_method), method_name)
|
30
|
-
end
|
31
|
-
end
|
6
|
+
module MethodAdded
|
7
|
+
module Handler
|
8
|
+
private
|
9
|
+
|
10
|
+
METHOD_CALLED_TOO_EARLY_HANDLER = lambda { |*|
|
11
|
+
raise 'You cannot call a decorated method before its decorator is initialized'
|
12
|
+
}
|
32
13
|
|
33
|
-
|
34
|
-
|
14
|
+
def declared_decorators
|
15
|
+
@declared_decorators ||= []
|
16
|
+
end
|
35
17
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
18
|
+
# method addition handling is the same for singleton and instance method
|
19
|
+
def handle_method_addition(clazz, method_name)
|
20
|
+
# @_ignore_additions allows to temporarily disable the hook
|
21
|
+
return if @_ignore_additions || declared_decorators.empty?
|
40
22
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
23
|
+
decorator_class, params, kparams, blk = declared_decorators.pop
|
24
|
+
|
25
|
+
undecorated_method = clazz.instance_method(method_name)
|
26
|
+
decorator = METHOD_CALLED_TOO_EARLY_HANDLER
|
27
|
+
|
28
|
+
clazz.send(:define_method, method_name) do |*args, **kwargs, &block|
|
29
|
+
decorator.call(self, *args, **kwargs, &block)
|
45
30
|
end
|
46
31
|
|
47
|
-
|
48
|
-
|
32
|
+
decorated_method = clazz.instance_method(method_name)
|
33
|
+
@_ignore_additions = true
|
34
|
+
decorator = decorator_class.new(clazz, undecorated_method, decorated_method, *params, **kparams, &blk)
|
35
|
+
@_ignore_additions = false
|
36
|
+
|
37
|
+
clazz.send(Decors::Utils.method_visibility(undecorated_method), method_name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module StandardListener
|
42
|
+
include ::Decors::MethodAdded::Handler
|
43
|
+
|
44
|
+
def method_added(meth)
|
45
|
+
super
|
46
|
+
handle_method_addition(self, meth)
|
47
|
+
end
|
48
|
+
|
49
|
+
def singleton_method_added(meth)
|
50
|
+
super
|
51
|
+
handle_method_addition(singleton_class, meth)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module SingletonListener
|
56
|
+
include ::Decors::MethodAdded::Handler
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
58
|
+
def singleton_method_added(meth)
|
59
|
+
super
|
60
|
+
handle_method_addition(singleton_class, meth)
|
61
|
+
end
|
54
62
|
|
55
|
-
|
56
|
-
|
57
|
-
|
63
|
+
def self.extended(base)
|
64
|
+
ObjectSpace.each_object(base).first.send(:extend, ForwardToSingletonListener)
|
65
|
+
end
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
end
|
67
|
+
module ForwardToSingletonListener
|
68
|
+
def singleton_method_added(meth)
|
69
|
+
super
|
70
|
+
singleton_class.send(:handle_method_addition, singleton_class, meth)
|
65
71
|
end
|
72
|
+
end
|
66
73
|
end
|
74
|
+
end
|
67
75
|
end
|
data/lib/decors/utils.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Decors
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|
4
|
+
module Utils
|
5
|
+
class << self
|
6
|
+
def method_visibility(method)
|
7
|
+
if method.owner.private_method_defined?(method.name)
|
8
|
+
:private
|
9
|
+
elsif method.owner.protected_method_defined?(method.name)
|
10
|
+
:protected
|
11
|
+
elsif method.owner.public_method_defined?(method.name)
|
12
|
+
:public
|
13
|
+
else
|
14
|
+
raise ArgumentError, 'Unkwnown method'
|
15
15
|
end
|
16
|
+
end
|
16
17
|
end
|
18
|
+
end
|
17
19
|
end
|
data/spec/decors_spec.rb
CHANGED
@@ -1,456 +1,458 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
4
|
|
3
5
|
describe Decors do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
before { stub_class(:TestClass) { extend Decors::DecoratorDefinition } }
|
7
|
+
before do
|
8
|
+
stub_class(:Spy) do
|
9
|
+
def self.passed_args(*args, **kwargs, &block)
|
10
|
+
arguments(args: args, kwargs: kwargs, evald_block: block&.call)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.arguments(**); end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'Check decorated_method & undecorated_method' do
|
18
|
+
let(:instance) { TestClass.new }
|
19
|
+
|
20
|
+
before do
|
21
|
+
stub_class(:SimpleDecorator, inherits: [::Decors::DecoratorBase])
|
22
|
+
call_method = call_proc
|
23
|
+
SimpleDecorator.send(:define_method, :call) { |*| instance_eval(&call_method) }
|
24
|
+
TestClass.class_eval do
|
25
|
+
define_decorator :SimpleDecorator, SimpleDecorator
|
26
|
+
SimpleDecorator()
|
27
|
+
def test
|
28
|
+
42
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
10
32
|
|
11
|
-
|
12
|
-
|
13
|
-
|
33
|
+
context 'Check decorated_method value' do
|
34
|
+
let(:call_proc) { proc { decorated_method } }
|
35
|
+
it { expect(instance.test).to eq TestClass.instance_method(:test) }
|
36
|
+
end
|
14
37
|
|
15
|
-
context 'Check
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
context 'Check decorated_method value' do
|
30
|
-
let(:call_proc) { proc { decorated_method } }
|
31
|
-
it { expect(instance.test).to eq TestClass.instance_method(:test) }
|
38
|
+
context 'Check undecorated_method value' do
|
39
|
+
let(:call_proc) { proc { undecorated_method } }
|
40
|
+
it { expect(instance.test.bind(instance).call).to eq 42 }
|
41
|
+
it { expect(instance.test.bind(instance).call).not_to eq TestClass.instance_method(:test) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when simple case decorator' do
|
46
|
+
before do
|
47
|
+
stub_class(:SimpleDecorator, inherits: [::Decors::DecoratorBase]) do
|
48
|
+
def initialize(deco_class, undeco_method, deco_method, *deco_args, **deco_kwargs, &deco_block)
|
49
|
+
super
|
50
|
+
Spy.passed_args(*deco_args, **deco_kwargs, &deco_block)
|
32
51
|
end
|
33
52
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
53
|
+
def call(instance, *args, **kwargs, &block)
|
54
|
+
super
|
55
|
+
Spy.passed_args(*args, **kwargs, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
TestClass.class_eval { define_decorator :SimpleDecorator, SimpleDecorator }
|
60
|
+
end
|
38
61
|
|
62
|
+
context 'It receive all the parameters at initialization' do
|
63
|
+
it { expect(SimpleDecorator).to receive(:new).with(TestClass, anything, anything, 1, 2, a: 3).and_call_original }
|
64
|
+
it { expect(Spy).to receive(:arguments).with(args: [1, 2], kwargs: { a: 3 }, evald_block: 'ok') }
|
65
|
+
|
66
|
+
after do
|
67
|
+
TestClass.class_eval do
|
68
|
+
SimpleDecorator(1, 2, a: 3) { 'ok' }
|
69
|
+
def test_method(*); end
|
70
|
+
|
71
|
+
def test_method_not_decorated(*); end
|
39
72
|
end
|
73
|
+
end
|
40
74
|
end
|
41
75
|
|
42
|
-
context '
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
Spy.passed_args(*args, &block)
|
53
|
-
end
|
54
|
-
}
|
55
|
-
|
56
|
-
TestClass.class_eval { define_decorator :SimpleDecorator, SimpleDecorator }
|
57
|
-
}
|
58
|
-
|
59
|
-
context 'It receive all the parameters at initialization' do
|
60
|
-
it { expect(SimpleDecorator).to receive(:new).with(TestClass, anything, anything, 1, 2, a: 3).and_call_original }
|
61
|
-
it { expect(Spy).to receive(:arguments).with(args: [1, 2], kwargs: { a: 3 }, evald_block: 'ok') }
|
62
|
-
|
63
|
-
after {
|
64
|
-
TestClass.class_eval {
|
65
|
-
SimpleDecorator(1, 2, a: 3) { 'ok' }
|
66
|
-
def test_method(*); end
|
67
|
-
|
68
|
-
def test_method_not_decorated(*); end
|
69
|
-
}
|
70
|
-
}
|
76
|
+
context 'It receive the instance and the arguments passed to the method when called' do
|
77
|
+
let(:instance) { TestClass.new }
|
78
|
+
before do
|
79
|
+
TestClass.class_eval do
|
80
|
+
SimpleDecorator()
|
81
|
+
def test_method(*args, **kwargs, &block)
|
82
|
+
Spy.passed_args(*args, **kwargs, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_method_not_decorated; end
|
71
86
|
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it { expect_any_instance_of(SimpleDecorator).to receive(:call).with(instance, 1, a: 2, b: 3, &proc { 'yes' }) }
|
90
|
+
it { expect(Spy).to receive(:arguments).with(args: [1], kwargs: { a: 2, b: 3 }, evald_block: 'yes').twice }
|
91
|
+
|
92
|
+
after do
|
93
|
+
instance.test_method(1, a: 2, b: 3) { 'yes' }
|
94
|
+
instance.test_method_not_decorated
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'It keep the method visibility' do
|
99
|
+
before do
|
100
|
+
TestClass.class_eval do
|
101
|
+
SimpleDecorator()
|
102
|
+
public def public_test_method(*); end
|
103
|
+
|
104
|
+
SimpleDecorator()
|
105
|
+
private def private_test_method(*); end
|
72
106
|
|
73
|
-
|
74
|
-
|
75
|
-
before {
|
76
|
-
TestClass.class_eval {
|
77
|
-
SimpleDecorator()
|
78
|
-
def test_method(*args, &block)
|
79
|
-
Spy.passed_args(*args, &block)
|
80
|
-
end
|
81
|
-
|
82
|
-
def test_method_not_decorated; end
|
83
|
-
}
|
84
|
-
}
|
85
|
-
|
86
|
-
it { expect_any_instance_of(SimpleDecorator).to receive(:call).with(instance, 1, a: 2, b: 3, &proc { 'yes' }) }
|
87
|
-
it { expect(Spy).to receive(:arguments).with(args: [1], kwargs: { a: 2, b: 3 }, evald_block: 'yes').twice }
|
88
|
-
|
89
|
-
after {
|
90
|
-
instance.test_method(1, a: 2, b: 3) { 'yes' }
|
91
|
-
instance.test_method_not_decorated
|
92
|
-
}
|
107
|
+
SimpleDecorator()
|
108
|
+
protected def protected_test_method(*); end
|
93
109
|
end
|
110
|
+
end
|
94
111
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
112
|
+
it { expect(TestClass).to be_public_method_defined(:public_test_method) }
|
113
|
+
it { expect(TestClass).to be_protected_method_defined(:protected_test_method) }
|
114
|
+
it { expect(TestClass).to be_private_method_defined(:private_test_method) }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'when decorator is defining a method during initialization' do
|
119
|
+
before do
|
120
|
+
stub_class(:StrangeDecorator, inherits: [::Decors::DecoratorBase]) do
|
121
|
+
def initialize(decorated_class, undecorated_method, decorated_method, *deco_args, **deco_kwargs, &deco_block)
|
122
|
+
super
|
123
|
+
decorated_class.send(:define_method, :foo) { 42 }
|
124
|
+
end
|
100
125
|
|
101
|
-
|
102
|
-
|
126
|
+
def call(*)
|
127
|
+
super * 2
|
128
|
+
end
|
129
|
+
end
|
103
130
|
|
104
|
-
|
105
|
-
|
106
|
-
}
|
107
|
-
}
|
131
|
+
TestClass.class_eval { define_decorator :StrangeDecorator, StrangeDecorator }
|
132
|
+
end
|
108
133
|
|
109
|
-
|
110
|
-
|
111
|
-
|
134
|
+
before do
|
135
|
+
TestClass.class_eval do
|
136
|
+
StrangeDecorator()
|
137
|
+
StrangeDecorator()
|
138
|
+
def test_method
|
139
|
+
5
|
112
140
|
end
|
141
|
+
end
|
113
142
|
end
|
114
143
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
144
|
+
it { expect(TestClass.new.test_method).to eq 5 * 2 * 2 }
|
145
|
+
it { expect(TestClass.new.foo).to eq 42 }
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when mutiple decorators' do
|
149
|
+
before do
|
150
|
+
Spy.class_eval do
|
151
|
+
@ordered_calls = []
|
152
|
+
|
153
|
+
class << self
|
154
|
+
attr_reader :ordered_calls
|
155
|
+
|
156
|
+
def calling(name)
|
157
|
+
ordered_calls << name
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
stub_class(:Deco1, inherits: [::Decors::DecoratorBase]) do
|
163
|
+
def call(*)
|
164
|
+
Spy.calling(:deco1_before)
|
165
|
+
super
|
166
|
+
Spy.calling(:deco1_after)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
stub_class(:Deco2, inherits: [::Decors::DecoratorBase]) do
|
171
|
+
def call(*)
|
172
|
+
Spy.calling(:deco2_before)
|
173
|
+
super
|
174
|
+
Spy.calling(:deco2_after)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
TestClass.class_eval do
|
179
|
+
define_decorator :Deco1, Deco1
|
180
|
+
define_decorator :Deco2, Deco2
|
181
|
+
|
182
|
+
Deco2()
|
183
|
+
Deco1()
|
184
|
+
def test_method(*)
|
185
|
+
Spy.calling(:inside)
|
186
|
+
end
|
187
|
+
end
|
143
188
|
end
|
144
189
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
Spy.calling(:deco1_before)
|
162
|
-
super
|
163
|
-
Spy.calling(:deco1_after)
|
164
|
-
end
|
165
|
-
}
|
166
|
-
|
167
|
-
stub_class(:Deco2, inherits: [::Decors::DecoratorBase]) {
|
168
|
-
def call(*)
|
169
|
-
Spy.calling(:deco2_before)
|
170
|
-
super
|
171
|
-
Spy.calling(:deco2_after)
|
172
|
-
end
|
173
|
-
}
|
174
|
-
|
175
|
-
TestClass.class_eval {
|
176
|
-
define_decorator :Deco1, Deco1
|
177
|
-
define_decorator :Deco2, Deco2
|
178
|
-
|
179
|
-
Deco2()
|
180
|
-
Deco1()
|
181
|
-
def test_method(*)
|
182
|
-
Spy.calling(:inside)
|
183
|
-
end
|
184
|
-
}
|
185
|
-
}
|
186
|
-
|
187
|
-
before { TestClass.new.test_method }
|
188
|
-
it { expect(Spy.ordered_calls).to eq [:deco2_before, :deco1_before, :inside, :deco1_after, :deco2_after] }
|
190
|
+
before { TestClass.new.test_method }
|
191
|
+
it { expect(Spy.ordered_calls).to eq %i[deco2_before deco1_before inside deco1_after deco2_after] }
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'when method has return value' do
|
195
|
+
before do
|
196
|
+
stub_class(:ModifierDeco, inherits: [::Decors::DecoratorBase])
|
197
|
+
|
198
|
+
TestClass.class_eval do
|
199
|
+
define_decorator :ModifierDeco, ModifierDeco
|
200
|
+
|
201
|
+
ModifierDeco()
|
202
|
+
def test_method
|
203
|
+
:ok
|
204
|
+
end
|
205
|
+
end
|
189
206
|
end
|
190
207
|
|
191
|
-
|
192
|
-
|
193
|
-
stub_class(:ModifierDeco, inherits: [::Decors::DecoratorBase])
|
208
|
+
it { expect(TestClass.new.test_method).to eq :ok }
|
209
|
+
end
|
194
210
|
|
195
|
-
|
196
|
-
|
211
|
+
context 'when method has arguments' do
|
212
|
+
before do
|
213
|
+
stub_class(:ModifierDeco, inherits: [::Decors::DecoratorBase])
|
197
214
|
|
198
|
-
|
199
|
-
|
200
|
-
:ok
|
201
|
-
end
|
202
|
-
}
|
203
|
-
}
|
215
|
+
TestClass.class_eval do
|
216
|
+
define_decorator :ModifierDeco, ModifierDeco
|
204
217
|
|
205
|
-
|
218
|
+
ModifierDeco()
|
219
|
+
def test_method(*args, **kwargs, &block)
|
220
|
+
Spy.passed_args(*args, **kwargs, &block)
|
221
|
+
end
|
222
|
+
end
|
206
223
|
end
|
207
224
|
|
208
|
-
|
209
|
-
|
210
|
-
|
225
|
+
it { expect(Spy).to receive(:arguments).with(args: [1, 2, 3], kwargs: { a: :a }, evald_block: 'yay') }
|
226
|
+
after { TestClass.new.test_method(1, 2, 3, a: :a) { 'yay' } }
|
227
|
+
end
|
211
228
|
|
212
|
-
|
213
|
-
|
229
|
+
context 'when changing arguments given to the method' do
|
230
|
+
before do
|
231
|
+
stub_class(:ModifierDeco, inherits: [::Decors::DecoratorBase]) do
|
232
|
+
def call(instance, *)
|
233
|
+
undecorated_call(instance, 1, 2, 3, a: :a, &proc { 'yay' })
|
234
|
+
end
|
235
|
+
end
|
214
236
|
|
215
|
-
|
216
|
-
|
217
|
-
Spy.passed_args(*args, &block)
|
218
|
-
end
|
219
|
-
}
|
220
|
-
}
|
237
|
+
TestClass.class_eval do
|
238
|
+
define_decorator :ModifierDeco, ModifierDeco
|
221
239
|
|
222
|
-
|
223
|
-
|
240
|
+
ModifierDeco()
|
241
|
+
def test_method(*args, **kwargs, &block)
|
242
|
+
Spy.passed_args(*args, **kwargs, &block)
|
243
|
+
end
|
244
|
+
end
|
224
245
|
end
|
225
246
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
247
|
+
it { expect(Spy).to receive(:arguments).with(args: [1, 2, 3], kwargs: { a: :a }, evald_block: 'yay') }
|
248
|
+
after { TestClass.new.test_method }
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'when method is recursive' do
|
252
|
+
before do
|
253
|
+
stub_class(:AddOneToArg, inherits: [::Decors::DecoratorBase]) do
|
254
|
+
def call(instance, *args)
|
255
|
+
undecorated_call(instance, args.first + 1)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
TestClass.class_eval do
|
260
|
+
define_decorator :AddOneToArg, AddOneToArg
|
261
|
+
|
262
|
+
AddOneToArg()
|
263
|
+
def test_method(n)
|
264
|
+
return 0 if n.zero?
|
265
|
+
|
266
|
+
n + test_method(n - 2)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
it { expect(TestClass.new.test_method(4)).to eq 5 + 4 + 3 + 2 + 1 }
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'when already has a method_added' do
|
275
|
+
before do
|
276
|
+
stub_module(:TestMixin) do
|
277
|
+
def method_added(*)
|
278
|
+
Spy.called
|
279
|
+
end
|
280
|
+
end
|
281
|
+
stub_class(:Deco, inherits: [::Decors::DecoratorBase])
|
246
282
|
end
|
283
|
+
it { expect(Spy).to receive(:called) }
|
247
284
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
TestClass.class_eval {
|
257
|
-
define_decorator :AddOneToArg, AddOneToArg
|
258
|
-
|
259
|
-
AddOneToArg()
|
260
|
-
def test_method(n)
|
261
|
-
return 0 if n.zero?
|
262
|
-
n + test_method(n - 2)
|
263
|
-
end
|
264
|
-
}
|
265
|
-
}
|
266
|
-
|
267
|
-
it { expect(TestClass.new.test_method(4)).to eq 5 + 4 + 3 + 2 + 1 }
|
285
|
+
after do
|
286
|
+
TestClass.class_eval do
|
287
|
+
extend TestMixin
|
288
|
+
|
289
|
+
define_decorator :Deco, Deco
|
290
|
+
|
291
|
+
def test_method; end
|
292
|
+
end
|
268
293
|
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context 'when inherited' do
|
297
|
+
before do
|
298
|
+
stub_class(:Deco, inherits: [::Decors::DecoratorBase])
|
269
299
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
}
|
279
|
-
it { expect(Spy).to receive(:called) }
|
280
|
-
|
281
|
-
after {
|
282
|
-
TestClass.class_eval {
|
283
|
-
extend TestMixin
|
284
|
-
|
285
|
-
define_decorator :Deco, Deco
|
286
|
-
|
287
|
-
def test_method; end
|
288
|
-
}
|
289
|
-
}
|
300
|
+
TestClass.class_eval do
|
301
|
+
define_decorator :Deco, Deco
|
302
|
+
|
303
|
+
Deco()
|
304
|
+
def test_method
|
305
|
+
:ok
|
306
|
+
end
|
307
|
+
end
|
290
308
|
end
|
291
309
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
}
|
328
|
-
|
329
|
-
expect(TestClass3.new.test_method).to eq 'this is ok'
|
330
|
-
}
|
310
|
+
it {
|
311
|
+
stub_class(:TestClass2, inherits: [TestClass])
|
312
|
+
TestClass2.class_eval do
|
313
|
+
Deco()
|
314
|
+
def test_method
|
315
|
+
:ko
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
expect(TestClass.new.test_method).to eq :ok
|
320
|
+
expect(TestClass2.new.test_method).to eq :ko
|
321
|
+
}
|
322
|
+
|
323
|
+
it {
|
324
|
+
stub_class(:TestClass3, inherits: [TestClass])
|
325
|
+
|
326
|
+
TestClass3.class_eval do
|
327
|
+
Deco()
|
328
|
+
def test_method
|
329
|
+
"this is #{super}"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
expect(TestClass3.new.test_method).to eq 'this is ok'
|
334
|
+
}
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'when decorating a class method' do
|
338
|
+
before do
|
339
|
+
stub_class(:Deco, inherits: [::Decors::DecoratorBase]) do
|
340
|
+
def call(*)
|
341
|
+
super
|
342
|
+
Spy.called
|
343
|
+
end
|
344
|
+
end
|
331
345
|
end
|
332
346
|
|
333
|
-
context 'when
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
context 'when mixin extended on the class (singleton method in class)' do
|
344
|
-
before {
|
345
|
-
TestClass.class_eval {
|
346
|
-
define_decorator :Deco, Deco
|
347
|
-
|
348
|
-
Deco()
|
349
|
-
def self.test_method
|
350
|
-
:ok
|
351
|
-
end
|
352
|
-
}
|
353
|
-
}
|
354
|
-
|
355
|
-
it { expect(Spy).to receive(:called) }
|
356
|
-
after { TestClass.test_method }
|
347
|
+
context 'when mixin extended on the class (singleton method in class)' do
|
348
|
+
before do
|
349
|
+
TestClass.class_eval do
|
350
|
+
define_decorator :Deco, Deco
|
351
|
+
|
352
|
+
Deco()
|
353
|
+
def self.test_method
|
354
|
+
:ok
|
355
|
+
end
|
357
356
|
end
|
357
|
+
end
|
358
358
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
class << self
|
363
|
-
extend Decors::DecoratorDefinition
|
359
|
+
it { expect(Spy).to receive(:called) }
|
360
|
+
after { TestClass.test_method }
|
361
|
+
end
|
364
362
|
|
365
|
-
|
363
|
+
context 'when mixin extended on the class (singleton method in singleton class)' do
|
364
|
+
before do
|
365
|
+
TestClass.class_eval do
|
366
|
+
class << self
|
367
|
+
extend Decors::DecoratorDefinition
|
366
368
|
|
367
|
-
|
368
|
-
def self.test_method
|
369
|
-
:ok
|
370
|
-
end
|
371
|
-
end
|
372
|
-
}
|
373
|
-
}
|
369
|
+
define_decorator :Deco, Deco
|
374
370
|
|
375
|
-
|
376
|
-
|
371
|
+
Deco()
|
372
|
+
def self.test_method
|
373
|
+
:ok
|
374
|
+
end
|
375
|
+
end
|
377
376
|
end
|
377
|
+
end
|
378
|
+
|
379
|
+
it { expect(Spy).to receive(:called) }
|
380
|
+
after { TestClass.singleton_class.test_method }
|
381
|
+
end
|
378
382
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
}
|
395
|
-
}
|
396
|
-
|
397
|
-
it { expect(Spy).to receive(:called) }
|
398
|
-
after { TestClass.singleton_class.test_method }
|
383
|
+
context 'when mixin extended on the class (method in singleton class of singleton class)' do
|
384
|
+
before do
|
385
|
+
TestClass.class_eval do
|
386
|
+
class << self
|
387
|
+
class << self
|
388
|
+
extend Decors::DecoratorDefinition
|
389
|
+
|
390
|
+
define_decorator :Deco, Deco
|
391
|
+
|
392
|
+
Deco()
|
393
|
+
def test_method
|
394
|
+
:ok
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
399
398
|
end
|
399
|
+
end
|
400
400
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
class << self
|
405
|
-
extend Decors::DecoratorDefinition
|
401
|
+
it { expect(Spy).to receive(:called) }
|
402
|
+
after { TestClass.singleton_class.test_method }
|
403
|
+
end
|
406
404
|
|
407
|
-
|
405
|
+
context 'when mixin extended on the class (method in singleton class)' do
|
406
|
+
before do
|
407
|
+
TestClass.class_eval do
|
408
|
+
class << self
|
409
|
+
extend Decors::DecoratorDefinition
|
408
410
|
|
409
|
-
|
410
|
-
def test_method
|
411
|
-
:ok
|
412
|
-
end
|
413
|
-
end
|
414
|
-
}
|
415
|
-
}
|
411
|
+
define_decorator :Deco, Deco
|
416
412
|
|
417
|
-
|
418
|
-
|
413
|
+
Deco()
|
414
|
+
def test_method
|
415
|
+
:ok
|
416
|
+
end
|
417
|
+
end
|
419
418
|
end
|
419
|
+
end
|
420
420
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
define_decorator :Deco, Deco
|
421
|
+
it { expect(Spy).to receive(:called) }
|
422
|
+
after { TestClass.test_method }
|
423
|
+
end
|
425
424
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
425
|
+
context 'when mixin extended on the class (both method in singleton class and singleton method in class)' do
|
426
|
+
before do
|
427
|
+
TestClass.class_eval do
|
428
|
+
define_decorator :Deco, Deco
|
430
429
|
|
431
|
-
|
432
|
-
|
430
|
+
Deco()
|
431
|
+
def self.test_method__in_class
|
432
|
+
:ok
|
433
|
+
end
|
433
434
|
|
434
|
-
|
435
|
-
extend Decors::DecoratorDefinition
|
435
|
+
def self.untest_method__in_class; end
|
436
436
|
|
437
|
-
|
437
|
+
class << self
|
438
|
+
extend Decors::DecoratorDefinition
|
438
439
|
|
439
|
-
|
440
|
-
def test_method__in_singleton
|
441
|
-
:ok
|
442
|
-
end
|
440
|
+
define_decorator :Deco, Deco
|
443
441
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
}
|
442
|
+
Deco()
|
443
|
+
def test_method__in_singleton
|
444
|
+
:ok
|
445
|
+
end
|
449
446
|
|
450
|
-
|
451
|
-
|
452
|
-
it { expect(Spy).to_not receive(:called) and TestClass.untest_method__in_class }
|
453
|
-
it { expect(Spy).to_not receive(:called) and TestClass.untest_method__in_singleton }
|
447
|
+
def untest_method__in_singleton; end
|
448
|
+
end
|
454
449
|
end
|
450
|
+
end
|
451
|
+
|
452
|
+
it { expect(Spy).to receive(:called) and TestClass.test_method__in_class }
|
453
|
+
it { expect(Spy).to receive(:called) and TestClass.test_method__in_singleton }
|
454
|
+
it { expect(Spy).to_not receive(:called) and TestClass.untest_method__in_class }
|
455
|
+
it { expect(Spy).to_not receive(:called) and TestClass.untest_method__in_singleton }
|
455
456
|
end
|
457
|
+
end
|
456
458
|
end
|