durable_decorator 0.0.4 → 0.0.6
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.
- data/README.md +6 -6
- data/lib/durable_decorator/base.rb +38 -9
- data/lib/durable_decorator/version.rb +1 -1
- data/lib/durable_decorator.rb +4 -4
- data/spec/durable_decorator_spec.rb +46 -40
- data/spec/spec_helper.rb +2 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
[](https://travis-ci.org/jumph4x/durable_decorator)
|
6
6
|
[](https://codeclimate.com/github/jumph4x/durable_decorator)
|
7
7
|
|
8
|
-
This is a project for modifying the behavior of gems outside of your reach. You may be using a large Rails Engine and be wanting to
|
8
|
+
This is a project for modifying the behavior of gems outside of your reach. You may be using a large Rails Engine and be wanting to simply decorate some existing behavior, but at the same time you want to inherit original behavior.
|
9
9
|
|
10
10
|
## On tracking new decorators and managing fragility
|
11
11
|
|
@@ -33,8 +33,8 @@ class ExampleClass
|
|
33
33
|
end
|
34
34
|
|
35
35
|
class ExampleClass
|
36
|
-
|
37
|
-
|
36
|
+
durably_decorate :string_method do
|
37
|
+
string_method_original + " and new"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -67,15 +67,15 @@ ExampleClass.class_eval do
|
|
67
67
|
sha: 'WE-IGNORE-THE-ABOVE'
|
68
68
|
}
|
69
69
|
|
70
|
-
|
71
|
-
|
70
|
+
durably_decorate :string_method, meta do
|
71
|
+
string_method_original + " and new"
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
75
|
DurableDecorator::TamperedDefinitionError: Method SHA mismatch, the definition has been tampered with
|
76
76
|
```
|
77
77
|
|
78
|
-
DurableDecorator also maintains explicit versions of each method overriden by creating aliases with appended SHAs of the form ```some_method_1234abcd``` so you can always target explicit method versions without relying on ```
|
78
|
+
DurableDecorator also maintains explicit versions of each method overriden by creating aliases with appended SHAs of the form ```some_method_1234abcd``` so you can always target explicit method versions without relying on ```some_method_original```.
|
79
79
|
|
80
80
|
### No more suprise monkey patching
|
81
81
|
Once you decorate the method and seal it with its SHA, if some gem tries to come in and overwrite your work **BEFORE** decorate-time, DurableDecorator will warn you. Similarly, expect to see an exception bubble up if the definition of the original method has changed and requires a review and a re-hash.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'digest/
|
1
|
+
require 'digest/sha1'
|
2
2
|
|
3
3
|
module DurableDecorator
|
4
4
|
class Base
|
@@ -6,6 +6,10 @@ module DurableDecorator
|
|
6
6
|
DECORATION_MODES = ['strict']
|
7
7
|
REDEFINITIONS = {}
|
8
8
|
|
9
|
+
def reset!
|
10
|
+
REDEFINITIONS.clear
|
11
|
+
end
|
12
|
+
|
9
13
|
def redefine clazz, method_name, meta, &block
|
10
14
|
if method_name.to_s.match /^self\./
|
11
15
|
redefine_instance (class << clazz; self; end), method_name.to_s.gsub("self.",''), meta, &block
|
@@ -17,6 +21,7 @@ module DurableDecorator
|
|
17
21
|
def redefine_instance clazz, method_name, meta, &block
|
18
22
|
return unless old_method = existing_method(clazz, method_name, meta, &block)
|
19
23
|
|
24
|
+
alias_original clazz, method_name
|
20
25
|
alias_definitions clazz, method_name, method_sha(old_method)
|
21
26
|
redefine_method clazz, method_name, &block
|
22
27
|
|
@@ -65,24 +70,48 @@ module DurableDecorator
|
|
65
70
|
end
|
66
71
|
end
|
67
72
|
|
73
|
+
def redefinitions
|
74
|
+
REDEFINITIONS
|
75
|
+
end
|
76
|
+
|
68
77
|
def alias_definitions clazz, method_name, old_sha
|
69
78
|
clazz.class_eval do
|
70
79
|
alias_method("#{method_name}_#{old_sha}", method_name)
|
71
|
-
alias_method("#{method_name}
|
80
|
+
alias_method("#{method_name}_#{old_sha[0..3]}", method_name)
|
81
|
+
alias_method("#{method_name}_#{old_sha[0..5]}", method_name)
|
72
82
|
end
|
73
83
|
end
|
74
84
|
|
85
|
+
def alias_original clazz, method_name
|
86
|
+
return unless original_redefinition? clazz, method_name
|
87
|
+
|
88
|
+
clazz.class_eval do
|
89
|
+
alias_method("#{method_name}_original", method_name)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def class_name clazz
|
94
|
+
name = clazz.name || ''
|
95
|
+
name = clazz.to_s if name.empty?
|
96
|
+
name.to_sym
|
97
|
+
end
|
98
|
+
|
99
|
+
def full_method_name clazz, method_name
|
100
|
+
"#{class_name(clazz)}##{method_name}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def original_redefinition? clazz, method_name
|
104
|
+
!REDEFINITIONS[full_method_name(clazz, method_name)]
|
105
|
+
end
|
106
|
+
|
75
107
|
def store_redefinition clazz, name, old_method, new_method
|
76
|
-
|
77
|
-
class_name = "Meta#{clazz.superclass.to_s}" if class_name.empty?
|
78
|
-
class_index = REDEFINITIONS[class_name.to_sym] ||= {}
|
79
|
-
method_index = class_index[name.to_sym] ||= []
|
108
|
+
methods = REDEFINITIONS[full_method_name(clazz, name)] ||= []
|
80
109
|
|
81
110
|
to_store = [new_method]
|
82
|
-
to_store.unshift(old_method) if
|
111
|
+
to_store.unshift(old_method) if original_redefinition?(clazz, name)
|
83
112
|
|
84
113
|
to_store.each do |method|
|
85
|
-
|
114
|
+
methods << method_hash(name, method)
|
86
115
|
end
|
87
116
|
|
88
117
|
true
|
@@ -96,7 +125,7 @@ module DurableDecorator
|
|
96
125
|
end
|
97
126
|
|
98
127
|
def method_sha method
|
99
|
-
Digest::
|
128
|
+
Digest::SHA1.hexdigest(method.source.gsub(/\s+/, ' '))
|
100
129
|
end
|
101
130
|
|
102
131
|
def redefined? clazz, method_name, &block
|
data/lib/durable_decorator.rb
CHANGED
@@ -21,18 +21,18 @@ end
|
|
21
21
|
# monkey-patching Ruby core to create an API
|
22
22
|
Object.class_eval do
|
23
23
|
class << self
|
24
|
-
def
|
24
|
+
def durably_decorate method_name, meta = nil, &block
|
25
25
|
DurableDecorator::Base.redefine self, method_name, meta, &block
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
29
|
-
|
28
|
+
def durably_decorate_singleton method_name, meta = nil, &block
|
29
|
+
durably_decorate "self.#{method_name}", meta, &block
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
Module.class_eval do
|
35
|
-
def
|
35
|
+
def durably_decorate method_name, meta = nil, &block
|
36
36
|
DurableDecorator::Base.redefine self, method_name, meta, &block
|
37
37
|
end
|
38
38
|
end
|
@@ -3,34 +3,34 @@ require 'spec_helper'
|
|
3
3
|
describe DurableDecorator::Base do
|
4
4
|
|
5
5
|
context 'with classes' do
|
6
|
-
# Spec uses ./example_class.rb
|
6
|
+
# Spec uses ./example_class.rb
|
7
7
|
context 'for existing instance methods' do
|
8
|
-
it 'guarantees access to #
|
8
|
+
it 'guarantees access to #method_original' do
|
9
9
|
ExampleClass.class_eval do
|
10
|
-
|
11
|
-
|
10
|
+
durably_decorate :no_param_method do
|
11
|
+
no_param_method_original + " and a new string"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
instance = ExampleClass.new
|
16
16
|
instance.no_param_method.should == 'original and a new string'
|
17
|
-
end
|
17
|
+
end
|
18
18
|
|
19
19
|
context 'with incorrect arity' do
|
20
20
|
it 'throws an error' do
|
21
21
|
lambda{
|
22
22
|
ExampleClass.class_eval do
|
23
|
-
|
23
|
+
durably_decorate(:no_param_method){|a,b| }
|
24
24
|
end
|
25
25
|
}.should raise_error(DurableDecorator::BadArityError)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
context 'for methods with parameters' do
|
30
|
-
it 'guarantees access to #
|
30
|
+
it 'guarantees access to #method_original' do
|
31
31
|
ExampleClass.class_eval do
|
32
|
-
|
33
|
-
"#{
|
32
|
+
durably_decorate :one_param_method do |another_string|
|
33
|
+
"#{one_param_method_original('check')} and #{another_string}"
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -44,8 +44,8 @@ describe DurableDecorator::Base do
|
|
44
44
|
@original_sha = DurableDecorator::Base.determine_sha("ExampleClass#one_param_method")
|
45
45
|
|
46
46
|
ExampleClass.class_eval do
|
47
|
-
|
48
|
-
"#{
|
47
|
+
durably_decorate :one_param_method do |another_string|
|
48
|
+
"#{one_param_method_935888f04d9e132be458591d5755cb8131fec457('check')} and #{another_string}"
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -54,9 +54,9 @@ describe DurableDecorator::Base do
|
|
54
54
|
|
55
55
|
it 'works with explicit method version invocation' do
|
56
56
|
ExampleClass.class_eval do
|
57
|
-
|
57
|
+
durably_decorate :one_param_method do |boolean|
|
58
58
|
if boolean
|
59
|
-
|
59
|
+
one_param_method_original("check and ")
|
60
60
|
else
|
61
61
|
"latest"
|
62
62
|
end
|
@@ -68,18 +68,24 @@ describe DurableDecorator::Base do
|
|
68
68
|
instance.one_param_method(false).should == 'latest'
|
69
69
|
@original_sha.should_not == @redef_sha
|
70
70
|
end
|
71
|
+
|
72
|
+
it 'work with short explicit method version invocation' do
|
73
|
+
instance = ExampleClass.new
|
74
|
+
instance.one_param_method_9358('').should == "original: "
|
75
|
+
instance.one_param_method_935888('').should == "original: "
|
76
|
+
end
|
71
77
|
end
|
72
78
|
|
73
79
|
context 'for strict definitions' do
|
74
80
|
context 'with the correct SHA' do
|
75
|
-
it 'guarantees access to #
|
81
|
+
it 'guarantees access to #method_original' do
|
76
82
|
ExampleClass.class_eval do
|
77
83
|
meta = {
|
78
84
|
:mode => 'strict',
|
79
|
-
:sha => '
|
85
|
+
:sha => 'd54f9c7ea2038fac0ae2ff9af49c56f35761725d'
|
80
86
|
}
|
81
|
-
|
82
|
-
|
87
|
+
durably_decorate :no_param_method, meta do
|
88
|
+
no_param_method_original + " and a new string"
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
@@ -96,8 +102,8 @@ describe DurableDecorator::Base do
|
|
96
102
|
:mode => 'strict',
|
97
103
|
:sha => '1234wrong'
|
98
104
|
}
|
99
|
-
|
100
|
-
|
105
|
+
durably_decorate :no_param_method, meta do
|
106
|
+
no_param_method_original + " and a new string"
|
101
107
|
end
|
102
108
|
end
|
103
109
|
}.should raise_error(DurableDecorator::TamperedDefinitionError)
|
@@ -111,8 +117,8 @@ describe DurableDecorator::Base do
|
|
111
117
|
meta = {
|
112
118
|
:mode => 'strict'
|
113
119
|
}
|
114
|
-
|
115
|
-
|
120
|
+
durably_decorate :no_param_method, meta do
|
121
|
+
no_param_method_original + " and a new string"
|
116
122
|
end
|
117
123
|
end
|
118
124
|
}.should raise_error(DurableDecorator::InvalidDecorationError)
|
@@ -125,38 +131,38 @@ describe DurableDecorator::Base do
|
|
125
131
|
it 'throws an error' do
|
126
132
|
lambda{
|
127
133
|
ExampleClass.class_eval do
|
128
|
-
|
134
|
+
durably_decorate(:integer_method){ }
|
129
135
|
end
|
130
136
|
}.should raise_error(DurableDecorator::UndefinedMethodError)
|
131
137
|
end
|
132
138
|
end
|
133
139
|
|
134
140
|
context 'for existing class methods' do
|
135
|
-
it 'guarantees access to ::
|
141
|
+
it 'guarantees access to ::method_original' do
|
136
142
|
ExampleClass.class_eval do
|
137
|
-
|
138
|
-
|
143
|
+
durably_decorate_singleton :clazz_level do
|
144
|
+
clazz_level_original + " and a new string"
|
139
145
|
end
|
140
146
|
end
|
141
147
|
|
142
148
|
ExampleClass.clazz_level.should == 'original and a new string'
|
143
|
-
end
|
149
|
+
end
|
144
150
|
|
145
151
|
context 'with incorrect arity' do
|
146
152
|
it 'throws an error' do
|
147
153
|
lambda{
|
148
154
|
ExampleClass.class_eval do
|
149
|
-
|
155
|
+
durably_decorate_singleton(:clazz_level){|a,b| }
|
150
156
|
end
|
151
157
|
}.should raise_error(DurableDecorator::BadArityError)
|
152
158
|
end
|
153
159
|
end
|
154
160
|
|
155
161
|
context 'for methods with parameters' do
|
156
|
-
it 'guarantees access to ::
|
162
|
+
it 'guarantees access to ::method_original' do
|
157
163
|
ExampleClass.class_eval do
|
158
|
-
|
159
|
-
"#{
|
164
|
+
durably_decorate_singleton :clazz_level_paramed do |another_string|
|
165
|
+
"#{clazz_level_paramed_original('check')} and #{another_string}"
|
160
166
|
end
|
161
167
|
end
|
162
168
|
|
@@ -169,7 +175,7 @@ describe DurableDecorator::Base do
|
|
169
175
|
it 'throws an error' do
|
170
176
|
lambda{
|
171
177
|
ExampleClass.class_eval do
|
172
|
-
|
178
|
+
durably_decorate_singleton(:integer_method){ }
|
173
179
|
end
|
174
180
|
}.should raise_error(DurableDecorator::UndefinedMethodError)
|
175
181
|
end
|
@@ -177,25 +183,25 @@ describe DurableDecorator::Base do
|
|
177
183
|
end
|
178
184
|
|
179
185
|
context 'with modules' do
|
180
|
-
# Spec uses ./sample_module.rb
|
186
|
+
# Spec uses ./sample_module.rb
|
181
187
|
context 'for existing methods' do
|
182
|
-
it 'guarantees access to #
|
188
|
+
it 'guarantees access to #method_original' do
|
183
189
|
Sample.class_eval do
|
184
|
-
|
185
|
-
|
190
|
+
durably_decorate :module_method do
|
191
|
+
module_method_original + " and a new string"
|
186
192
|
end
|
187
193
|
end
|
188
194
|
|
189
195
|
o = Object.new
|
190
196
|
o.extend(Sample)
|
191
197
|
o.module_method.should == 'original and a new string'
|
192
|
-
end
|
198
|
+
end
|
193
199
|
|
194
200
|
context 'with incorrect arity' do
|
195
201
|
it 'throws an error' do
|
196
202
|
lambda{
|
197
203
|
Sample.class_eval do
|
198
|
-
|
204
|
+
durably_decorate(:module_method){|a,b| }
|
199
205
|
end
|
200
206
|
}.should raise_error(DurableDecorator::BadArityError)
|
201
207
|
end
|
@@ -206,7 +212,7 @@ describe DurableDecorator::Base do
|
|
206
212
|
it 'throws an error' do
|
207
213
|
lambda{
|
208
214
|
Sample.class_eval do
|
209
|
-
|
215
|
+
durably_decorate(:integer_method){ }
|
210
216
|
end
|
211
217
|
}.should raise_error(DurableDecorator::UndefinedMethodError)
|
212
218
|
end
|
@@ -226,14 +232,14 @@ describe DurableDecorator::Base do
|
|
226
232
|
context 'when the target is an instance method' do
|
227
233
|
it 'should return the sha' do
|
228
234
|
DurableDecorator::Base.determine_sha('ExampleClass#no_param_method').should ==
|
229
|
-
'
|
235
|
+
'd54f9c7ea2038fac0ae2ff9af49c56f35761725d'
|
230
236
|
end
|
231
237
|
end
|
232
238
|
|
233
239
|
context 'when the target is a class method' do
|
234
240
|
it 'should return the sha' do
|
235
241
|
DurableDecorator::Base.determine_sha('ExampleClass.clazz_level').should ==
|
236
|
-
'
|
242
|
+
'f6490bec1af021697ed8e5990f0d1db3976f065f'
|
237
243
|
end
|
238
244
|
end
|
239
245
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: durable_decorator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: method_source
|