true-web 0.1.0

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.
@@ -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