blob: 9b6b38f622d2e384e874452127163d88cda690c2 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
require_relative '../../spec_helper'
describe "Module#using" do
it "imports class refinements from module into the current class/module" do
refinement = Module.new do
refine Integer do
def foo; "foo"; end
end
end
result = nil
Module.new do
using refinement
result = 1.foo
end
result.should == "foo"
end
it "accepts module as argument" do
refinement = Module.new do
refine Integer do
def foo; "foo"; end
end
end
-> () {
Module.new do
using refinement
end
}.should_not raise_error
end
it "accepts module without refinements" do
mod = Module.new
-> () {
Module.new do
using mod
end
}.should_not raise_error
end
it "does not accept class" do
klass = Class.new
-> () {
Module.new do
using klass
end
}.should raise_error(TypeError)
end
it "raises TypeError if passed something other than module" do
-> () {
Module.new do
using "foo"
end
}.should raise_error(TypeError)
end
it "returns self" do
refinement = Module.new
result = nil
mod = Module.new do
result = using refinement
end
result.should equal(mod)
end
it "works in classes too" do
refinement = Module.new do
refine Integer do
def foo; "foo"; end
end
end
result = nil
Class.new do
using refinement
result = 1.foo
end
result.should == "foo"
end
it "raises error in method scope" do
mod = Module.new do
def self.foo
using Module.new {}
end
end
-> () {
mod.foo
}.should raise_error(RuntimeError, /Module#using is not permitted in methods/)
end
it "activates refinement even for existed objects" do
result = nil
Module.new do
klass = Class.new do
def foo; "foo"; end
end
refinement = Module.new do
refine klass do
def foo; "foo from refinement"; end
end
end
obj = klass.new
using refinement
result = obj.foo
end
result.should == "foo from refinement"
end
it "activates updates when refinement reopens later" do
result = nil
Module.new do
klass = Class.new do
def foo; "foo"; end
end
refinement = Module.new do
refine klass do
def foo; "foo from refinement"; end
end
end
using refinement
refinement.class_eval do
refine klass do
def foo; "foo from reopened refinement"; end
end
end
obj = klass.new
result = obj.foo
end
result.should == "foo from reopened refinement"
end
describe "scope of refinement" do
it "is active until the end of current class/module" do
ScratchPad.record []
Module.new do
Class.new do
using Module.new {
refine String do
def to_s; "hello from refinement"; end
end
}
ScratchPad << "1".to_s
end
ScratchPad << "1".to_s
end
ScratchPad.recorded.should == ["hello from refinement", "1"]
end
# Refinements are lexical in scope.
# Refinements are only active within a scope after the call to using.
# Any code before the using statement will not have the refinement activated.
it "is not active before the `using` call" do
ScratchPad.record []
Module.new do
Class.new do
ScratchPad << "1".to_s
using Module.new {
refine String do
def to_s; "hello from refinement"; end
end
}
ScratchPad << "1".to_s
end
end
ScratchPad.recorded.should == ["1", "hello from refinement"]
end
# If you call a method that is defined outside the current scope
# the refinement will be deactivated
it "is not active for code defined outside the current scope" do
result = nil
Module.new do
klass = Class.new do
def foo; "foo"; end
end
refinement = Module.new do
refine klass do
def foo; "foo from refinement"; end
end
end
def self.call_foo(c)
c.foo
end
using refinement
result = call_foo(klass.new)
end
result.should == "foo"
end
# If a method is defined in a scope where a refinement is active
# the refinement will be active when the method is called.
it "is active for method defined in a scope wherever it's called" do
klass = Class.new do
def foo; "foo"; end
end
mod = Module.new do
refinement = Module.new do
refine klass do
def foo; "foo from refinement"; end
end
end
using refinement
def self.call_foo(c)
c.foo
end
end
c = klass.new
mod.call_foo(c).should == "foo from refinement"
end
it "is not active if `using` call is not evaluated" do
result = nil
Module.new do
if false
using Module.new {
refine String do
def to_s; "hello from refinement"; end
end
}
end
result = "1".to_s
end
result.should == "1"
end
# The refinements in module are not activated automatically
# if the class is reopened later
it "is not active when class/module reopens" do
refinement = Module.new do
refine String do
def to_s
"hello from refinement"
end
end
end
result = []
klass = Class.new do
using refinement
result << "1".to_s
end
klass.class_eval do
result << "1".to_s
end
result.should == ["hello from refinement", "1"]
end
end
end
|