y_support 2.1.18 → 2.4.4

Sign up to get free protection for your applications and to get access to all the features.
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