decors 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|