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
@@ -3,210 +3,254 @@
3
3
  # Class methods for the classes that include NameMagic.
4
4
  #
5
5
  module NameMagic::ClassMethods
6
- # Presents the instances registered by the namespace. Takes one optional
7
- # argument. If set to _false_, the method returns all the instances
8
- # registered by the namespace. If set to _true_ (default), only returns
9
- # those instances registered by the namespace, which are of exactly the
10
- # same class as the receiver (ie. excluding the instances of the subclasses
11
- # of this class). Example:
6
+ # Delegates methods to the namespace used by the class. Since the
7
+ # class frequently acts as its own namespace, this delegation
8
+ # requires special handling.
12
9
  #
13
- # <code>
14
- # class Animal; include NameMagic end
15
- # Cat, Dog = Class.new( Animal ), Class.new( Animal )
16
- # Spot = Dog.new
17
- # Livia = Cat.new
18
- # Animal.instances #=> returns 2 instances (2 animals)
19
- # Dog.instances #=> returns 1 instance (only 1 is of Dog subclass)
20
- # Dog.instances( false ) #=> 2 instances again (all the animals)
21
- # </code>
22
- #
23
- def instances option=true
24
- return super if namespace == self
25
- ii = namespace.instances
26
- option ? ii.select { |i| i.kind_of? self } : ii
10
+ def self.delegate_to_namespace *symbols
11
+ symbols.each { |ß|
12
+ module_eval "def #{ß} *args\n" +
13
+ " return super if namespace == self\n" +
14
+ " namespace.#{ß}( *args )\n" +
15
+ "end"
16
+ }
27
17
  end
28
18
 
29
- # Deprecated method to get full names of the named instances. Use
30
- # <code>instances.names</code> instead. Takes one optional argument,
31
- # same as +#instances+ method.
32
- #
33
- def instance_names option=true
34
- warn "Method #instance_names is deprecated. Use 'instances._names_' or 'instances.names' instead!"
35
- instances( option ).names( false )
36
- end
19
+ delegate_to_namespace :permanent_names!
20
+ delegate_to_namespace :permanent_names?
21
+ delegate_to_namespace :__instances__
22
+ delegate_to_namespace :__avid_instances__
23
+ delegate_to_namespace :const_magic
24
+ delegate_to_namespace :validate_name
25
+ delegate_to_namespace :forget_nameless_instances
26
+ delegate_to_namespace :instantiation_exec
27
+ delegate_to_namespace :exec_when_naming
28
+ delegate_to_namespace :exec_when_unnaming
37
29
 
38
- # Presents namespace-owned +@instances+ hash. The hash consists of pairs
39
- # <code>{ instance => instance_name }</code>. Unnamed instances have +nil+
40
- # assigned to them as their name. (The method does not trigger
41
- # +#const_magic+.)
42
- #
43
- def __instances__
44
- namespace == self ? super : namespace.__instances__
45
- end
30
+ # Note: These aliases must stay while the dependencies need them.
31
+ alias new_instance_hook instantiation_exec
32
+ alias name_set_hook exec_when_naming
46
33
 
47
- # Presents namespace-owned +@avid_instances+ (array of avid instances). "Avid"
48
- # means that the instance is able to overwrite a name used by another
49
- # registered instance. (This method does not trigger +#const_magic+.)
34
+ # Sets the namespace for the class.
50
35
  #
51
- def __avid_instances__
52
- namespace == self ? super : namespace.__avid_instances__
36
+ def namespace= modul
37
+ fail "Namespace cannot be redefined when instance registry " +
38
+ "is not empty!" unless instances.empty?
39
+ modul.extend ::NameMagic::Namespace
40
+ define_singleton_method :namespace do modul end
53
41
  end
54
42
 
55
- # Returns the instance identified by the first argument, which can typically
56
- # be a name (string/symbol). If a registered instance is supplied, it is
57
- # returned without change. The second argument is optional, with the same
58
- # meaning as in +NameMagic::ClassMethods#instances+ method.
43
+ # Sets the namespace for the class to self.
59
44
  #
