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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/lib/y_support/all.rb +2 -32
  3. data/lib/y_support/core_ext/array.rb +2 -2
  4. data/lib/y_support/core_ext/class.rb +2 -2
  5. data/lib/y_support/core_ext/enumerable.rb +2 -2
  6. data/lib/y_support/core_ext/hash/misc.rb +23 -10
  7. data/lib/y_support/core_ext/hash.rb +2 -2
  8. data/lib/y_support/core_ext/module/misc.rb +9 -0
  9. data/lib/y_support/core_ext/module.rb +2 -2
  10. data/lib/y_support/core_ext/numeric.rb +2 -2
  11. data/lib/y_support/core_ext/object/inspection.rb +8 -2
  12. data/lib/y_support/core_ext/object.rb +3 -3
  13. data/lib/y_support/core_ext/string/misc.rb +9 -12
  14. data/lib/y_support/core_ext/string.rb +2 -2
  15. data/lib/y_support/core_ext/symbol.rb +2 -2
  16. data/lib/y_support/core_ext.rb +1 -1
  17. data/lib/y_support/flex_coerce/class_methods.rb +49 -0
  18. data/lib/y_support/flex_coerce/flex_proxy.rb +121 -0
  19. data/lib/y_support/flex_coerce/module_methods.rb +37 -0
  20. data/lib/y_support/flex_coerce.rb +24 -0
  21. data/lib/y_support/kde.rb +1 -1
  22. data/lib/y_support/literate.rb +253 -0
  23. data/lib/y_support/local_object.rb +1 -1
  24. data/lib/y_support/name_magic/array_methods.rb +48 -0
  25. data/lib/y_support/name_magic/class_methods.rb +205 -161
  26. data/lib/y_support/name_magic/hash_methods.rb +33 -0
  27. data/lib/y_support/name_magic/namespace.rb +449 -0
  28. data/lib/y_support/name_magic.rb +358 -100
  29. data/lib/y_support/null_object.rb +1 -1
  30. data/lib/y_support/respond_to.rb +1 -1
  31. data/lib/y_support/stdlib_ext/matrix/misc.rb +2 -2
  32. data/lib/y_support/stdlib_ext/matrix.rb +2 -2
  33. data/lib/y_support/stdlib_ext.rb +1 -1
  34. data/lib/y_support/typing/array.rb +1 -1
  35. data/lib/y_support/typing/enumerable.rb +1 -1
  36. data/lib/y_support/typing/hash.rb +1 -1
  37. data/lib/y_support/typing/module.rb +1 -1
  38. data/lib/y_support/typing/object/typing.rb +17 -15
  39. data/lib/y_support/typing/object.rb +1 -1
  40. data/lib/y_support/typing.rb +14 -10
  41. data/lib/y_support/unicode.rb +1 -1
  42. data/lib/y_support/version.rb +1 -1
  43. data/lib/y_support/x.rb +2 -1
  44. data/test/flex_coerce_test.rb +134 -0
  45. data/test/literate_test.rb +231 -0
  46. data/test/misc_test.rb +49 -27
  47. data/test/name_magic_test.rb +907 -60
  48. data/test/typing_test.rb +7 -7
  49. metadata +14 -13
  50. data/lib/y_support/abstract_algebra.rb +0 -234
  51. data/lib/y_support/name_magic/array.rb +0 -38
  52. data/lib/y_support/name_magic/hash.rb +0 -31
  53. data/lib/y_support/name_magic/namespace_methods.rb +0 -260
  54. data/lib/y_support/try.rb +0 -133
  55. data/test/abstract_algebra_test.rb +0 -138
  56. data/test/performance_test_example.rb +0 -23
  57. data/test/try_test.rb +0 -102
@@ -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
- @ç = Class.new do include mod end
11
- @reporter = Object.new
12
- puts "..."
13
- @reporter.singleton_class.class_exec { attr_reader :report, :naming }
14
- @ç.ancestors.must_include NameMagic
15
- @ç.singleton_class.ancestors.must_include NameMagic::ClassMethods
16
- @ç.namespace.new_instance_hook do |instance|
17
- @reporter.define_singleton_method :report do "Instance reported" end
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
- @ç.namespace.name_set_hook do |name, instance, old_name|
20
- @reporter.define_singleton_method :name_set do
21
- "Name of the new instance was #{name}"
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
- @ç.name_get_hook do |name_object|
26
- @reporter.define_singleton_method :name_get do
27
- "Name get hook called on #{name_object}"
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
- name_object
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
- it "should work" do
34
- @ç.must_respond_to :const_magic
35
- @ç.instances.must_be_empty
36
- @ç.nameless_instances.must_be_empty
37
- @reporter.report.must_equal nil
38
- x = @ç.new( name: "Boris" )
39
- @reporter.report.must_equal "Instance reported"
40
- @reporter.name_set.must_equal "Name of the new instance was Boris"
41
- x.name.must_equal :Boris
42
- @reporter.name_get.must_equal "Name get hook called on Boris"
43
- ufo = @ç.new
44
- @ç.nameless_instances.must_equal [ufo]
45
- UFO = @ç.new
46
- @reporter.report.must_equal "Instance reported"
47
- @reporter.name_set.must_equal "Name of the new instance was Boris"
48
- UFO.name
49
- @reporter.name_set.must_equal "Name of the new instance was UFO"
50
- @reporter.name_get.must_equal "Name get hook called on UFO"
51
- Elaine = @ç.new
52
- Elaine.name.must_equal :Elaine
53
- m = Module.new
54
- XXX = m
55
- @ç.namespace = XXX
56
- @ç.namespace.must_equal m
57
- @ç.singleton_class.must_include ::NameMagic::ClassMethods
58
- m.singleton_class.must_include ::NameMagic::NamespaceMethods
59
- Rover = @ç.new
60
- @ç.namespace.must_equal XXX
61
- @ç.nameless_instances.must_equal [ Rover ]
62
- @ç.const_magic
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
- end
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