true-web 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +99 -0
- data/README +0 -0
- data/Rakefile +2 -0
- data/lib/true-web.rb +131 -0
- data/lib/true-web/controller.rb +123 -0
- data/lib/true-web/env_methods.rb +13 -0
- data/lib/true-web/plugins.rb +29 -0
- data/lib/true-web/plugins/directory_first_sort.rb +14 -0
- data/lib/true-web/service.rb +176 -0
- data/lib/true-web/version.rb +3 -0
- data/lib/true-web/views.rb +85 -0
- data/spec/fixture-app/app.rb +26 -0
- data/spec/fixture-app/services/authentication/app/templates/index.html.ms +1 -0
- data/spec/fixture-app/services/authentication/init.rb +1 -0
- data/spec/fixture-app/services/authentication/public/javascripts/foo.js +2 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/spec_suite.rb +3 -0
- data/spec/true-web/service_spec.rb +35 -0
- data/spec/true-web/views_spec.rb +36 -0
- data/true-web.gemspec +39 -0
- data/vendor/superhash/InstalledFiles +1 -0
- data/vendor/superhash/README +5 -0
- data/vendor/superhash/RELEASE-NOTES +9 -0
- data/vendor/superhash/config.save +12 -0
- data/vendor/superhash/examples/attributed-node.rb +83 -0
- data/vendor/superhash/examples/class-superhash.rb +26 -0
- data/vendor/superhash/examples/state-object.rb +95 -0
- data/vendor/superhash/install.rb +1015 -0
- data/vendor/superhash/lib/superhash.rb +454 -0
- data/vendor/superhash/test/test.rb +124 -0
- metadata +242 -0
@@ -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
|