60
- def instance instance, option=true
61
- return super if namespace == self
62
- return namespace.instance( instance ) unless option
63
- namespace.instance( instance ).tap { |instance|
64
- fail NameError, "No #{self} instance #{instance} registered in " +
65
- "#{namespace}!" unless instance.kind_of? self
66
- }
45
+ def namespace!
46
+ nil.tap { self.namespace = self }
67
47
  end
68
48
 
69
- # Searches all the modules in the the object space for constants pointing to
70
- # anonymous instances of this class, and names them accordingly. The number
71
- # of remaining anonymous instances is returned.
49
+ # Returns the registered instances. Example:
50
+ #
51
+ # <code>
52
+ # class Animal; include NameMagic end
53
+ # Cat, Dog = Class.new( Animal ), Class.new( Animal )
54
+ # Spot, Livia = Dog.new, Cat.new
55
+ # Animal.instances #=> [Spot, Livia]
56
+ # Dog.instances #=> [Spot]
57
+ # Cat.instances #=> [Livia]
58
+ # </code>
72
59
  #
73
- def const_magic
74
- namespace == self ? super : namespace.const_magic
60
+ def instances
61
+ return super if namespace == self
62
+ namespace.instances.select { |i| i.kind_of? self }
75
63
  end
76
64
 
77
- # Returns the nameless instances. The optional argument has the same meaning
78
- # as in +NameMagic::ClassMethods#instances+ method.
65
+ # Returns the instance identified by the first argument.
79
66
  #
80
- def nameless_instances option=true
67
+ def instance instance
81
68
  return super if namespace == self
82
- return namespace.nameless_instances unless option
83
- namespace.nameless_instances.select { |i| i.kind_of? self }
69
+ namespace.instance( instance ).tap do |i|
70
+ fail NameError, "No #{self} instance #{instance} " +
71
+ "registered in #{namespace}!" unless i.kind_of? self
72
+ end
84
73
  end
85
74
 
86
- # Clears namespace-owned references to a specified instance. (This is
87
- # different from de-naming an instance by setting <code>inst.name =
88
- # nil</code>, which makes the instance anonymous, but still registered.)
75
+ # Returns those of the registered instances, which are nameless.
89
76
  #
90
- def forget instance, option=true
91
- namespace == self || ! option ? super : namespace.forget( instance instance )
77
+ def nameless_instances
78
+ return super if namespace == self
79
+ __instances__
80
+ .select { |key, val| val.nil? and key.is_a? self }
81
+ .keys
92
82
  end
93
83
 
94
- # Clears namespace-owned references to an instance, without performing
95
- # #const_magic first. The argument should be a registered instance. Returns
96
- # the instance's name for named instances, _nil_ for anonymous instances,
97
- # and _false_ if there was no such registered instance. The optional second
98
- # argument has the same meaning as in +NameMaic::ClassMethods#instances+.
84
+ # Clears namespace-owned references to the specified
85
+ # instance. (This is different from de-naming an instance by
86
+ # setting <code>inst.name = nil</code>, which makes the instance
87
+ # anonymous, but still registered.)
99
88
  #
100
- def __forget__( instance, option=true )
89
+ def forget instance
101
90
  return super if namespace == self
102
- fail NameError, "Supplied argument not an instance of #{self}!" unless
103
- instance.is_a? self if option
104
- namespace.__forget__ instance
91
+ namespace.forget( instance instance )
105
92
  end
106
93
 
107
- # Clears namespace-owned references to all the anonymous instances.
94
+ # De-registers an instance without performing #const_magic
95
+ # first. The argument must be a registered instance, or TypeError
96
+ # ensues. Returns instance name for forgotten named instances,
97
+ # _nil_ for forgotten nameless instances.
108
98
  #
109
- def forget_nameless_instances
110
- namespace == self ? super : namespace.forget_nameless_instances
99
+ def __forget__ instance
100
+ fail TypeError, "Supplied argument is not an instance " +
101
+ "of #{self}!" unless instance.is_a? self
102
+ return super if namespace == self
103
+ namespace.__forget__ instance
111
104
  end
