true-web 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,454 @@
1
+ =begin
2
+
3
+ ==class SuperHash
4
+
5
+ The Ruby inheritance system is a powerful way to organize methods and constants
6
+ in a hierarchy of classes and modules. However, it does not provide an easy way
7
+ to organize class attributes with inherited values in such a hierarchy. There is no inheritance mechanism that combines:
8
+
9
+ 1. propagation of values to descendant classes;
10
+
11
+ 2. overriding of values by a subclass; and
12
+
13
+ 3. mutability.
14
+
15
+ The closest approximations in Ruby are class variables, class instance variables, and constants.
16
+
17
+ A class variable ((({@@var}))) is stored in the base class in which it was
18
+ defined. When its value is changed by a subclass, the change propagates to all
19
+ subclasses of the base class. The value cannot be overridden just for that
20
+ subclass and its descendants. This satisfies 1 and 3, but not 2.
21
+
22
+ A class instance variable ((({@var}))) can take on a different value in each
23
+ subclass, but there is no inheritance mechanism. Its value is privately
24
+ accessible by its owner (though it may be exposed by methods). However, the value does not propagate to subclasses. This satisfies 2 and 3, but not 1.
25
+
26
+ A constant is inherited and can take on different values in subclasses. However it cannot be changed and is always public. This satisfies 1 and 2, but not 3.
27
+
28
+ (({SuperHash})) solves this class attribute problem and in addition is a
29
+ general mechanism for defining attribute inheritance structures among objects
30
+ of any type, not just classes. An example of the former is (({StateObject})),
31
+ in (({examples/state-object.rb})). An example of the latter is
32
+ (({AttributedNode})), in (({examples/attributed-node.rb})).
33
+
34
+ A superhash is simply a hash bundled with a list of parents, which can be
35
+ hashes or other hash-like objects. For all lookup methods, like (({[]})),
36
+ (({each})), (({size})), and so on, the superhash behaves as if the parent hash
37
+ entries were included in it. The inheritance search is depth-first, and in the
38
+ same order as the parents list.
39
+
40
+ Destructive methods, such as (({[]=})) and (({delete})), do not affect the
41
+ parent (however, see (({rehash})) below), but attempt to emulate the expected
42
+ effect by changing the superhash itself. Operations on a parent are immdiately
43
+ reflected in the child; the parent's data is referenced, not copied, by the
44
+ child.
45
+
46
+ The equality semantics of (({SuperHash})) is the same as that of (({Hash})).
47
+ The (({==})) method returns true if and only if the receiver and the argument
48
+ have the same (in the sense of (({==}))) key-value pairs. The (({eql?}))
49
+ method is inherited from (({Object})). Naturally, (({SuperHash})) includes the
50
+ (({Enumerable})) module.
51
+
52
+ Note that (({SuperHash})) is not very efficient. Because (({SuperHash})) is
53
+ dynamic and flexible, even an operation as simple as (({size})) requires
54
+ sending (({size})) messages to the parents. Also, the current implementation
55
+ emphasizes simplicity over speed. For instance, (({each})) requires
56
+ constructing the set of all keys, which requires collecting key sets for
57
+ parents, and then taking their union.
58
+
59
+ ===class method
60
+
61
+ ---SuperHash.new parents = [], default = nil
62
+
63
+ The (({parents})) argument can be an enumerable collection of hash-like
64
+ objects, or a single hash-like object, or [] or nil. The hash-like objects must
65
+ support (({find})), (({collect})), (({keys})), (({key?})), and (({[]})).
66
+
67
+ The precedence order of parents is the same as their order in the (({parents}))
68
+ array. In other words, the first parent in the list overrides later ones, and
69
+ so on. Inheritance is by depth first.
70
+
71
+ If the (({default})) argument is specified, it affects the (({SuperHash})) just
72
+ like the (({default})) argument in the (({Hash})) constructor. The default
73
+ behavior of the child replaces the default behaviors of the parents.
74
+
75
+ ===overridden instance methods
76
+
77
+ The SuperHash instance methods provide a hash-like interface. Hash methods which
78
+ need special explanation are documented below.
79
+
80
+ ---SuperHash#clear
81
+
82
+ The implementation of (({clear})) is to simply call (({delete_if {true}})).
83
+
84
+ ---SuperHash#delete(key)
85
+ ---SuperHash#delete(key) { |key| block }
86
+ ---SuperHash#delete_if { |key, value| block }
87
+
88
+ If the key is inherited, these methods simply associate the default value to
89
+ the key in the (({SuperHash})). Note that if the default is changed after the
90
+ deletion, the key-value pair is not updated to reflect the change--the value
91
+ will still be the old default.
92
+
93
+ ---SuperHash#empty?
94
+ ---SuperHash#size
95
+
96
+ Note that (({superhash.clear.empty?})) will not return (({true})) if there are
97
+ inherited keys. The (({SuperHash})) needs to remember which parent keys have
98
+ been deleted, and this is not easily distinguishable from the case in which
99
+ those keys have been explicitly associated with (({nil})) (or the default
100
+ value). Similar remarks apply to (({size})).
101
+
102
+ ---SuperHash#invert
103
+ ---SuperHash#to_hash
104
+
105
+ Returns a (({Hash})), in the first case with inverted key-value pairs, in the
106
+ second case with the same key-value pairs, as the receiver.
107
+
108
+ ---SuperHash#rehash
109
+
110
+ Rehashes the receiver's (({own})) hash and rehashes all parents (if they
111
+ respond to (({rehash}))). Note that this is the only (({SuperHash})) method
112
+ that modifies the parent objects.
113
+
114
+ ---SuperHash#replace(hash)
115
+
116
+ Replaces the receiver's (({own})) hash with the argument, and replaces the
117
+ receiver's parent array with the empty array.
118
+
119
+ ---SuperHash#shift
120
+
121
+ As long as the (({own})) hash has entries, shifts them out and returns them.
122
+ Raises (({ParentImmutableError})) if the receiver's (({own})) hash is empty.
123
+
124
+ ===new instance methods
125
+
126
+ (({SuperHash})) defines some instance methods that are not available in
127
+ (({Hash})).
128
+
129
+ ---SuperHash#inherits_key? k
130
+
131
+ Returns (({true})) if and only if (({k})) is a key in a parent but not in the
132
+ receiver's (({own})) hash.
133
+
134
+ ---SuperHash#own
135
+
136
+ Returns the hash of key-value pairs that belong to the superhash and are not
137
+ inherited.
138
+
139
+ ---SuperHash#own_keys
140
+
141
+ Returns the array of keys in the (({own})) hash.
142
+
143
+ ---SuperHash#owns_key? k
144
+
145
+ Returns (({true})) if and only if (({k})) is a key in the (({own})) hash.
146
+
147
+ ==version
148
+
149
+ SuperHash 0.3
150
+
151
+ The current version of this software can be found at
152
+ ((<"http://redshift.sourceforge.net/superhash
153
+ "|URL:http://redshift.sourceforge.net/superhash>)).
154
+
155
+ ==license
156
+ This software is distributed under the Ruby license.
157
+ See ((<"http://www.ruby-lang.org"|URL:http://www.ruby-lang.org>)).
158
+
159
+ ==author
160
+ Joel VanderWerf,
161
+ ((<vjoel@users.sourceforge.net|URL:mailto:vjoel@users.sourceforge.net>))
162
+
163
+ =end
164
+
165
+ class SuperHash
166
+ include Enumerable
167
+
168
+ attr_reader :parents
169
+
170
+ def initialize parents = [], default = nil
171
+ @hash = Hash.new default
172
+ if parents == nil
173
+ @parents = []
174
+ elsif parents.respond_to? :key?
175
+ @parents = [parents]
176
+ else
177
+ @parents = parents
178
+ end
179
+ end
180
+
181
+ # methods that are not overrides of Hash methods
182
+
183
+ def inherits_key? k
184
+ !(@hash.key? k) && (!! @parents.find {|parent| parent.key? k } )
185
+ end
186
+
187
+ def own
188
+ @hash
189
+ end
190
+
191
+ def own_keys
192
+ @hash.keys
193
+ end
194
+
195
+ def owns_key? k
196
+ @hash.key? k
197
+ end
198
+
199
+ # methods that override Hash methods
200
+
201
+ def ==(other)
202
+ return false unless other.respond_to? :size and
203
+ size == other.size and
204
+ other.respond_to? :[]
205
+ each { |key, value| return false unless self[key] == other[key] }
206
+ return true
207
+ end
208
+
209
+ def [](key)
210
+ fetch(key) {default}
211
+ end
212
+
213
+ def []=(key, value)
214
+ @hash[key] = value
215
+ end
216
+ alias store []=
217
+
218
+ def clear
219
+ delete_if {true}
220
+ end
221
+
222
+ def default
223
+ @hash.default
224
+ end
225
+
226
+ def default=(value)
227
+ @hash.default = value
228
+ end
229
+
230
+ def delete(key)
231
+ if key? key
232
+ @hash.delete(key) do
233
+ value = fetch(key)
234
+ @hash[key] = default
235
+ value
236
+ end
237
+ else
238
+ block_given? ? (yield key) : default
239
+ end
240
+ end
241
+
242
+ def delete_if
243
+ each do |key, value|
244
+ if yield key, value
245
+ @hash.delete(key) { @hash[key] = default }
246
+ end
247
+ end
248
+ end
249
+
250
+ def each
251
+ keys.each { |k| yield k, fetch(k) }
252
+ self
253
+ end
254
+ alias each_pair each
255
+
256
+ def each_key
257
+ keys.each { |k| yield k }
258
+ self
259
+ end
260
+
261
+ def each_value
262
+ keys.each { |k| yield fetch(k) }
263
+ self
264
+ end
265
+
266
+ def empty?
267
+ @hash.empty? && ( not @parents.find {|parent| not parent.empty?} )
268
+ end
269
+
270
+ def fetch(*args)
271
+ case args.size
272
+ when 1
273
+ key, = args
274
+ @hash.fetch(key) {
275
+ @parents.each do |parent|
276
+ begin
277
+ return parent.fetch(key)
278
+ rescue IndexError
279
+ end
280
+ end
281
+ if block_given?
282
+ yield key
283
+ else
284
+ raise IndexError, "key not found"
285
+ end
286
+ }
287
+ when 2
288
+ if block_given?
289
+ raise ArgumentError, "wrong # of arguments"
290
+ end
291
+ key, default_object = args
292
+ @hash.fetch(key) {
293
+ @parents.each do |parent|
294
+ begin
295
+ return parent.fetch(key)
296
+ rescue IndexError
297
+ end
298
+ end
299
+ return default_object
300
+ }
301
+ else
302
+ raise ArgumentError, "wrong # of arguments(#{args.size} for 2)"
303
+ end
304
+ end
305
+
306
+ def has_value? val
307
+ each { |k,v| return true if val == v }
308
+ return false
309
+ end
310
+ alias value? has_value?
311
+
312
+ def index val
313
+ each { |k,v| return k if val == v }
314
+ return false
315
+ end
316
+
317
+ def indexes(*ks)
318
+ ks.collect { |k| index k }
319
+ end
320
+ alias indices indexes
321
+
322
+ def invert
323
+ h = {}
324
+ keys.each { |k| h[fetch(k)] = k }
325
+ h
326
+ end
327
+
328
+ def key? k
329
+ (@hash.key? k) || (!! @parents.find {|parent| parent.key?(k)} )
330
+ end
331
+ alias has_key? key?
332
+ alias include? key?
333
+ alias member? key?
334
+
335
+ def keys
336
+ (@hash.keys + (@parents.collect { |parent| parent.keys }).flatten).uniq
337
+ end
338
+
339
+ def rehash
340
+ @hash.rehash
341
+ @parents.each { |parent| parent.rehash if parent.respond_to? :rehash }
342
+ self
343
+ end
344
+
345
+ def reject
346
+ dup.delete_if { |k, v| yield k, v } ## or is '&Proc.new' faster?
347
+ end
348
+
349
+ def reject!
350
+ changed = false
351
+
352
+ each do |key, value|
353
+ if yield key, value
354
+ changed = true
355
+ @hash.delete(key) { @hash[key] = default }
356
+ end
357
+ end
358
+
359
+ changed ? self : nil
360
+ end
361
+
362
+ def replace hash
363
+ @hash.replace hash
364
+ @parents.replace []
365
+ end
366
+
367
+ class ParentImmutableError < StandardError; end
368
+
369
+ def shift
370
+ if @hash.empty?
371
+ raise ParentImmutableError, "Attempted to shift data out of parent"
372
+ else
373
+ @hash.shift
374
+ end
375
+ end
376
+
377
+ def size
378
+ keys.size
379
+ end
380
+ alias length size
381
+
382
+ def sort
383
+ if block_given?
384
+ to_a.sort { |x, y| yield x, y } ## or is '&Proc.new' faster?
385
+ else
386
+ to_a.sort
387
+ end
388
+ end
389
+
390
+ def to_a
391
+ to_hash.to_a
392
+ end
393
+
394
+ def to_hash
395
+ h = {}
396
+ keys.each { |k| h[k] = fetch(k) }
397
+ h
398
+ end
399
+
400
+ def to_s
401
+ to_hash.to_s
402
+ end
403
+
404
+ def update h
405
+ @hash.update h
406
+ self
407
+ end
408
+
409
+ def values
410
+ keys.collect { |k| self[k] }
411
+ end
412
+
413
+ end
414
+
415
+ class Class
416
+ private
417
+ def class_superhash(*vars)
418
+ for var in vars
419
+ class_eval %{
420
+ @#{var} = Hash.new
421
+ def self.#{var}
422
+ @#{var} ||= SuperHash.new(superclass.#{var})
423
+ end
424
+ }
425
+ end
426
+ end
427
+
428
+ # A superhash of key-value pairs in which the value is a superhash
429
+ # which inherits from the key-indexed superhash in the superclass.
430
+ def class_superhash2(*vars)
431
+ for var in vars
432
+ class_eval %{
433
+ @#{var} = Hash.new
434
+ def self.#{var}(arg = nil)
435
+ @#{var} ||= SuperHash.new(superclass.#{var})
436
+ if arg
437
+ if self == #{self.name}
438
+ unless @#{var}.has_key? arg
439
+ @#{var}[arg] = Hash.new
440
+ end
441
+ else
442
+ unless @#{var}.owns_key? arg
443
+ @#{var}[arg] = SuperHash.new(superclass.#{var}(arg))
444
+ end
445
+ end
446
+ @#{var}[arg]
447
+ else
448
+ @#{var}
449
+ end
450
+ end
451
+ }
452
+ end
453
+ end
454
+ end