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.
- 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
@@ -3,210 +3,254 @@
|
|
3
3
|
# Class methods for the classes that include NameMagic.
|
4
4
|
#
|
5
5
|
module NameMagic::ClassMethods
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
#
|
39
|
-
|
40
|
-
|
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
|
-
#
|
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
|
52
|
-
|
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
|
-
#
|
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
|
61
|
-
|
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
|
-
#
|
70
|
-
#
|
71
|
-
#
|
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
|
74
|
-
namespace == self
|
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
|
78
|
-
# as in +NameMagic::ClassMethods#instances+ method.
|
65
|
+
# Returns the instance identified by the first argument.
|
79
66
|
#
|
80
|
-
def
|
67
|
+
def instance instance
|
81
68
|
return super if namespace == self
|
82
|
-
|
83
|
-
|
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
|
-
#
|
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
|
91
|
-
|
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
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
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
|
89
|
+
def forget instance
|
101
90
|
return super if namespace == self
|
102
|
-
|
103
|
-
instance.is_a? self if option
|
104
|
-
namespace.__forget__ instance
|
91
|
+
namespace.forget( instance instance )
|
105
92
|
end
|
106
93
|
|
107
|
-
#
|
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
|
110
|
-
|
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
|
106
|
+
# Clears references to all the instances.
|
114
107
|
#
|
115
108
|
def forget_all_instances
|
116
|
-
namespace == self
|
109
|
+
return super if namespace == self
|
110
|
+
instances.map { |instance| __forget__ instance }
|
117
111
|
end
|
118
112
|
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
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
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
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
|
148
|
-
|
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
|
-
|
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
|
-
#
|
192
|
+
# Honors class'es hook .instantiation_exec. Takes one argument,
|
193
|
+
# the newly constructed instance.
|
160
194
|
#
|
161
|
-
def
|
162
|
-
|
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
|
-
#
|
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
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
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
|
-
|
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
|
-
|
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
|