112
105
 
113
- # Clears namespace-owned references to all the instances.
106
+ # Clears references to all the instances.
114
107
  #
115
108
  def forget_all_instances
116
- namespace == self ? super : namespace.forget_all_instances
109
+ return super if namespace == self
110
+ instances.map { |instance| __forget__ instance }
117
111
  end
118
112
 
119
- # Registers a hook to execute upon instantiation. Expects a unary block, whose
120
- # argument represents the new instance. It is called right after instantiation,
121
- # but before naming the instance.
113
+ # In addition to the ability to name objects by constant
114
+ # assignment it provides, +NameMagic+ modifies #new method so
115
+ # that it will swallow certain parameters, namely +:name+ (alias
116
+ # +:ɴ+), +:name!+ and +:avid+. These can be used to name
117
+ # instances right off the bat with #new constructor:
118
+ #
119
+ # Human = Class.new do include NameMagic end
120
+ # Human.new name: "Fred"
121
+ # Human.instances #=> [Fred]
122
122
  #
123
- def new_instance_hook &block
124
- namespace == self ? super : namespace.new_instance_hook( &block )
125
- end
126
-
127
- # Registers a hook to execute upon instance naming. Expects a ternary block,
128
- # with arguments instance, name, old_name, representing respectively the
129
- # instance to be named, the requested name, and the previous name of that
130
- # instance (if any). The output of the block should be the name to actually
131
- # be used. In other words, the hook can be used (among other things) to check
132
- # and/or modify the requested name when christening the instance. It is the
133
- # responsibility of this block to output a symbol that can be used as a Ruby
134
- # constant name.
123
+ # Option +:avid+ (_true_ or _false_), when set to _true_, makes
124
+ # the instance so eager to be named that it will overwrite
125
+ # (steal) names already given to other instances. This allows us
126
+ # to redefine names to which we have already assigned something
127
+ # else.
128
+ #
129
+ # Finally, parameter +:name!+ acts as +:name+ with +:avid+ set to
130
+ # _true_:
135
131
  #
136
- def name_set_hook &block
137
- namespace == self ? super : namespace.name_set_hook( &block )
138
- end
139
-
140
- # Registers a hook to execute whenever the instance is asked its name. The
141
- # instance names are objects that are kept in a hash referred to by
142
- # +@instances+ variable owned by the namespace. Normally, +NameMagic#name+
143
- # simply returns the name of the instance, as found in the +@instances+ hash.
144
- # When +name_get_hook+ is defined, this name is transformed by it before being
145
- # returned.
132
+ # instance_1 = Human.new name: "Joe"
133
+ # instance_1.name #=> :Joe
134
+ # Human.instance( :Joe ) == instance_1 #=> true
135
+ # instance_2 = Human.new name!: "Joe"
136
+ # instance_2.name #=> :Joe
137
+ # instance_1.name #=> nil
138
+ # Human.instance( :Joe ) == instance_1 #=> false
139
+ # Human.instance( :Joe ) == instance_2 #=> true
146
140
  #
