decors 0.1.0 → 0.2.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 +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +43 -0
- data/LICENSE +23 -0
- data/README.md +182 -2
- data/decors.gemspec +3 -2
- data/lib/decors/decorator_definition.rb +3 -3
- data/lib/decors/method_added.rb +63 -0
- data/lib/decors/utils.rb +17 -0
- data/lib/decors.rb +2 -2
- data/spec/decors_spec.rb +61 -1
- metadata +15 -8
- data/lib/decors/method_added_listener.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7759f5e7d47d9e741ae03ea06cb02acaff1f984e
|
4
|
+
data.tar.gz: 2b41184cffff4ba470f4730fe68c3fe52e779518
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7abe88ecad1024a83fefd0629af2120689063b0335d0fcb8f5711a071213c5919177804ddd9d377f38faffc7e0bf0aaa7cd9772b42e4df15376326850328c219
|
7
|
+
data.tar.gz: 66d71eea9cd047431f76e349f4a95e15cdf06d60bcaaaa7103831f4eb12539fe3622ead623755a9e68ba820b77c6c10fc09708ab1e09fbe7a131f1644f46c7ec
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Decors changelog
|
2
2
|
|
3
|
+
## Version 0.2 (Released May 10, 2017)
|
4
|
+
|
5
|
+
- (bugfix) nested singleton handling (https://github.com/getbannerman/decors/issues/2)
|
6
|
+
- (bugfix) keep method visibility (https://github.com/getbannerman/decors/issues/3)
|
7
|
+
|
8
|
+
|
3
9
|
## Version 0.1 (Released April 24, 2017)
|
4
10
|
|
5
11
|
- First implementation
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
decors (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
coderay (1.1.1)
|
10
|
+
diff-lcs (1.3)
|
11
|
+
method_source (0.8.2)
|
12
|
+
pry (0.10.4)
|
13
|
+
coderay (~> 1.1.0)
|
14
|
+
method_source (~> 0.8.1)
|
15
|
+
slop (~> 3.4)
|
16
|
+
rspec (3.5.0)
|
17
|
+
rspec-core (~> 3.5.0)
|
18
|
+
rspec-expectations (~> 3.5.0)
|
19
|
+
rspec-mocks (~> 3.5.0)
|
20
|
+
rspec-core (3.5.4)
|
21
|
+
rspec-support (~> 3.5.0)
|
22
|
+
rspec-expectations (3.5.0)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.5.0)
|
25
|
+
rspec-mocks (3.5.0)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.5.0)
|
28
|
+
rspec-support (3.5.0)
|
29
|
+
slop (3.6.0)
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
ruby
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
decors!
|
36
|
+
pry!
|
37
|
+
rspec (~> 3)!
|
38
|
+
|
39
|
+
RUBY VERSION
|
40
|
+
ruby 2.3.1p112
|
41
|
+
|
42
|
+
BUNDLED WITH
|
43
|
+
1.14.6
|
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2012 Michael Fairley
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Decors
|
2
2
|
|
3
|
-
#### Yet another implementation of Python Decorators/Java
|
3
|
+
#### Yet another implementation of Python Decorators/Java annotations in ruby
|
4
4
|
|
5
5
|
Here are other implementations:
|
6
6
|
- https://github.com/wycats/ruby_decorators
|
@@ -8,11 +8,191 @@ Here are other implementations:
|
|
8
8
|
- https://github.com/fredwu/ruby_decorators
|
9
9
|
- https://gist.github.com/reu/2762650
|
10
10
|
|
11
|
+
[See why](#faq) we decided to implement this differently.
|
12
|
+
|
11
13
|
## Install
|
12
14
|
|
13
|
-
Add `gem 'decors'` to your Gemfile
|
15
|
+
Add `gem 'decors'` to your Gemfile
|
16
|
+
Then run `bundle`
|
14
17
|
|
15
18
|
## Usage
|
16
19
|
|
20
|
+
#### Basic usage
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
# Create a new decorator
|
24
|
+
module DB
|
25
|
+
class WithinTransaction < ::Decors::DecoratorBase
|
26
|
+
def call(*)
|
27
|
+
DB.transaction { super }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Define the alias on the class/module you want to use it
|
33
|
+
class Item
|
34
|
+
define_decorator :WithinTransaction, DB::WithinTransaction
|
35
|
+
|
36
|
+
# From now on you can use the decorator to decorate any method/singleton method
|
37
|
+
|
38
|
+
WithinTransaction()
|
39
|
+
def do_stuff_in_db
|
40
|
+
# do_stuff_in_db within a db transaction
|
41
|
+
end
|
42
|
+
|
43
|
+
WithinTransaction()
|
44
|
+
def self.do_stuff_in_db
|
45
|
+
# do_stuff_in_db within a db transaction
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
#### Mixin usage
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
# Create a new decorator
|
54
|
+
module DbDecorator
|
55
|
+
class WithinTransaction < ::Decors::DecoratorBase; end
|
56
|
+
class LogBeforeDbHit < ::Decors::DecoratorBase; end
|
57
|
+
|
58
|
+
# define decorator that can be mixed with other classes/modules
|
59
|
+
define_mixin_decorator :WithinTransaction, WithinTransaction
|
60
|
+
define_mixin_decorator :LogBeforeDbHit, LogBeforeDbHit
|
61
|
+
end
|
62
|
+
|
63
|
+
class Item
|
64
|
+
extend DbDecorator
|
65
|
+
|
66
|
+
WithinTransaction()
|
67
|
+
LogBeforeDbHit()
|
68
|
+
def do_stuff_in_db
|
69
|
+
# do_stuff_in_db within a db transaction
|
70
|
+
end
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
#### Argument modification
|
75
|
+
|
76
|
+
Sometimes you want the decorator to modify your method arguments. For example a generic way to define an api that handles pagination and filtering.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
module Api
|
80
|
+
class SimpleApi < ::Decors::DecoratorBase
|
81
|
+
attr_reader :model_store, :pagination, :filtering
|
82
|
+
|
83
|
+
def initialize(*, model_store, pagination:, filtering:)
|
84
|
+
@model_store = model_store
|
85
|
+
@pagination = pagination
|
86
|
+
@filtering = filtering
|
87
|
+
super
|
88
|
+
end
|
89
|
+
|
90
|
+
def call(this, *)
|
91
|
+
models, page, per_page = paginate(filter(model_store.all))
|
92
|
+
|
93
|
+
super(this, models, page: page, per_page: per_page)
|
94
|
+
end
|
95
|
+
|
96
|
+
...
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class ItemController
|
101
|
+
SimpleApi(Item, pagination: true, filtering: true)
|
102
|
+
def index(items, page:, per_page:)
|
103
|
+
render json: { items: items.map(&:as_json), page: page, per_page: per_page }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
## FAQ
|
109
|
+
|
110
|
+
#### What is going on under the wood?
|
111
|
+
```ruby
|
112
|
+
class User
|
113
|
+
define_decorator :Delay, Async::Delay
|
114
|
+
|
115
|
+
Delay(priority: :high)
|
116
|
+
def delayed_computation(*args, &blk)
|
117
|
+
# CODE here
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Is strictly equivalent to
|
122
|
+
|
123
|
+
class User
|
124
|
+
def secret_computation(*args, &blk)
|
125
|
+
Async::Delay.new(User, User.instance_method(:secret_computation), priority: :high)
|
126
|
+
.call(self, *args, &blk) do
|
127
|
+
# CODE here
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
#### What is the difference between this and method modifiers?
|
136
|
+
|
137
|
+
Method modifiers modify methods, specifically those are class methods which:
|
138
|
+
|
139
|
+
1. Take the instance method name (symbol) as first argument
|
140
|
+
2. Change subsequent calls to that method
|
141
|
+
3. return the same symbol
|
142
|
+
|
143
|
+
The limitation of those are:
|
144
|
+
- They often come after the method which hurts lisibility
|
145
|
+
- If they come before the method you cannot have other argument than the method_name
|
146
|
+
- nesting those is a pain
|
147
|
+
|
148
|
+
with decorator you can do the following
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class User
|
152
|
+
...
|
153
|
+
|
154
|
+
Delay(priority: :high)
|
155
|
+
Retry(3)
|
156
|
+
DbTransaction()
|
157
|
+
def secret_computation
|
158
|
+
# computation inside transaction retried up to 3 times if an error occurs.
|
159
|
+
# All that performed async
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# VS
|
164
|
+
|
165
|
+
class User
|
166
|
+
...
|
167
|
+
|
168
|
+
db_transaction def secret_computation
|
169
|
+
end
|
170
|
+
|
171
|
+
retry_when_failing :secret_computation, 3
|
172
|
+
handle_async :secret_computation, priority: :high
|
173
|
+
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
#### Why another implementation of this ?
|
178
|
+
|
179
|
+
Most of the implementation use the unary operator `+@` syntax to define the decorator which leads to a lost reference to the caller instance. This imply a global state of what are the current decorators to apply to the next method definition (which is not always threadsafe depending on the implementation).
|
180
|
+
|
181
|
+
Current implementations wasn't handling nested singleton (see https://github.com/getbannerman/decors/issues/2)
|
182
|
+
|
183
|
+
Current implementations have a rather large footprint. A class that needs a decorated method have all of its method definition hooked on the `method_added` callback. In our case it's only added at the decorator call. And we'll soon support callback removal after method definition.
|
184
|
+
|
185
|
+
The obvious drawback of our approach are: The syntax that might be less comprehensive for a new comer, and the namespacing which we handle via aliasing at decorator declaration.
|
186
|
+
|
187
|
+
#### What is that weird syntax?
|
188
|
+
|
189
|
+
By convention we use ConstantName case for the decorators in order not to mix them with other method call/DSL.
|
190
|
+
This is not enforced and you can have the text case you like.
|
191
|
+
|
192
|
+
Note that, this syntax is also used in other gems such as [Contract](https://github.com/egonSchiele/contracts.ruby)
|
193
|
+
|
17
194
|
## License
|
18
195
|
|
196
|
+
Copyright (c) 2017 [Bannerman](https://github.com/getbannerman), [Vivien Meyet](https://github.com/vmeyet)
|
197
|
+
|
198
|
+
Licensed under the [MIT license](http://fredwu.mit-license.org/).
|
data/decors.gemspec
CHANGED
@@ -6,6 +6,7 @@ require 'decors'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "decors"
|
8
8
|
gem.version = ::Decors::VERSION
|
9
|
+
gem.licenses = ['MIT']
|
9
10
|
gem.authors = ['Vivien Meyet']
|
10
11
|
gem.email = ['vivien@getbannerman.com']
|
11
12
|
gem.description = "Ruby implementation of Python method decorators / Java annotations"
|
@@ -17,6 +18,6 @@ Gem::Specification.new do |gem|
|
|
17
18
|
gem.test_files = gem.files.grep(%r{^spec/})
|
18
19
|
gem.require_paths = ['lib']
|
19
20
|
|
20
|
-
gem.add_development_dependency 'pry
|
21
|
-
gem.add_development_dependency 'rspec', '~>
|
21
|
+
gem.add_development_dependency 'pry', '~> 0'
|
22
|
+
gem.add_development_dependency 'rspec', '~> 3'
|
22
23
|
end
|
@@ -8,9 +8,9 @@ module Decors
|
|
8
8
|
method_definer = mixin ? :define_method : :define_singleton_method
|
9
9
|
|
10
10
|
send(method_definer, decorator_name) do |*params, &blk|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
extend(singleton_class? ? Decors::MethodAdded::SingletonListener : Decors::MethodAdded::StandardListener)
|
12
|
+
|
13
|
+
declared_decorators << [decorator_class, params, blk]
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'decors/utils'
|
2
|
+
|
3
|
+
module Decors
|
4
|
+
module MethodAdded
|
5
|
+
module Handler
|
6
|
+
private
|
7
|
+
|
8
|
+
def declared_decorators
|
9
|
+
@declared_decorators ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
# method addition handling is the same for singleton and instance method
|
13
|
+
def handle_method_addition(clazz, method_name)
|
14
|
+
# @_ignore_additions allows to temporarily disable the hook
|
15
|
+
return if @_ignore_additions || declared_decorators.empty?
|
16
|
+
decorator_class, params, blk = declared_decorators.pop
|
17
|
+
|
18
|
+
decorated_method = clazz.instance_method(method_name)
|
19
|
+
|
20
|
+
@_ignore_additions = true
|
21
|
+
decorator = decorator_class.new(clazz, decorated_method, *params, &blk)
|
22
|
+
@_ignore_additions = false
|
23
|
+
|
24
|
+
clazz.send(:define_method, method_name) { |*args, &block| decorator.call(self, *args, &block) }
|
25
|
+
clazz.send(Decors::Utils.method_visibility(decorated_method), method_name)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module StandardListener
|
30
|
+
include ::Decors::MethodAdded::Handler
|
31
|
+
|
32
|
+
def method_added(meth)
|
33
|
+
super
|
34
|
+
handle_method_addition(self, meth)
|
35
|
+
end
|
36
|
+
|
37
|
+
def singleton_method_added(meth)
|
38
|
+
super
|
39
|
+
handle_method_addition(singleton_class, meth)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module SingletonListener
|
44
|
+
include ::Decors::MethodAdded::Handler
|
45
|
+
|
46
|
+
def singleton_method_added(meth)
|
47
|
+
super
|
48
|
+
handle_method_addition(singleton_class, meth)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.extended(base)
|
52
|
+
ObjectSpace.each_object(base).first.send(:extend, ForwardToSingletonListener)
|
53
|
+
end
|
54
|
+
|
55
|
+
module ForwardToSingletonListener
|
56
|
+
def singleton_method_added(meth)
|
57
|
+
super
|
58
|
+
singleton_class.send(:handle_method_addition, singleton_class, meth)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/decors/utils.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Decors
|
2
|
+
module Utils
|
3
|
+
class << self
|
4
|
+
def method_visibility(method)
|
5
|
+
if method.owner.private_method_defined?(method.name)
|
6
|
+
:private
|
7
|
+
elsif method.owner.protected_method_defined?(method.name)
|
8
|
+
:protected
|
9
|
+
elsif method.owner.public_method_defined?(method.name)
|
10
|
+
:public
|
11
|
+
else
|
12
|
+
fail ArgumentError, 'Unkwnown method'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/decors.rb
CHANGED
data/spec/decors_spec.rb
CHANGED
@@ -64,6 +64,25 @@ describe Decors do
|
|
64
64
|
instance.test_method_not_decorated
|
65
65
|
}
|
66
66
|
end
|
67
|
+
|
68
|
+
context 'It keep the method visibility' do
|
69
|
+
before {
|
70
|
+
TestClass.class_eval {
|
71
|
+
SimpleDecorator()
|
72
|
+
public def public_test_method(*); end
|
73
|
+
|
74
|
+
SimpleDecorator()
|
75
|
+
private def private_test_method(*); end
|
76
|
+
|
77
|
+
SimpleDecorator()
|
78
|
+
protected def protected_test_method(*); end
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
it { expect(TestClass).to be_public_method_defined(:public_test_method) }
|
83
|
+
it { expect(TestClass).to be_protected_method_defined(:protected_test_method) }
|
84
|
+
it { expect(TestClass).to be_private_method_defined(:private_test_method) }
|
85
|
+
end
|
67
86
|
end
|
68
87
|
|
69
88
|
context 'when decorator is defining a method during initialization' do
|
@@ -310,6 +329,48 @@ describe Decors do
|
|
310
329
|
after { TestClass.test_method }
|
311
330
|
end
|
312
331
|
|
332
|
+
context 'when mixin extended on the class (singleton method in singleton class)' do
|
333
|
+
before {
|
334
|
+
TestClass.class_eval {
|
335
|
+
class << self
|
336
|
+
extend Decors::DecoratorDefinition
|
337
|
+
|
338
|
+
define_decorator :Deco, Deco
|
339
|
+
|
340
|
+
Deco()
|
341
|
+
def self.test_method
|
342
|
+
:ok
|
343
|
+
end
|
344
|
+
end
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
it { expect(Spy).to receive(:called) }
|
349
|
+
after { TestClass.singleton_class.test_method }
|
350
|
+
end
|
351
|
+
|
352
|
+
context 'when mixin extended on the class (method in singleton class of singleton class)' do
|
353
|
+
before {
|
354
|
+
TestClass.class_eval {
|
355
|
+
class << self
|
356
|
+
class << self
|
357
|
+
extend Decors::DecoratorDefinition
|
358
|
+
|
359
|
+
define_decorator :Deco, Deco
|
360
|
+
|
361
|
+
Deco()
|
362
|
+
def test_method
|
363
|
+
:ok
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
it { expect(Spy).to receive(:called) }
|
371
|
+
after { TestClass.singleton_class.test_method }
|
372
|
+
end
|
373
|
+
|
313
374
|
context 'when mixin extended on the class (method in singleton class)' do
|
314
375
|
before {
|
315
376
|
TestClass.class_eval {
|
@@ -366,4 +427,3 @@ describe Decors do
|
|
366
427
|
end
|
367
428
|
end
|
368
429
|
end
|
369
|
-
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vivien Meyet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: pry
|
14
|
+
name: pry
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '3'
|
41
41
|
description: Ruby implementation of Python method decorators / Java annotations
|
42
42
|
email:
|
43
43
|
- vivien@getbannerman.com
|
@@ -46,17 +46,24 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- ".gitignore"
|
49
|
+
- ".ruby-version"
|
50
|
+
- ".travis.yml"
|
49
51
|
- CHANGELOG.md
|
52
|
+
- Gemfile
|
53
|
+
- Gemfile.lock
|
54
|
+
- LICENSE
|
50
55
|
- README.md
|
51
56
|
- decors.gemspec
|
52
57
|
- lib/decors.rb
|
53
58
|
- lib/decors/decorator_base.rb
|
54
59
|
- lib/decors/decorator_definition.rb
|
55
|
-
- lib/decors/
|
60
|
+
- lib/decors/method_added.rb
|
61
|
+
- lib/decors/utils.rb
|
56
62
|
- spec/decors_spec.rb
|
57
63
|
- spec/spec_helper.rb
|
58
64
|
homepage: https://github.com/getbannerman/decors
|
59
|
-
licenses:
|
65
|
+
licenses:
|
66
|
+
- MIT
|
60
67
|
metadata: {}
|
61
68
|
post_install_message:
|
62
69
|
rdoc_options: []
|
@@ -74,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
81
|
version: '0'
|
75
82
|
requirements: []
|
76
83
|
rubyforge_project:
|
77
|
-
rubygems_version: 2.
|
84
|
+
rubygems_version: 2.5.1
|
78
85
|
signing_key:
|
79
86
|
specification_version: 4
|
80
87
|
summary: Ruby implementation of Python method decorators / Java annotations
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Decors
|
2
|
-
# Mixin that add method_added hooks
|
3
|
-
module MethodAddedListener
|
4
|
-
def method_added(meth)
|
5
|
-
super
|
6
|
-
handle_method_addition(self, meth)
|
7
|
-
end
|
8
|
-
|
9
|
-
def singleton_method_added(meth)
|
10
|
-
super
|
11
|
-
handle_method_addition(singleton_class, meth)
|
12
|
-
end
|
13
|
-
|
14
|
-
def declared_decorators
|
15
|
-
@declared_decorators ||= []
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
# method addition handling is the same for singleton and instance method
|
21
|
-
def handle_method_addition(clazz, method_name)
|
22
|
-
# @_ignore_additions allows to temporarily disable the hook
|
23
|
-
return if @_ignore_additions || declared_decorators.empty?
|
24
|
-
decorator_class, params, blk = declared_decorators.pop
|
25
|
-
|
26
|
-
decorated_method = clazz.instance_method(method_name)
|
27
|
-
|
28
|
-
@_ignore_additions = true
|
29
|
-
decorator = decorator_class.new(clazz, decorated_method, *params, &blk)
|
30
|
-
@_ignore_additions = false
|
31
|
-
|
32
|
-
clazz.send(:define_method, method_name) { |*args, &block| decorator.call(self, *args, &block) }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|