y_support 2.1.18 → 2.4.4
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/lib/y_support/all.rb +2 -32
- data/lib/y_support/core_ext/array.rb +2 -2
- data/lib/y_support/core_ext/class.rb +2 -2
- data/lib/y_support/core_ext/enumerable.rb +2 -2
- data/lib/y_support/core_ext/hash/misc.rb +23 -10
- data/lib/y_support/core_ext/hash.rb +2 -2
- data/lib/y_support/core_ext/module/misc.rb +9 -0
- data/lib/y_support/core_ext/module.rb +2 -2
- data/lib/y_support/core_ext/numeric.rb +2 -2
- data/lib/y_support/core_ext/object/inspection.rb +8 -2
- data/lib/y_support/core_ext/object.rb +3 -3
- data/lib/y_support/core_ext/string/misc.rb +9 -12
- data/lib/y_support/core_ext/string.rb +2 -2
- data/lib/y_support/core_ext/symbol.rb +2 -2
- data/lib/y_support/core_ext.rb +1 -1
- data/lib/y_support/flex_coerce/class_methods.rb +49 -0
- data/lib/y_support/flex_coerce/flex_proxy.rb +121 -0
- data/lib/y_support/flex_coerce/module_methods.rb +37 -0
- data/lib/y_support/flex_coerce.rb +24 -0
- data/lib/y_support/kde.rb +1 -1
- data/lib/y_support/literate.rb +253 -0
- data/lib/y_support/local_object.rb +1 -1
- data/lib/y_support/name_magic/array_methods.rb +48 -0
- data/lib/y_support/name_magic/class_methods.rb +205 -161
- data/lib/y_support/name_magic/hash_methods.rb +33 -0
- data/lib/y_support/name_magic/namespace.rb +449 -0
- data/lib/y_support/name_magic.rb +358 -100
- data/lib/y_support/null_object.rb +1 -1
- data/lib/y_support/respond_to.rb +1 -1
- data/lib/y_support/stdlib_ext/matrix/misc.rb +2 -2
- data/lib/y_support/stdlib_ext/matrix.rb +2 -2
- data/lib/y_support/stdlib_ext.rb +1 -1
- data/lib/y_support/typing/array.rb +1 -1
- data/lib/y_support/typing/enumerable.rb +1 -1
- data/lib/y_support/typing/hash.rb +1 -1
- data/lib/y_support/typing/module.rb +1 -1
- data/lib/y_support/typing/object/typing.rb +17 -15
- data/lib/y_support/typing/object.rb +1 -1
- data/lib/y_support/typing.rb +14 -10
- data/lib/y_support/unicode.rb +1 -1
- data/lib/y_support/version.rb +1 -1
- data/lib/y_support/x.rb +2 -1
- data/test/flex_coerce_test.rb +134 -0
- data/test/literate_test.rb +231 -0
- data/test/misc_test.rb +49 -27
- data/test/name_magic_test.rb +907 -60
- data/test/typing_test.rb +7 -7
- metadata +14 -13
- data/lib/y_support/abstract_algebra.rb +0 -234
- data/lib/y_support/name_magic/array.rb +0 -38
- data/lib/y_support/name_magic/hash.rb +0 -31
- data/lib/y_support/name_magic/namespace_methods.rb +0 -260
- data/lib/y_support/try.rb +0 -133
- data/test/abstract_algebra_test.rb +0 -138
- data/test/performance_test_example.rb +0 -23
- data/test/try_test.rb +0 -102
data/test/name_magic_test.rb
CHANGED
@@ -6,70 +6,917 @@ require './../lib/y_support/name_magic'
|
|
6
6
|
|
7
7
|
describe NameMagic do
|
8
8
|
before do
|
9
|
-
mod = Module.new do include NameMagic end
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
@c = -> { mod = Module.new do include NameMagic end
|
10
|
+
Class.new do include mod end }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe NameMagic::ClassMethods do
|
14
|
+
it "should carry certain instance methods" do
|
15
|
+
[ :__instances__,
|
16
|
+
:__avid_instances__,
|
17
|
+
:const_magic,
|
18
|
+
:validate_name,
|
19
|
+
:namespace=,
|
20
|
+
:namespace!,
|
21
|
+
:instances,
|
22
|
+
:instance,
|
23
|
+
:nameless_instances,
|
24
|
+
:forget,
|
25
|
+
:__forget__,
|
26
|
+
:forget_nameless_instances,
|
27
|
+
:forget_all_instances,
|
28
|
+
:instantiation_exec,
|
29
|
+
:exec_when_naming,
|
30
|
+
:new,
|
31
|
+
].each { |ß|
|
32
|
+
NameMagic::ClassMethods.instance_methods.must_include ß
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".__instances__" do
|
37
|
+
it "must act as a selector of the instance registry" do
|
38
|
+
c = @c.call
|
39
|
+
c.__instances__.must_be_kind_of Hash
|
40
|
+
c.__instances__.must_be_empty
|
41
|
+
i1 = c.new
|
42
|
+
c.__instances__.to_a.first.must_equal [ i1, nil ]
|
43
|
+
i2 = c.new name: "Joe"
|
44
|
+
c.__instances__.must_equal( { i1 => nil, i2 => :Joe } )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe ".__avid_instances__" do
|
49
|
+
it "must return the avid (typically unnamed) instances" do
|
50
|
+
c = @c.call
|
51
|
+
c.__avid_instances__.must_equal []
|
52
|
+
instance = c.new
|
53
|
+
c.__avid_instances__.must_equal [ instance ]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe ".validate_name" do
|
58
|
+
it "must validate suitable capitalized names" do
|
59
|
+
c = @c.call
|
60
|
+
c.validate_name( :Fred ).must_equal :Fred
|
61
|
+
c.validate_name( "Fred" ).must_equal "Fred"
|
62
|
+
-> { c.validate_name "fred" }.must_raise NameError
|
63
|
+
-> { c.validate_name "Name With Spaces" }
|
64
|
+
.must_raise NameError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe ".namespace" do
|
69
|
+
it "should return namespace of the user class" do
|
70
|
+
c = @c.call
|
71
|
+
c.namespace.must_equal c
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe ".namespace=" do
|
76
|
+
it "shoud can redefine the namespace of a class" do
|
77
|
+
c, m = @c.call, Module.new
|
78
|
+
d = Class.new c
|
79
|
+
c.namespace.must_equal c
|
80
|
+
c.namespace = m
|
81
|
+
c.namespace.must_equal m
|
82
|
+
d.namespace.must_equal m
|
83
|
+
c, m = @c.call, Module.new
|
84
|
+
d = Class.new c
|
85
|
+
d.namespace.must_equal c
|
86
|
+
d.namespace = m
|
87
|
+
c.namespace.must_equal c
|
88
|
+
end
|
89
|
+
|
90
|
+
it "raises error when instance registry is not empty" do
|
91
|
+
c, m = @c.call, Module.new
|
92
|
+
c.new
|
93
|
+
-> { c.namespace = m }.must_raise StandardError
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe ".namespace!" do
|
98
|
+
it "should work" do
|
99
|
+
c = @c.call
|
100
|
+
subclass = Class.new c
|
101
|
+
refute subclass.namespace == subclass
|
102
|
+
subclass.namespace!
|
103
|
+
assert subclass.namespace == subclass
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "instance selectors" do
|
108
|
+
before do
|
109
|
+
@human = @c.call
|
110
|
+
@saint = Class.new @human
|
111
|
+
@joe = @human.new name: "Joe"
|
112
|
+
@dick = @saint.new name: "St_IGNUcius"
|
113
|
+
end
|
114
|
+
|
115
|
+
describe ".instances" do
|
116
|
+
it "must return the list of instances in the registry" do
|
117
|
+
@human.instances.must_equal [ @joe, @dick ]
|
118
|
+
@saint.instances.must_equal [ @dick ]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe ".instance" do
|
123
|
+
it "must return instance reference" do
|
124
|
+
-> { @saint.instance @joe }.must_raise NameError
|
125
|
+
-> { @saint.instance :Joe }.must_raise NameError
|
126
|
+
@human.instance( @joe ).must_equal @joe
|
127
|
+
@human.instance( :Joe ).must_equal @joe
|
128
|
+
@saint.instance( @dick ).must_equal @dick
|
129
|
+
@saint.instance( :St_IGNUcius ).must_equal @dick
|
130
|
+
@human.instance( @dick ).must_equal @dick
|
131
|
+
@human.instance( :St_IGNUcius ).must_equal @dick
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe ".nameless_instances" do
|
136
|
+
it "returns the list of registered nameless instances" do
|
137
|
+
c = @c.call
|
138
|
+
d = Class.new c
|
139
|
+
c.nameless_instances.must_equal []
|
140
|
+
i1 = c.new
|
141
|
+
c.nameless_instances.must_equal [ i1 ]
|
142
|
+
d.nameless_instances.must_equal []
|
143
|
+
i2 = d.new
|
144
|
+
c.nameless_instances.must_equal [ i1, i2 ]
|
145
|
+
d.nameless_instances.must_equal [ i2 ]
|
146
|
+
i1.name = :Foo
|
147
|
+
c.nameless_instances.must_equal [ i2 ]
|
148
|
+
d.nameless_instances.must_equal [ i2 ]
|
149
|
+
i2.name = :Bar
|
150
|
+
c.nameless_instances.must_equal []
|
151
|
+
d.nameless_instances.must_equal []
|
152
|
+
end
|
153
|
+
end
|
18
154
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
155
|
+
|
156
|
+
describe "forgetting instances" do
|
157
|
+
before do
|
158
|
+
@human = @c.call
|
159
|
+
@saint = Class.new @human
|
160
|
+
@joe = @human.new name: "Joe"
|
161
|
+
@dick = @saint.new name: "St_IGNUcius"
|
162
|
+
end
|
163
|
+
|
164
|
+
describe ".forget" do
|
165
|
+
it "should clear the given instance from the registry" do
|
166
|
+
-> { @saint.forget @joe }.must_raise NameError
|
167
|
+
@human.forget( :Joe ).must_equal @joe
|
168
|
+
@saint.forget( @dick ).must_equal @dick
|
169
|
+
@human.instances.must_equal []
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe ".__forget__" do
|
174
|
+
it "shoulf forget instance without doing #const_magic" do
|
175
|
+
-> { @saint.__forget__ @joe }.must_raise TypeError
|
176
|
+
-> { @human.__forget__ :Joe }.must_raise TypeError
|
177
|
+
@saint.__forget__( @dick ).must_equal :St_IGNUcius
|
178
|
+
i = @saint.new
|
179
|
+
@saint.__forget__( i ).must_equal nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe ".forget_nameless_instances" do
|
184
|
+
it "should unregister anonymous instances" do
|
185
|
+
i, j = @human.new, @human.new
|
186
|
+
@human.nameless_instances.must_equal [ i, j ]
|
187
|
+
Fred = i
|
188
|
+
@human.forget_nameless_instances
|
189
|
+
@human.instances.must_equal [ @joe, @dick, i ]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe ".forget_all_instances" do
|
194
|
+
it "should unregister all instances" do
|
195
|
+
@saint.forget_all_instances
|
196
|
+
@human.instances.must_equal [ @joe ]
|
197
|
+
@human.forget_all_instances
|
198
|
+
@human.instances.must_equal []
|
199
|
+
end
|
22
200
|
end
|
23
|
-
name
|
24
201
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
202
|
+
|
203
|
+
describe ".new with :name/:ɴ parameters supplied" do
|
204
|
+
it "should work" do
|
205
|
+
c = @c.call
|
206
|
+
i1 = c.new name: :Fred
|
207
|
+
i1.name.must_equal :Fred
|
208
|
+
i2 = c.new name: "Joe"
|
209
|
+
i2.name.must_equal :Joe
|
28
210
|
end
|
29
|
-
|
211
|
+
end
|
212
|
+
|
213
|
+
describe ".new with name!: parameter supplied" do
|
214
|
+
it "must steal names of other instances" do
|
215
|
+
c = @c.call
|
216
|
+
i1 = c.new name: "Joe"
|
217
|
+
i1.name.must_equal :Joe
|
218
|
+
-> { c.new name: "Joe" }.must_raise NameError
|
219
|
+
i2 = c.new name!: "Joe"
|
220
|
+
i2.name.must_equal :Joe
|
221
|
+
i1.name.must_equal nil
|
222
|
+
-> { c.new name: "Joe", name!: "Joe" }
|
223
|
+
.must_raise ArgumentError
|
224
|
+
-> { c.new name!: "Joe", avid: nil }
|
225
|
+
.must_raise ArgumentError
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end # describe NameMagic::ClassMethods
|
229
|
+
|
230
|
+
describe ".instantiation_exec hook" do
|
231
|
+
it "must execute upon instantiation" do
|
232
|
+
c = @c.call
|
233
|
+
object_ids = []
|
234
|
+
c.instantiation_exec do |instance|
|
235
|
+
object_ids << instance.object_id
|
236
|
+
end
|
237
|
+
i1 = c.new
|
238
|
+
object_ids.size.must_equal 1
|
239
|
+
i2 = c.new
|
240
|
+
object_ids.size.must_equal 2
|
241
|
+
object_ids.must_equal [ i1.object_id, i2.object_id ]
|
242
|
+
end
|
243
|
+
|
244
|
+
it "must run in the context of the instance's class" do
|
245
|
+
c = @c.call
|
246
|
+
c.instantiation_exec do foobar() end
|
247
|
+
flag = false
|
248
|
+
-> { c.new }.must_raise NoMethodError
|
249
|
+
flag.must_equal false
|
250
|
+
c.define_singleton_method :foobar do flag = true end
|
251
|
+
c.new
|
252
|
+
flag.must_equal true
|
30
253
|
end
|
31
254
|
end
|
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
|
-
Rover.name.must_equal :Rover
|
64
|
-
XXX::Rover.must_be_kind_of @ç
|
65
|
-
@ç.namespace!
|
66
|
-
Spot = @ç.new
|
67
|
-
@ç.const_magic
|
68
|
-
Spot.name.must_equal :Spot
|
69
|
-
-> { XXX::Spot }.must_raise NameError
|
70
|
-
@ç.const_get( :Spot ).must_be_kind_of @ç
|
71
|
-
# Array
|
72
|
-
[ Spot ].names.must_equal [ :Spot ]
|
73
|
-
{ Spot => 42 }.keys_to_names.must_equal( { Spot: 42 } )
|
255
|
+
|
256
|
+
describe ".exec_when_naming hook" do
|
257
|
+
it "must execute just before naming and allow censorship" do
|
258
|
+
c = @c.call
|
259
|
+
baptised_instances = []
|
260
|
+
discarded_names = []
|
261
|
+
c.exec_when_naming do |name, instance, old_name|
|
262
|
+
baptised_instances << instance
|
263
|
+
discarded_names << old_name if old_name
|
264
|
+
# Censor the name.
|
265
|
+
name.end_with?( "gnucius" ) ? "St_IGNUcius" : name
|
266
|
+
end
|
267
|
+
c.new name: :Joe
|
268
|
+
c.new name: :Dave
|
269
|
+
baptised_instances
|
270
|
+
.must_equal [ c.instance( :Joe ), c.instance( :Dave ) ]
|
271
|
+
discarded_names.must_equal []
|
272
|
+
c.instance( :Dave ).name = "Ignucius"
|
273
|
+
baptised_instances.names
|
274
|
+
.must_equal [ :Joe, :St_IGNUcius, :St_IGNUcius ]
|
275
|
+
discarded_names.must_equal [ :Dave ]
|
276
|
+
end
|
277
|
+
|
278
|
+
it "must execute in the context of the instance's class" do
|
279
|
+
c = @c.call
|
280
|
+
i = c.new
|
281
|
+
c.define_singleton_method :foobar do "Foobar" end
|
282
|
+
c.exec_when_naming do foobar end
|
283
|
+
i.name = :Dave
|
284
|
+
i.name.must_equal :Foobar
|
285
|
+
end
|
74
286
|
end
|
75
|
-
|
287
|
+
|
288
|
+
describe NameMagic::Namespace do
|
289
|
+
before do
|
290
|
+
@human = @c.call
|
291
|
+
@saint = Class.new @human
|
292
|
+
@ns = Module.new
|
293
|
+
@human.namespace = @ns
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should carry certain instance methods" do
|
297
|
+
[ :instances,
|
298
|
+
:__instances__,
|
299
|
+
:__avid_instances__,
|
300
|
+
:instance,
|
301
|
+
:const_magic,
|
302
|
+
:nameless_instances,
|
303
|
+
:forget,
|
304
|
+
:__forget__,
|
305
|
+
:forget_nameless_instances,
|
306
|
+
:forget_all_instances,
|
307
|
+
:instantiation_exec,
|
308
|
+
:exec_when_naming,
|
309
|
+
:validate_name
|
310
|
+
].each { |ß|
|
311
|
+
NameMagic::Namespace.instance_methods.must_include ß
|
312
|
+
}
|
313
|
+
NameMagic::Namespace.private_instance_methods
|
314
|
+
.must_include :search_all_modules
|
315
|
+
end
|
316
|
+
|
317
|
+
describe "Namespace#instances" do
|
318
|
+
it "should work as expected" do
|
319
|
+
@ns.instances.must_equal []
|
320
|
+
@saint.new
|
321
|
+
@ns.instances.size.must_equal 1
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe "Namespace#__instances__" do
|
326
|
+
it "should work as expected" do
|
327
|
+
@ns.__instances__.must_equal( {} )
|
328
|
+
@saint.new
|
329
|
+
@ns.__instances__.size.must_equal 1
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
describe "Namespace#__avid_instances__" do
|
334
|
+
it "should work as expected" do
|
335
|
+
@ns.__avid_instances__.must_equal []
|
336
|
+
@saint.new
|
337
|
+
@ns.__avid_instances__.size.must_equal 1
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
describe "Namespace#instance" do
|
342
|
+
it "should work as expected" do
|
343
|
+
-> { @ns.instance( :foo ) }.must_raise NameError
|
344
|
+
@saint.new name: :Dave
|
345
|
+
@ns.instance( :Dave ).must_be_kind_of @saint
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
describe "Namespace#const_magic" do
|
350
|
+
it "should manually trigger constant magic" do
|
351
|
+
registry = @ns.__instances__
|
352
|
+
registry.size.must_equal 0
|
353
|
+
m = Module.new
|
354
|
+
# New instance is assigned to the constant "Dave", but
|
355
|
+
# Namespace#const_magic is not triggered automatically.
|
356
|
+
m::Dave = @saint.new
|
357
|
+
registry.size.must_equal 1
|
358
|
+
instance, name = registry.to_a.first
|
359
|
+
assert name.nil?
|
360
|
+
# Up until this point, #const_magic was not triggered,
|
361
|
+
# because no methods triggering it were invoked. In
|
362
|
+
# everyday use of NameMagic, this rarely happens, but
|
363
|
+
# should it ever happen, we can trigger #const_magic
|
364
|
+
# manually:
|
365
|
+
@ns.const_magic
|
366
|
+
# Now the instance already knows its name:
|
367
|
+
instance, name = registry.to_a.first
|
368
|
+
name.must_equal :Dave
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
describe "Namespace#nameless_instances" do
|
373
|
+
it "should work as expected" do
|
374
|
+
@ns.nameless_instances.must_equal []
|
375
|
+
@human.new
|
376
|
+
@ns.nameless_instances.size.must_equal 1
|
377
|
+
@ns.instances.first.name = "Fred"
|
378
|
+
@ns.nameless_instances.must_equal []
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
describe "Namespace#forget" do
|
383
|
+
it "should work as expected" do
|
384
|
+
i = @human.new name: :Fred
|
385
|
+
@ns.instances.size.must_equal 1
|
386
|
+
@ns.forget :Fred
|
387
|
+
@ns.instances.size.must_equal 0
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe "Namespace#__forget__" do
|
392
|
+
it "should work as expected" do
|
393
|
+
i = @human.new name: :Fred
|
394
|
+
@ns.instances.size.must_equal 1
|
395
|
+
@ns.__forget__( :Fred ).must_equal false
|
396
|
+
@ns.instances.size.must_equal 1
|
397
|
+
@ns.__forget__( i ).must_equal :Fred
|
398
|
+
@ns.instances.size.must_equal 0
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
describe "Namespace#forget_nameless_instances" do
|
403
|
+
it "should work as expected" do
|
404
|
+
i = @saint.new ɴ: "Dave"
|
405
|
+
j = @saint.new
|
406
|
+
@ns.instances.size.must_equal 2
|
407
|
+
@ns.forget_nameless_instances
|
408
|
+
@ns.instances.must_equal [ i ]
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
describe "Namespace#forget_all_instances" do
|
413
|
+
it "should work as expected" do
|
414
|
+
@saint.new ɴ: "Dave"
|
415
|
+
@ns.instances.size.must_equal 1
|
416
|
+
@ns.forget_all_instances
|
417
|
+
@ns.instances.must_equal []
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
describe "Namespace#instantiation_exec hook" do
|
422
|
+
it "should work as expected" do
|
423
|
+
flag = false
|
424
|
+
@ns.instantiation_exec do flag = true end
|
425
|
+
@saint.new
|
426
|
+
assert flag
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe "Namespace#exec_when_naming hook" do
|
431
|
+
it "should work as expected" do
|
432
|
+
flag = false
|
433
|
+
@ns.exec_when_naming do |name, _, _| flag = true; name end
|
434
|
+
@saint.new
|
435
|
+
refute flag
|
436
|
+
@ns.instances.first.name = :Dave
|
437
|
+
assert flag
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
describe "Namespace#validate_name" do
|
442
|
+
it "should work as expected" do
|
443
|
+
@ns.validate_name( :Dave ).must_equal :Dave
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end # describe NameMagic::Namespace
|
447
|
+
|
448
|
+
describe "NameMagic instance methods" do
|
449
|
+
before do
|
450
|
+
@human = @c.call
|
451
|
+
@anon = @human.new
|
452
|
+
@m = Module.new
|
453
|
+
@m::Foo = Module.new
|
454
|
+
@m::Foo::Frank = @human.new
|
455
|
+
end
|
456
|
+
|
457
|
+
describe "#_name_" do
|
458
|
+
it "should return demodulized name of the instance" do
|
459
|
+
@m::Foo::Frank._name_.must_equal :Frank
|
460
|
+
@anon._name_.must_equal nil
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
describe "#full_name" do
|
465
|
+
it "should return full name of the instance" do
|
466
|
+
skip
|
467
|
+
# FIXME: The test below is purposely wrong.
|
468
|
+
@m::Foo::Frank.full_name.must_equal "@m::Foo::Frank"
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe "#__name__" do
|
473
|
+
it "returns full name without triggering #const_magic" do
|
474
|
+
@m::Foo::Frank.__name__.must_equal nil
|
475
|
+
# Now trigger #const_magic manually.
|
476
|
+
@human.const_magic
|
477
|
+
@m::Foo::Frank.__name__.must_equal :Frank
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
describe "#name=" do
|
482
|
+
it "names the instance" do
|
483
|
+
@m::Foo::Frank.name.must_equal :Frank
|
484
|
+
@m::Foo::Frank.name = :Joe
|
485
|
+
@m::Foo::Frank.name.must_equal :Joe
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
describe "#name!" do
|
490
|
+
it "names the instance aggresively" do
|
491
|
+
@m::Foo::Frank.name.must_equal :Frank
|
492
|
+
i = @human.new
|
493
|
+
-> { i.name = :Frank }.must_raise NameError
|
494
|
+
i.name! :Frank
|
495
|
+
i.name.must_equal :Frank
|
496
|
+
@m::Foo::Frank.name.must_equal nil
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
describe "#avid?" do
|
501
|
+
it "informes whether the instance is avid" do
|
502
|
+
# Avid instance is an unnamed instance that is willing
|
503
|
+
# to accept name even if this already belongs to another
|
504
|
+
# instance, unregistering the conflicter in the process.
|
505
|
+
assert @anon.avid?
|
506
|
+
@anon.name! :Fred
|
507
|
+
refute @anon.avid?
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
describe "#make_avid!" do
|
512
|
+
it "is a private method that makes the instance avid" do
|
513
|
+
@anon.name = :Fred
|
514
|
+
@anon.unname!
|
515
|
+
-> { @anon.make_avid! }.must_raise NoMethodError
|
516
|
+
@anon.send :make_avid!
|
517
|
+
assert @anon.avid?
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
describe "#make_not_avid!" do
|
522
|
+
it "removes the avid state from the instance" do
|
523
|
+
assert @anon.avid?
|
524
|
+
@anon.make_not_avid!
|
525
|
+
refute @anon.avid?
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
describe "NameMagic#exec_when_named hook" do
|
530
|
+
it "is executed just after instance naming" do
|
531
|
+
i = @human.new
|
532
|
+
names = []
|
533
|
+
i.exec_when_named do names << name end
|
534
|
+
names.must_equal []
|
535
|
+
i.name = :Fred
|
536
|
+
names.must_equal [:Fred]
|
537
|
+
j = @human.new
|
538
|
+
names.must_equal [:Fred]
|
539
|
+
j.name = :Joe
|
540
|
+
names.must_equal [:Fred]
|
541
|
+
# Finally, one more way how to use this hook.
|
542
|
+
@human.instantiation_exec do |instance|
|
543
|
+
instance.exec_when_named do names << name end
|
544
|
+
end
|
545
|
+
@human.new name: :Dave
|
546
|
+
names.must_equal [:Fred, :Dave]
|
547
|
+
@human.new name: :St_IGNUcius
|
548
|
+
names.must_equal [:Fred, :Dave, :St_IGNUcius]
|
549
|
+
end
|
550
|
+
|
551
|
+
it "is executed in the context of the instance" do
|
552
|
+
i = @human.new
|
553
|
+
i.define_singleton_method :hello do "hello" end
|
554
|
+
result = nil
|
555
|
+
i.exec_when_named do result = hello, name end
|
556
|
+
i.name = :Dave
|
557
|
+
result.join( ' ' ).must_equal "hello Dave"
|
558
|
+
# Explanation: If the block was not executed in the
|
559
|
+
# context of instance i, method hello wouldn't work.
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
describe "NameMagic#to_s" do
|
564
|
+
it "is defined to show names of named instances" do
|
565
|
+
@m::Foo::Frank.to_s.must_equal "Frank"
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
describe "NameMagic#inspect" do
|
570
|
+
it "must show name for named instances" do
|
571
|
+
@m::Foo::Frank.inspect.must_equal "Frank"
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end # describe "NameMagic instance methods"
|
575
|
+
|
576
|
+
describe NameMagic::ArrayMethods do
|
577
|
+
before do
|
578
|
+
c = @c.call
|
579
|
+
@e1 = c.new name: :Fred
|
580
|
+
@e2 = c.new name: :Joe
|
581
|
+
@e3 = c.new
|
582
|
+
@e4 = c.new name: :Julia
|
583
|
+
@a = @e1, @e2, @e3, @e4
|
584
|
+
end
|
585
|
+
|
586
|
+
describe "Array#names" do
|
587
|
+
it "maps unnamed objects into nil by default" do
|
588
|
+
@a.names.must_equal [ :Fred, :Joe, nil, :Julia ]
|
589
|
+
@a.names( nil ).must_equal [ :Fred, :Joe, nil, :Julia ]
|
590
|
+
end
|
591
|
+
|
592
|
+
it "maps unnamed objects into themselves if option=true" do
|
593
|
+
@a.names( true ).must_equal [ :Fred, :Joe, @e3, :Julia ]
|
594
|
+
end
|
595
|
+
|
596
|
+
it "omits the unnamed instances if option=false" do
|
597
|
+
@a.names( false ).must_equal [ :Fred, :Joe, :Julia ]
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
describe "Array#_names_" do
|
602
|
+
it "maps unnamed objects into nil by default" do
|
603
|
+
@a._names_.must_equal [ :Fred, :Joe, nil, :Julia ]
|
604
|
+
@a._names_( nil ).must_equal [ :Fred, :Joe, nil, :Julia ]
|
605
|
+
end
|
606
|
+
|
607
|
+
it "maps unnamed objects into themselves if option=true" do
|
608
|
+
@a._names_( true ).must_equal [ :Fred, :Joe, @e3, :Julia ]
|
609
|
+
end
|
610
|
+
|
611
|
+
it "omits the unnamed instances if option=false" do
|
612
|
+
@a._names_( false ).must_equal [ :Fred, :Joe, :Julia ]
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
describe NameMagic::HashMethods do
|
618
|
+
before do
|
619
|
+
c = @c.call
|
620
|
+
@i, @j = c.new( name: :Fred ), c.new( name: :Joe )
|
621
|
+
end
|
622
|
+
|
623
|
+
describe "Hash#keys_to_names" do
|
624
|
+
# TODO: Consider method to keys_to_full_names.
|
625
|
+
it "must work as expected" do
|
626
|
+
{ @i => 1, @j => 2 }.keys_to_names
|
627
|
+
.must_equal( { Fred: 1, Joe: 2 } )
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
describe "Hash#keys_to_names!" do
|
632
|
+
it "must work as expected" do
|
633
|
+
h = { @i => 1, @j => 2 }
|
634
|
+
h.keys_to_names!
|
635
|
+
h.must_equal( { Fred: 1, Joe: 2 } )
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
describe "Hash#keys_to_ɴ" do
|
640
|
+
it "must work as expected" do
|
641
|
+
{ @i => 1, @j => 2 }.keys_to_ɴ
|
642
|
+
.must_equal( { Fred: 1, Joe: 2 } )
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
describe "Hash#keys_to_ɴ!" do
|
647
|
+
it "must work as expected" do
|
648
|
+
h = { @i => 1, @j => 2 }
|
649
|
+
h.keys_to_ɴ!
|
650
|
+
h.must_equal( { Fred: 1, Joe: 2 } )
|
651
|
+
end
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
describe "renaming" do
|
656
|
+
before do
|
657
|
+
@human = @c.call
|
658
|
+
@city = Module.new
|
659
|
+
@human.namespace = @city
|
660
|
+
end
|
661
|
+
|
662
|
+
it "is possible without restrictions" do
|
663
|
+
a = @human.new name: :Fred
|
664
|
+
a.name.must_equal :Fred
|
665
|
+
# Fred can be renamed to Joe without problems.
|
666
|
+
a.name = :Joe
|
667
|
+
a.name.must_equal :Joe
|
668
|
+
end
|
669
|
+
|
670
|
+
it "can be prohibited eg. by naming hook" do
|
671
|
+
@city.exec_when_naming do |name, instance, old_name|
|
672
|
+
fail NameError, "Renaming is now prohibited!" if old_name
|
673
|
+
name
|
674
|
+
end
|
675
|
+
a = @human.new name: :Fred
|
676
|
+
-> { a.name = :Joe }.must_raise NameError
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
describe "name collisions" do
|
681
|
+
before do
|
682
|
+
@human = @c.call
|
683
|
+
@city = Module.new
|
684
|
+
@human.namespace = @city
|
685
|
+
@a = @human.new name: :Fred
|
686
|
+
end
|
687
|
+
|
688
|
+
it "cannot construct another Fred by #new method" do
|
689
|
+
-> { @human.new name: :Fred }.must_raise NameError
|
690
|
+
end
|
691
|
+
|
692
|
+
it "cannot construct another Fred by #name= method" do
|
693
|
+
b = @human.new
|
694
|
+
-> { b.name = :Fred }.must_raise NameError
|
695
|
+
end
|
696
|
+
|
697
|
+
it "can, however, steal name Fred by constant assignment" do
|
698
|
+
m = Module.new
|
699
|
+
m::Fred = @human.new
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
describe "how avid instances work" do
|
704
|
+
describe "why avidity is needed" do
|
705
|
+
# In NameMagic, names are unique. At the same time, naming
|
706
|
+
# can be performed by constant assignment. If all instances
|
707
|
+
# were always avid, fights over a name would ensue if two
|
708
|
+
# constant assignments leading to the same naming were
|
709
|
+
# present in the namespace. We could get rid of avidity
|
710
|
+
# only if the naming was done using constant names with
|
711
|
+
# full Ruby namespace path. The question is, should we do
|
712
|
+
# this? It would surely break some dependencies. I am not
|
713
|
+
# gonna do it until YPetri paper is out. However, I feel
|
714
|
+
# that naming by full namespace path of the constant will
|
715
|
+
# be a must if I try to make use of the Ruby's native
|
716
|
+
# namespace mechanism for YPetri / YNelson nets and for
|
717
|
+
# YChem / YCell modules.
|
718
|
+
end
|
719
|
+
|
720
|
+
describe "new instance" do
|
721
|
+
before do
|
722
|
+
@test_class = @c.call
|
723
|
+
@test_instance = @test_class.new
|
724
|
+
end
|
725
|
+
|
726
|
+
it "is created avid" do
|
727
|
+
assert @test_instance.avid?
|
728
|
+
end
|
729
|
+
|
730
|
+
it "can lose its avidity using #make_not_avid! method" do
|
731
|
+
@test_instance.make_not_avid!
|
732
|
+
refute @test_instance.avid?
|
733
|
+
end
|
734
|
+
|
735
|
+
it "also loses avidity by naming" do
|
736
|
+
@test_instance.name = :Joe
|
737
|
+
refute @test_instance.avid?
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
describe "unnamed instance" do
|
742
|
+
before do
|
743
|
+
@test_class = @c.call
|
744
|
+
@test_instance = @test_class.new
|
745
|
+
end
|
746
|
+
|
747
|
+
it "does not regain avidity it lost upon naming" do
|
748
|
+
assert @test_instance.avid?
|
749
|
+
@test_instance.name = :Jane
|
750
|
+
refute @test_instance.avid?
|
751
|
+
@test_instance.unname!.must_equal :Jane
|
752
|
+
refute @test_instance.avid?
|
753
|
+
end
|
754
|
+
|
755
|
+
it "does not raise repeated NameError if it is assigned" +
|
756
|
+
"to some forgotten constant in the namespace" do
|
757
|
+
m, n = Module.new, Module.new
|
758
|
+
i, j = @test_class.new, @test_class.new
|
759
|
+
m::Jane = i
|
760
|
+
assert i.name == :Jane
|
761
|
+
n::Jane = j
|
762
|
+
assert i.name == nil
|
763
|
+
assert j.name == :Jane
|
764
|
+
i.send :make_avid!
|
765
|
+
assert i.avid?
|
766
|
+
assert i.name == :Jane
|
767
|
+
assert j.name == nil
|
768
|
+
refute i.avid?
|
769
|
+
refute j.avid?
|
770
|
+
end
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
describe "unnaming" do
|
775
|
+
before do
|
776
|
+
@test_class = @c.call
|
777
|
+
@test_instance = @test_class.new
|
778
|
+
end
|
779
|
+
|
780
|
+
describe "NameMagic#unname!" do
|
781
|
+
it "unnames the instance if unnaming is allowed" do
|
782
|
+
@test_instance.name = :Fred
|
783
|
+
@test_instance.unname!.must_equal :Fred
|
784
|
+
assert @test_instance.name.nil?
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
788
|
+
describe "NameMagic#unnaming_allowed?" do
|
789
|
+
it "depends on #permanent_names? method" do
|
790
|
+
assert @test_instance.unnaming_allowed?
|
791
|
+
@test_class.class_exec { permanent_names! }
|
792
|
+
refute @test_instance.unnaming_allowed?
|
793
|
+
end
|
794
|
+
end
|
795
|
+
|
796
|
+
describe "NameMagic#exec_when_unnamed" do
|
797
|
+
it "executes upon unnaming" do
|
798
|
+
reporter = []
|
799
|
+
@test_instance.exec_when_unnamed { reporter << object_id }
|
800
|
+
@test_instance.unname!
|
801
|
+
# The instance was anonymous to begin with, so no unnaming
|
802
|
+
# took place. Therefore, no change to the reporter.
|
803
|
+
reporter.must_equal []
|
804
|
+
@test_instance.name = :Fred
|
805
|
+
reporter.must_equal []
|
806
|
+
@test_instance.unname!
|
807
|
+
# Block was called only after the unnaming has happened.
|
808
|
+
reporter.must_equal [ @test_instance.object_id ]
|
809
|
+
@test_instance.name = :Fred
|
810
|
+
another_instance = @test_class.new
|
811
|
+
m = Module.new
|
812
|
+
# Here, unnaming of the former Fred should take place.
|
813
|
+
m::Fred = another_instance
|
814
|
+
# We have to invoke const_magic manually.
|
815
|
+
@test_class.const_magic
|
816
|
+
reporter.must_equal [ @test_instance.object_id ] * 2
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
describe "NameMagic::ClassMethods#exec_when_unnaming" do
|
821
|
+
it "executes just before unnaming the instance" do
|
822
|
+
reporter = []
|
823
|
+
@test_class.exec_when_unnaming do
|
824
|
+
reporter = instances.names
|
825
|
+
end
|
826
|
+
@test_instance.unname!.must_equal nil
|
827
|
+
reporter.must_equal []
|
828
|
+
@test_instance.name = :Fred
|
829
|
+
@test_instance.unname!.must_equal :Fred
|
830
|
+
reporter.must_equal [ :Fred ]
|
831
|
+
@test_class.instances.names.must_equal [ nil ]
|
832
|
+
end
|
833
|
+
end
|
834
|
+
|
835
|
+
describe "NameMagic::Namespace#exec_when_unnaming" do
|
836
|
+
it "executes just before unnaming the instance" do
|
837
|
+
m = Module.new
|
838
|
+
c = @c.call
|
839
|
+
c.namespace = m
|
840
|
+
reporter = []
|
841
|
+
m.exec_when_unnaming do |instance, former_name|
|
842
|
+
reporter << instance
|
843
|
+
reporter << former_name
|
844
|
+
end
|
845
|
+
i = c.new name: :Fred
|
846
|
+
j = c.new name!: :Fred
|
847
|
+
j.name.must_equal :Fred
|
848
|
+
i.name.must_equal nil
|
849
|
+
reporter.must_equal [ i, :Fred ]
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
describe "NameMagic::ClassMethods#permanent_names?" do
|
854
|
+
it "is selector of @permanent_names attribute" do
|
855
|
+
refute @test_class.permanent_names?
|
856
|
+
@test_class.class_exec { permanent_names! }
|
857
|
+
assert @test_class.permanent_names?
|
858
|
+
end
|
859
|
+
end
|
860
|
+
|
861
|
+
describe "NameMagic::ClassMethods#permanent_names!" do
|
862
|
+
it "should prohibit unnaming of the instances" do
|
863
|
+
c = @c.call
|
864
|
+
i = c.new name: :Fred
|
865
|
+
i.name.must_equal :Fred
|
866
|
+
i.unname!.must_equal :Fred
|
867
|
+
i.name = :Fred
|
868
|
+
c.instance_exec { permanent_names! }
|
869
|
+
-> { i.unname! }.must_raise NameError
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
describe "NameMagic::Namespace#permanent_names? and" +
|
874
|
+
"NameMagic::Namespace#permanent_names!" do
|
875
|
+
it "is selector of @permanent_names attribute" do
|
876
|
+
m = Module.new
|
877
|
+
c = @c.call
|
878
|
+
c.namespace = m
|
879
|
+
c.permanent_names!
|
880
|
+
assert m.permanent_names?
|
881
|
+
n = Module.new
|
882
|
+
c.namespace = n
|
883
|
+
# Shifting to a brand new namespace means that
|
884
|
+
# @permanent_names attribute will be reset to nil again.
|
885
|
+
# Fortunately, such operation is possible only when the
|
886
|
+
# instance registry is empty.
|
887
|
+
refute c.permanent_names?
|
888
|
+
refute n.permanent_names?
|
889
|
+
n.permanent_names!
|
890
|
+
assert c.permanent_names?
|
891
|
+
assert n.permanent_names?
|
892
|
+
end
|
893
|
+
end
|
894
|
+
end
|
895
|
+
|
896
|
+
describe "how NameMagic can be included modules and classes" do
|
897
|
+
it "can be included directly" do
|
898
|
+
c = Class.new do include NameMagic end
|
899
|
+
i = c.new name: :Jane
|
900
|
+
c.instances.names.must_equal [ :Jane ]
|
901
|
+
end
|
902
|
+
|
903
|
+
it "can be included in a module first, which can be " +
|
904
|
+
"included in another module, and so on, until it is " +
|
905
|
+
"included in a class, which can be subclassed, and " +
|
906
|
+
"subclassed again, and it will still work as expected" do
|
907
|
+
m1 = Module.new do include NameMagic end
|
908
|
+
m2 = Module.new do include m1 end
|
909
|
+
m3 = Module.new do include m2 end
|
910
|
+
animal = Class.new do include m3 end
|
911
|
+
mammal = Class.new animal
|
912
|
+
human = Class.new mammal
|
913
|
+
animal.instances.must_equal []
|
914
|
+
human.new name!: :Jane
|
915
|
+
animal.instances.names.must_equal [ :Jane ]
|
916
|
+
mammal.new name: :Spot
|
917
|
+
animal.instances.names.must_equal [ :Jane, :Spot ]
|
918
|
+
mammal.instances.names.must_equal [ :Jane, :Spot ]
|
919
|
+
human.instances.names.must_equal [ :Jane ]
|
920
|
+
end
|
921
|
+
end
|
922
|
+
end # describe NameMagic
|