147
- def name_get_hook &block
148
- namespace == self ? super : namespace.name_get_hook( &block )
141
+ def new *args, &block
142
+ # Extract hash from args.
143
+ oo = if args.last.is_a? Hash then args.pop else {} end
144
+ # Swallow :name / :ɴ parameters.
145
+ requested_name = oo.delete( :name ) || oo.delete( :ɴ )
146
+ # Swallow :name! parameter.
147
+ if oo[ :name! ] then
148
+ fail ArgumentError, "Parameters :name! and :name (:ɴ) " +
149
+ "cannot be supplied both at once!" if requested_name
150
+ requested_name = oo.delete( :name! )
151
+ exclamation_mark = true
152
+ else
153
+ exclamation_mark = false
154
+ end
155
+ # Prepare the arguments for instantiation.
156
+ args << oo unless oo.empty?
157
+ # Instantiate.
158
+ instance = super *args, &block
159
+ # Instantiation contract specifies that instances are created
160
+ # unnamed. Thus, register the instance and set its name to nil.
161
+ __instances__.update( instance => nil )
162
+ # Instantiation contract specifies that instances are created
163
+ # avid. Thus, make the instance avid.
164
+ instance.send :make_avid!
165
+ # Now, we have prepared the new instance nameless and avid
166
+ # exactly according to the instantiation contract. Depending on
167
+ # the arguments supplied to this method, the instance may soon
168
+ # lose its avid state and get a name, but now, before any of
169
+ # that happens, is the time to honor .instantiation_exec hook.
170
+ honor_instantiation_exec( instance )
171
+ # Return the instance if no name was requested for it.
172
+ return instance unless requested_name
173
+ # Now we know that a name was requested. The necessary action
174
+ # depends on whether :name (:ɴ) or :name! parameter was used.
175
+ # But already now, we know that the instance no longer needs
176
+ # to be avid.
177
+ instance.make_not_avid!
178
+ # Depending on whether :name (:ɴ) or :name! was supplied...
179
+ if exclamation_mark then
180
+ # Name the instance aggresively.
181
+ instance.name! requested_name
182
+ else
183
+ # Name the instance only if the name is not already in use.
184
+ instance.name = requested_name
185
+ end
186
+ # Return the instance.
187
+ return instance
149
188
  end
150
189
 
151
- # Sets the namespace for the class.
152
- #
153
- def namespace= modul
154
- puts "Assigning #{modul} as the namespace of #{self}." if ::NameMagic::DEBUG
155
- modul.extend ::NameMagic::NamespaceMethods
156
- define_singleton_method :namespace do modul end
157
- end
190
+ private
158
191
 
159
- # Sets the namespace for the class to self.
192
+ # Honors class'es hook .instantiation_exec. Takes one argument,
193
+ # the newly constructed instance.
160
194
  #
161
- def namespace!
162
- nil.tap { self.namespace = self }
195
+ def honor_instantiation_exec( instance )
196
+ # Method #instantiation_exec, when called without a block,
197
+ # returns the block defined earlier.
198
+ block = instantiation_exec
199
+ # Block is executed within the context of this class.
200
+ instance_exec instance, &block
201
+ # The method returns nil.
202
+ return nil
163
203
  end
164
204
 
165
- # In addition the ability to name objects by constant assignment, +NameMagic+
166
- # redefines #new method so as to swallow name argument +:name+ (alias :ɴ), and
167
- # naming the constructed instance by it. Also, +:name_avid+ option may be
168
- # supplied, which, if _true_, makes the instance capable of avid naming:
169
- # Overwriting (stealing) a name already given to another instance.
170
- #
171
- def new *args, &block
172
- oo = if args[-1].is_a? Hash then args.pop else {} end # extract hash
173
- nm = oo.delete( :name ) || oo.delete( :ɴ ) # consume :name / :ɴ if given
174
- avid = oo.delete( :name_avid )
175
- # Avoid overwriting existing names unless avid:
176
- fail NameError, "#{self} instance #{nm} already exists!" if
177
- __instances__.keys.include? nm unless avid
178
- args << oo unless oo.empty? # prepare the arguments
179
- super( *args, &block ).tap do |inst| # instantiate
180
- __instances__.update( inst => nil ) # Instances are created unnamed...
181
- namespace.new_instance_hook.tap { |λ|
182
- λ.( inst ) if λ
183
- if nm then # Name supplied, name the instance.
184
- avid ? inst.name!( nm ) : inst.name = nm
185
- else # Name not given, make the inst. avid unless expressly prohibited.
186
- __avid_instances__ << inst unless avid == false
187
- end
188
- }
189
- end
190
- end
205
+ # Backup of the #new method internals.
191
206
 
192
- # Calls #new in _avid_ _mode_ (<tt>name_avid: true</tt>); see #new method for
193
- # avid mode explanation.
194
- #
195
- def avid *ordered_args, **named_args, &block
196
- new *ordered_args, **named_args.update( name_avid: true ), &block
197
- end
207
+ # # Extract hash from args.
208
+ # oo = if args.last.is_a? Hash then args.pop else {} end
209
+ # # Swallow :name / :ɴ parameters.
210
+ # requested_name = oo.delete( :name ) || oo.delete( :ɴ )
211
+ # # Swallow :name! parameter.
212
+ # if oo[ :name! ] then
213
+ # fail ArgumentError, "Parameters :name! and :name (:ɴ) " +
214
+ # "cannot be supplied both at once!" if requested_name
215
+ # fail ArgumentError, "When parameter :name! is used, :avid " +
216
+ # "must not be used!" if oo.keys.include? :avid
217
+ # requested_name = oo.delete( :name! )
218
+ # avid = true
219
+ # else
220
+ # # Swallow :avid parameter.
221
+ # avid = oo.delete( :avid )
222
+ # end
223
+ # # I think this is a program error. The construct below never
224
+ # # executes, since in the first place it searches names in the
225
+ # # array of instances.
226
+ # #
227
+ # # # Avoid overwriting existing names unless avid:
228
+ # # fail NameError, "#{self} instance named #{requested_name} " +
229
+ # # "already exists!" if __instances__.keys.include? nm unless avid
230
+ # # Prepare the arguments for instantiation.
231
+ # args << oo unless oo.empty?
232
+ # # Instantiate.
233
+ # new_instance = super *args, &block
234
+ # # Instance construction contract specifies that the instances
235
+ # # are created unnamed. Thus, enter the instance into the
236
+ # # registry and set its name to nil.
237
+ # __instances__.update( instance => nil )
238
+ # # Instance construction contract specifies that the instances
239
+ # # are created avid Make the instance avid.
240
+
241
+ # # Honor the #instantiation_exec hook.
242
+ # honor_instantiation_exec( instance )
198
243
 
199
- # Performs general name validation.
200
- #
201
- def validate_name name
202
- namespace == self ? super : namespace.validate_name( name )
203
- end
204
244
 
205
- private
245
+ # # Name the instance if name has been given.
246
+ # if nm then
247
+ # # If name has been supplied, name the instance.
248
+ # avid ? instance.name!( nm ) : instance.name = nm
249
+ # else # Name has not been given.
250
+ # # Make the instance avid unless expressly prohibited.
251
+ # __avid_instances__ << instance unless avid == false
252
+ # end
206
253
 
207
- # Checks all the constants in some module's namespace, recursively.
208
- #
209
- def serve_all_modules
210
- namespace == self ? super : namespace.serve_all_modules
211
- end
254
+ # # Return the constructed instance.
255
+ # return new_instance
212
256
  end # module NameMagic::ClassMethods
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ module NameMagic::HashMethods
4
+ # Maps the hash into one whose keys have been replaced with full
5
+ # names of the keys (using #full_name method).
6
+ #
7
+ def keys_to_names # FIXME: Change to #keys_to_full_names
8
+ with_keys do |key| key.name || key end
9
+ # FIXME: Change #name to #full_name
10
+ end
11
+
12
+ # Modifies a hash in place so that the keys are replaced with key
13
+ # names (key objects are assumed to respond to +#name+ method).
14
+ #
15
+ def keys_to_names! # FIXME: Change to #keys_to_full_names!
16
+ with_keys! do |key| key.name || key end
17
+ # FIXME: Change #name to #full_name
18
+ end
19
+
20
+ # Maps the hash into one whose keys have been replaced with
21
+ # names of the key objects (using #_name_ method).
22
+ #
23
+ def keys_to_ɴ
24
+ with_keys do |key| key._name_ || key end
25
+ end
26
+
27
+ # Modifies a hash in place so that the keys are replaced with key
28
+ # names (using +#_name_+ method).
29
+ #
30
+ def keys_to_ɴ!
31
+ with_keys! do |key| key._name_ || key end
32
+ end
33
+ end