arrow 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +1590 -0
- data/LICENSE +28 -0
- data/README +75 -0
- data/Rakefile +366 -0
- data/Rakefile.local +63 -0
- data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
- data/data/arrow/applets/args.rb +50 -0
- data/data/arrow/applets/config.rb +55 -0
- data/data/arrow/applets/error.rb +63 -0
- data/data/arrow/applets/files.rb +46 -0
- data/data/arrow/applets/inspect.rb +46 -0
- data/data/arrow/applets/nosuchapplet.rb +31 -0
- data/data/arrow/applets/status.rb +92 -0
- data/data/arrow/applets/test.rb +133 -0
- data/data/arrow/applets/tutorial/counter.rb +96 -0
- data/data/arrow/applets/tutorial/dingus.rb +67 -0
- data/data/arrow/applets/tutorial/hello.rb +34 -0
- data/data/arrow/applets/tutorial/hello2.rb +73 -0
- data/data/arrow/applets/tutorial/imgtext.rb +90 -0
- data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
- data/data/arrow/applets/tutorial/index.rb +36 -0
- data/data/arrow/applets/tutorial/logo.rb +98 -0
- data/data/arrow/applets/tutorial/memcache.rb +61 -0
- data/data/arrow/applets/tutorial/missing.rb +37 -0
- data/data/arrow/applets/tutorial/protected.rb +100 -0
- data/data/arrow/applets/tutorial/redirector.rb +52 -0
- data/data/arrow/applets/tutorial/rndimages.rb +159 -0
- data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
- data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
- data/data/arrow/applets/tutorial/superhello.rb +72 -0
- data/data/arrow/applets/tutorial/timeclock.rb +78 -0
- data/data/arrow/applets/view-applet.rb +123 -0
- data/data/arrow/applets/view-template.rb +85 -0
- data/data/arrow/applets/wiki.rb +274 -0
- data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
- data/data/arrow/templates/applet-status.tmpl +153 -0
- data/data/arrow/templates/args-display.tmpl +120 -0
- data/data/arrow/templates/config/display-table.tmpl +36 -0
- data/data/arrow/templates/config/display.tmpl +36 -0
- data/data/arrow/templates/counter-deleted.tmpl +33 -0
- data/data/arrow/templates/counter.tmpl +59 -0
- data/data/arrow/templates/dingus.tmpl +55 -0
- data/data/arrow/templates/enumtable.tmpl +8 -0
- data/data/arrow/templates/error-display.tmpl +92 -0
- data/data/arrow/templates/filemap.tmpl +89 -0
- data/data/arrow/templates/hello-world-src.tmpl +34 -0
- data/data/arrow/templates/hello-world.tmpl +60 -0
- data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
- data/data/arrow/templates/imgtext/form.tmpl +70 -0
- data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
- data/data/arrow/templates/imgtext/reload.tmpl +55 -0
- data/data/arrow/templates/inspect/display.tmpl +80 -0
- data/data/arrow/templates/loginform.tmpl +64 -0
- data/data/arrow/templates/logout.tmpl +32 -0
- data/data/arrow/templates/memcache/display.tmpl +41 -0
- data/data/arrow/templates/navbar.incl +27 -0
- data/data/arrow/templates/nosuchapplet.tmpl +32 -0
- data/data/arrow/templates/printsource.tmpl +35 -0
- data/data/arrow/templates/protected.tmpl +36 -0
- data/data/arrow/templates/rndimages.tmpl +38 -0
- data/data/arrow/templates/service-response.tmpl +13 -0
- data/data/arrow/templates/sharenotes/display.tmpl +38 -0
- data/data/arrow/templates/status.tmpl +120 -0
- data/data/arrow/templates/templateviewer.tmpl +43 -0
- data/data/arrow/templates/test/harness.tmpl +57 -0
- data/data/arrow/templates/test/list.tmpl +48 -0
- data/data/arrow/templates/test/problem.tmpl +42 -0
- data/data/arrow/templates/tutorial/index.tmpl +37 -0
- data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
- data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
- data/data/arrow/templates/view-applet.tmpl +40 -0
- data/data/arrow/templates/view-template.tmpl +83 -0
- data/data/arrow/templates/wiki/formerror.tmpl +47 -0
- data/data/arrow/templates/wiki/markup_help.incl +6 -0
- data/data/arrow/templates/wiki/new.tmpl +56 -0
- data/data/arrow/templates/wiki/new_system.tmpl +122 -0
- data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
- data/data/arrow/templates/wiki/show.tmpl +34 -0
- data/docs/manual/layouts/default.page +43 -0
- data/docs/manual/lib/api-filter.rb +81 -0
- data/docs/manual/lib/editorial-filter.rb +64 -0
- data/docs/manual/lib/examples-filter.rb +244 -0
- data/docs/manual/lib/links-filter.rb +117 -0
- data/lib/apache/fakerequest.rb +448 -0
- data/lib/apache/logger.rb +33 -0
- data/lib/arrow.rb +51 -0
- data/lib/arrow/acceptparam.rb +207 -0
- data/lib/arrow/applet.rb +725 -0
- data/lib/arrow/appletmixins.rb +218 -0
- data/lib/arrow/appletregistry.rb +590 -0
- data/lib/arrow/applettestcase.rb +503 -0
- data/lib/arrow/broker.rb +255 -0
- data/lib/arrow/cache.rb +176 -0
- data/lib/arrow/config-loaders/yaml.rb +75 -0
- data/lib/arrow/config.rb +615 -0
- data/lib/arrow/constants.rb +24 -0
- data/lib/arrow/cookie.rb +359 -0
- data/lib/arrow/cookieset.rb +108 -0
- data/lib/arrow/dispatcher.rb +368 -0
- data/lib/arrow/dispatcherloader.rb +50 -0
- data/lib/arrow/exceptions.rb +61 -0
- data/lib/arrow/fallbackhandler.rb +48 -0
- data/lib/arrow/formvalidator.rb +631 -0
- data/lib/arrow/htmltokenizer.rb +343 -0
- data/lib/arrow/logger.rb +488 -0
- data/lib/arrow/logger/apacheoutputter.rb +69 -0
- data/lib/arrow/logger/arrayoutputter.rb +63 -0
- data/lib/arrow/logger/coloroutputter.rb +111 -0
- data/lib/arrow/logger/fileoutputter.rb +96 -0
- data/lib/arrow/logger/htmloutputter.rb +54 -0
- data/lib/arrow/logger/outputter.rb +123 -0
- data/lib/arrow/mixins.rb +425 -0
- data/lib/arrow/monkeypatches.rb +94 -0
- data/lib/arrow/object.rb +117 -0
- data/lib/arrow/path.rb +196 -0
- data/lib/arrow/service.rb +447 -0
- data/lib/arrow/session.rb +289 -0
- data/lib/arrow/session/dbstore.rb +100 -0
- data/lib/arrow/session/filelock.rb +160 -0
- data/lib/arrow/session/filestore.rb +132 -0
- data/lib/arrow/session/id.rb +98 -0
- data/lib/arrow/session/lock.rb +253 -0
- data/lib/arrow/session/md5id.rb +42 -0
- data/lib/arrow/session/nulllock.rb +42 -0
- data/lib/arrow/session/posixlock.rb +166 -0
- data/lib/arrow/session/sha1id.rb +54 -0
- data/lib/arrow/session/store.rb +366 -0
- data/lib/arrow/session/usertrackid.rb +52 -0
- data/lib/arrow/spechelpers.rb +73 -0
- data/lib/arrow/template.rb +713 -0
- data/lib/arrow/template/attr.rb +31 -0
- data/lib/arrow/template/call.rb +31 -0
- data/lib/arrow/template/comment.rb +33 -0
- data/lib/arrow/template/container.rb +118 -0
- data/lib/arrow/template/else.rb +41 -0
- data/lib/arrow/template/elsif.rb +44 -0
- data/lib/arrow/template/escape.rb +53 -0
- data/lib/arrow/template/export.rb +87 -0
- data/lib/arrow/template/for.rb +145 -0
- data/lib/arrow/template/if.rb +78 -0
- data/lib/arrow/template/import.rb +119 -0
- data/lib/arrow/template/include.rb +206 -0
- data/lib/arrow/template/iterator.rb +208 -0
- data/lib/arrow/template/nodes.rb +734 -0
- data/lib/arrow/template/parser.rb +571 -0
- data/lib/arrow/template/prettyprint.rb +53 -0
- data/lib/arrow/template/render.rb +191 -0
- data/lib/arrow/template/selectlist.rb +94 -0
- data/lib/arrow/template/set.rb +87 -0
- data/lib/arrow/template/timedelta.rb +81 -0
- data/lib/arrow/template/unless.rb +78 -0
- data/lib/arrow/template/urlencode.rb +51 -0
- data/lib/arrow/template/yield.rb +139 -0
- data/lib/arrow/templatefactory.rb +125 -0
- data/lib/arrow/testcase.rb +567 -0
- data/lib/arrow/transaction.rb +608 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/documentation.rb +114 -0
- data/rake/helpers.rb +502 -0
- data/rake/hg.rb +282 -0
- data/rake/manual.rb +787 -0
- data/rake/packaging.rb +129 -0
- data/rake/publishing.rb +278 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/spec/arrow/acceptparam_spec.rb +157 -0
- data/spec/arrow/applet_spec.rb +575 -0
- data/spec/arrow/appletmixins_spec.rb +409 -0
- data/spec/arrow/appletregistry_spec.rb +294 -0
- data/spec/arrow/broker_spec.rb +153 -0
- data/spec/arrow/config_spec.rb +224 -0
- data/spec/arrow/cookieset_spec.rb +164 -0
- data/spec/arrow/dispatcher_spec.rb +137 -0
- data/spec/arrow/dispatcherloader_spec.rb +65 -0
- data/spec/arrow/formvalidator_spec.rb +781 -0
- data/spec/arrow/logger_spec.rb +346 -0
- data/spec/arrow/mixins_spec.rb +120 -0
- data/spec/arrow/service_spec.rb +645 -0
- data/spec/arrow/session_spec.rb +121 -0
- data/spec/arrow/template/iterator_spec.rb +222 -0
- data/spec/arrow/templatefactory_spec.rb +185 -0
- data/spec/arrow/transaction_spec.rb +319 -0
- data/spec/arrow_spec.rb +37 -0
- data/spec/lib/appletmatchers.rb +281 -0
- data/spec/lib/constants.rb +77 -0
- data/spec/lib/helpers.rb +41 -0
- data/spec/lib/matchers.rb +44 -0
- data/tests/cookie.tests.rb +310 -0
- data/tests/path.tests.rb +157 -0
- data/tests/session.tests.rb +111 -0
- data/tests/session_id.tests.rb +82 -0
- data/tests/session_lock.tests.rb +191 -0
- data/tests/session_store.tests.rb +53 -0
- data/tests/template.tests.rb +1360 -0
- metadata +339 -0
data/lib/arrow/mixins.rb
ADDED
@@ -0,0 +1,425 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
module Arrow
|
3
|
+
|
4
|
+
require 'arrow/exceptions'
|
5
|
+
|
6
|
+
### A collection of utilities for working with Hashes.
|
7
|
+
module HashUtilities
|
8
|
+
|
9
|
+
# Recursive hash-merge function
|
10
|
+
HashMergeFunction = Proc.new {|key, oldval, newval|
|
11
|
+
#debugMsg "Merging '%s': %s -> %s" %
|
12
|
+
# [ key.inspect, oldval.inspect, newval.inspect ]
|
13
|
+
case oldval
|
14
|
+
when Hash
|
15
|
+
case newval
|
16
|
+
when Hash
|
17
|
+
#debugMsg "Hash/Hash merge"
|
18
|
+
oldval.merge( newval, &HashMergeFunction )
|
19
|
+
else
|
20
|
+
newval
|
21
|
+
end
|
22
|
+
|
23
|
+
when Array
|
24
|
+
case newval
|
25
|
+
when Array
|
26
|
+
#debugMsg "Array/Array union"
|
27
|
+
oldval | newval
|
28
|
+
else
|
29
|
+
newval
|
30
|
+
end
|
31
|
+
|
32
|
+
when Arrow::Path
|
33
|
+
if newval.is_a?( Arrow::Path )
|
34
|
+
newval
|
35
|
+
else
|
36
|
+
Arrow::Path.new( newval )
|
37
|
+
end
|
38
|
+
|
39
|
+
else
|
40
|
+
newval
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
44
|
+
###############
|
45
|
+
module_function
|
46
|
+
###############
|
47
|
+
|
48
|
+
### Return a version of the given +hash+ with its keys transformed
|
49
|
+
### into Strings from whatever they were before.
|
50
|
+
def stringify_keys( hash )
|
51
|
+
newhash = {}
|
52
|
+
|
53
|
+
hash.each do |key,val|
|
54
|
+
if val.is_a?( Hash )
|
55
|
+
newhash[ key.to_s ] = stringify_keys( val )
|
56
|
+
else
|
57
|
+
newhash[ key.to_s ] = val
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
return newhash
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
### Return a duplicate of the given +hash+ with its identifier-like keys
|
66
|
+
### transformed into symbols from whatever they were before.
|
67
|
+
def symbolify_keys( hash )
|
68
|
+
newhash = {}
|
69
|
+
|
70
|
+
hash.each do |key,val|
|
71
|
+
keysym = key.to_s.dup.untaint.to_sym
|
72
|
+
|
73
|
+
if val.is_a?( Hash )
|
74
|
+
newhash[ keysym ] = symbolify_keys( val )
|
75
|
+
else
|
76
|
+
newhash[ keysym ] = val
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
return newhash
|
81
|
+
end
|
82
|
+
alias_method :internify_keys, :symbolify_keys
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
### A collection of utilities for working with Arrays.
|
88
|
+
module ArrayUtilities
|
89
|
+
|
90
|
+
###############
|
91
|
+
module_function
|
92
|
+
###############
|
93
|
+
|
94
|
+
### Return a version of the given +array+ with any Symbols contained in it turned into
|
95
|
+
### Strings.
|
96
|
+
def stringify_array( array )
|
97
|
+
return array.collect do |item|
|
98
|
+
case item
|
99
|
+
when Symbol
|
100
|
+
item.to_s
|
101
|
+
when Array
|
102
|
+
stringify_array( item )
|
103
|
+
else
|
104
|
+
item
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
### Return a version of the given +array+ with any Strings contained in it turned into
|
111
|
+
### Symbols.
|
112
|
+
def symbolify_array( array )
|
113
|
+
return array.collect do |item|
|
114
|
+
case item
|
115
|
+
when String
|
116
|
+
item.to_sym
|
117
|
+
when Array
|
118
|
+
symbolify_array( item )
|
119
|
+
else
|
120
|
+
item
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
### A collection of HTML utility functions
|
129
|
+
module HTMLUtilities
|
130
|
+
|
131
|
+
###############
|
132
|
+
module_function
|
133
|
+
###############
|
134
|
+
|
135
|
+
# The name of the Thread-local variable to keep the serialized-object
|
136
|
+
# cache in (i.e., Thread[ THREAD_DUMP_KEY ] = {}). The cache is keyed by
|
137
|
+
# object_id
|
138
|
+
THREAD_DUMP_KEY = :__to_html_cache__
|
139
|
+
|
140
|
+
# The HTML fragment to wrap around Hash objects
|
141
|
+
HASH_HTML_CONTAINER = %{<div class="hash-members">%s</div>}
|
142
|
+
|
143
|
+
# The HTML fragment to use for pairs of a Hash
|
144
|
+
HASH_PAIR_HTML = %{<div class="hash-pair %s">\n} +
|
145
|
+
%{<div class="key">%s</div>\n} +
|
146
|
+
%{<div class="value">%s</div>\n} +
|
147
|
+
%{</div>\n}
|
148
|
+
|
149
|
+
# The HTML fragment to wrap around Array objects
|
150
|
+
ARRAY_HTML_CONTAINER = %{<ol class="array-members"><li>%s</li></ol>}
|
151
|
+
|
152
|
+
# The HTML fragment to wrap around immediate objects
|
153
|
+
IMMEDIATE_OBJECT_HTML_CONTAINER = %{<div class="immediate-object">%s</div>}
|
154
|
+
|
155
|
+
# The HTML fragment to wrap around objects other than Arrays and Hashes.
|
156
|
+
OBJECT_HTML_CONTAINER = %{<div id="object-%d" class="object %s">%s</div>}
|
157
|
+
|
158
|
+
# The HTML fragment to use for instance variables inside of object DIVs.
|
159
|
+
IVAR_HTML_FRAGMENT = %Q{
|
160
|
+
<div class="%s">
|
161
|
+
<div class="name">%s</div>
|
162
|
+
<div class="value">%s</div>
|
163
|
+
</div>
|
164
|
+
}
|
165
|
+
|
166
|
+
### Escape special characters in the given +string+ for display in an
|
167
|
+
### HTML inspection interface. This escapes common invisible characters
|
168
|
+
### like tabs and carriage-returns in additional to the regular HTML
|
169
|
+
### escapes.
|
170
|
+
def escape_html( string )
|
171
|
+
return "nil" if string.nil?
|
172
|
+
string = string.inspect unless string.is_a?( String )
|
173
|
+
string.
|
174
|
+
gsub(/&/, '&').
|
175
|
+
gsub(/</, '<').
|
176
|
+
gsub(/>/, '>').
|
177
|
+
gsub(/\n/, '↵').
|
178
|
+
gsub(/\t/, '→')
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
### Return an HTML fragment describing the specified +object+.
|
183
|
+
def make_html_for_object( object )
|
184
|
+
return object.html_inspect if
|
185
|
+
object.respond_to?( :html_inspect ) && ! object.is_a?( HtmlInspectableObject )
|
186
|
+
object_html = []
|
187
|
+
|
188
|
+
case object
|
189
|
+
when Hash
|
190
|
+
object_html << "\n<!-- Hash -->\n"
|
191
|
+
if object.empty?
|
192
|
+
object_html << '{}'
|
193
|
+
else
|
194
|
+
object_html << HASH_HTML_CONTAINER % [
|
195
|
+
object.collect {|k,v|
|
196
|
+
pairclass = v.instance_variables.empty? ?
|
197
|
+
"simple-hash-pair" :
|
198
|
+
"complex-hash-pair"
|
199
|
+
HASH_PAIR_HTML % [
|
200
|
+
pairclass,
|
201
|
+
make_html_for_object(k),
|
202
|
+
make_html_for_object(v),
|
203
|
+
]
|
204
|
+
}
|
205
|
+
]
|
206
|
+
end
|
207
|
+
|
208
|
+
when Array
|
209
|
+
object_html << "\n<!-- Array -->\n"
|
210
|
+
if object.empty?
|
211
|
+
object_html << '[]'
|
212
|
+
else
|
213
|
+
object_html << ARRAY_HTML_CONTAINER % [
|
214
|
+
object.collect {|o| make_html_for_object(o) }.join('</li><li>')
|
215
|
+
]
|
216
|
+
end
|
217
|
+
|
218
|
+
else
|
219
|
+
if object.instance_variables.empty?
|
220
|
+
return IMMEDIATE_OBJECT_HTML_CONTAINER %
|
221
|
+
[ HTMLUtilities.escape_html(object.inspect) ]
|
222
|
+
else
|
223
|
+
object_html << make_object_html_wrapper( object )
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
return object_html.join("\n")
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
### Wrap up the various parts of a complex object in an HTML fragment. If the
|
232
|
+
### object has already been wrapped, returns a link to the previous rendering
|
233
|
+
### instead.
|
234
|
+
def make_object_html_wrapper( object )
|
235
|
+
|
236
|
+
# If the object has been rendered already, just return a link to the previous
|
237
|
+
# HTML fragment
|
238
|
+
Thread.current[ THREAD_DUMP_KEY ] ||= {}
|
239
|
+
if Thread.current[ THREAD_DUMP_KEY ].key?( object.object_id )
|
240
|
+
return %Q{<a href="#object-%d" class="cache-link" title="jump to previous details">%s</a>} % [
|
241
|
+
object.object_id,
|
242
|
+
%{→ %s #%d} % [ object.class.name, object.object_id ]
|
243
|
+
]
|
244
|
+
else
|
245
|
+
Thread.current[ THREAD_DUMP_KEY ][ object.object_id ] = true
|
246
|
+
end
|
247
|
+
|
248
|
+
# Assemble the innards as an array of parts
|
249
|
+
parts = [
|
250
|
+
%{<div class="object-header">},
|
251
|
+
%{<span class="object-class">#{object.class.name}</span>},
|
252
|
+
%{<span class="object-id">##{object.object_id}</span>},
|
253
|
+
%{</div>},
|
254
|
+
%{<div class="object-body">},
|
255
|
+
]
|
256
|
+
|
257
|
+
object.instance_variables.sort.each do |ivar|
|
258
|
+
value = object.instance_variable_get( ivar )
|
259
|
+
html = make_html_for_object( value )
|
260
|
+
classes = %w[instance-variable]
|
261
|
+
if value.instance_variables.empty? && !value.respond_to?( :values_at )
|
262
|
+
classes << 'simple'
|
263
|
+
else
|
264
|
+
classes << 'complex'
|
265
|
+
end
|
266
|
+
parts << IVAR_HTML_FRAGMENT % [ classes.join(' '), ivar, html ]
|
267
|
+
end
|
268
|
+
|
269
|
+
parts << %{</div>}
|
270
|
+
|
271
|
+
# Make HTML class names out of the object's namespaces
|
272
|
+
namespaces = object.class.name.downcase.split(/::/)
|
273
|
+
classes = []
|
274
|
+
namespaces.each_index do |i|
|
275
|
+
classes << namespaces[0..i].join('-') + '-object'
|
276
|
+
end
|
277
|
+
|
278
|
+
# Glue the whole thing together and return it
|
279
|
+
return OBJECT_HTML_CONTAINER % [
|
280
|
+
object.object_id,
|
281
|
+
classes.join(" "),
|
282
|
+
parts.join("\n")
|
283
|
+
]
|
284
|
+
end
|
285
|
+
|
286
|
+
end # module HTMLUtilities
|
287
|
+
|
288
|
+
|
289
|
+
### Add a #html_inspect method to the including object that is capable of dumping its
|
290
|
+
### state as an HTML fragment.
|
291
|
+
###
|
292
|
+
### class MyObject
|
293
|
+
### include HtmlInspectableObject
|
294
|
+
### end
|
295
|
+
###
|
296
|
+
### irb> MyObject.new.html_inspect
|
297
|
+
### ==> "<span class=\"immediate-object\">#<MyObject:0x56e780></span>"
|
298
|
+
module HtmlInspectableObject
|
299
|
+
include Arrow::HTMLUtilities
|
300
|
+
|
301
|
+
### Return the receiver as an HTML fragment.
|
302
|
+
def html_inspect
|
303
|
+
if self.instance_variables.empty?
|
304
|
+
return make_html_for_object( self )
|
305
|
+
else
|
306
|
+
return make_object_html_wrapper( self )
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
end # HtmlInspectableObject
|
311
|
+
|
312
|
+
|
313
|
+
# Adds dependency-injection bejavior to a class. Classes which are Injectable
|
314
|
+
# are loadable by name, making it easier to refer to them from a configuration
|
315
|
+
# file or other symbolic source. Instead of classes explicitly referring to
|
316
|
+
# one another to satisfy their associations, these dependencies can be
|
317
|
+
# "injected" at runtime.
|
318
|
+
#
|
319
|
+
# Some references for the Dependency Injection pattern:
|
320
|
+
#
|
321
|
+
# * http://www.martinfowler.com/articles/injection.html
|
322
|
+
# * http://en.wikipedia.org/wiki/Dependency_injection
|
323
|
+
#
|
324
|
+
# == Usage
|
325
|
+
#
|
326
|
+
# # in myclass.rb
|
327
|
+
# require 'arrow/mixins'
|
328
|
+
#
|
329
|
+
# class MyClass
|
330
|
+
# include Arrow::Injectable
|
331
|
+
# end
|
332
|
+
#
|
333
|
+
# # somewhere else
|
334
|
+
# myclass = Arrow::Injectable.load_class( "myclass" )
|
335
|
+
#
|
336
|
+
#
|
337
|
+
module Injectable
|
338
|
+
|
339
|
+
@derivatives = {}
|
340
|
+
def self::derivatives; @derivatives; end
|
341
|
+
|
342
|
+
### Make the given object (which must be a Class) injectable.
|
343
|
+
def self::extend_object( obj )
|
344
|
+
raise ArgumentError, "can't make a #{obj.class} Injectable" unless
|
345
|
+
obj.is_a?( Class )
|
346
|
+
super
|
347
|
+
@derivatives[ obj.name ] = obj
|
348
|
+
end
|
349
|
+
|
350
|
+
|
351
|
+
### Mixin hook: extend including classes
|
352
|
+
def self::included( mod )
|
353
|
+
Arrow::Logger[self].debug "%s included Injectable" % [ mod.name ]
|
354
|
+
mod.extend( self )
|
355
|
+
super
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
### Return the Class object for the given derivative +classname+,
|
360
|
+
### attempting to load it if it hasn't been already.
|
361
|
+
def self::load_class( classname )
|
362
|
+
Arrow::Logger[self].debug "Loading injectable class '#{classname}'"
|
363
|
+
|
364
|
+
unless Arrow::Injectable.derivatives.include?( classname )
|
365
|
+
modname = classname.downcase.gsub( /::/, '/' )
|
366
|
+
Arrow::Logger[self].debug "Class not loaded yet. Trying to " +
|
367
|
+
"load it from #{modname}"
|
368
|
+
require modname or
|
369
|
+
raise "%s didn't register with Injectable for some reason" % [ classname ]
|
370
|
+
Arrow::Logger[self].debug "Loaded injectable class %s (%d classes loaded)" %
|
371
|
+
[ classname, Arrow::Injectable.derivatives.length ]
|
372
|
+
end
|
373
|
+
|
374
|
+
Arrow::Injectable.derivatives[ classname ]
|
375
|
+
end
|
376
|
+
|
377
|
+
|
378
|
+
#############################################################
|
379
|
+
### A P P E N D E D M E T H O D S
|
380
|
+
#############################################################
|
381
|
+
|
382
|
+
### Classes which inherit from Injectable classes should be
|
383
|
+
### Injectable, too.
|
384
|
+
def inherited( klass )
|
385
|
+
Arrow::Logger[self].debug "making %s Injectable" % [ klass.name ]
|
386
|
+
klass.extend( Arrow::Injectable )
|
387
|
+
super
|
388
|
+
end
|
389
|
+
|
390
|
+
end # module Injectable
|
391
|
+
|
392
|
+
|
393
|
+
# A mixin that adds a #log method to including classes that calls
|
394
|
+
# Arrow::Logger with the class of the receiving object.
|
395
|
+
#
|
396
|
+
# == Usage
|
397
|
+
#
|
398
|
+
# require "arrow/mixins"
|
399
|
+
#
|
400
|
+
# class MyClass
|
401
|
+
# include Arrow::Loggable
|
402
|
+
#
|
403
|
+
# def some_method
|
404
|
+
# self.log.debug "A debugging message"
|
405
|
+
# end
|
406
|
+
# end
|
407
|
+
#
|
408
|
+
module Loggable
|
409
|
+
require 'arrow/logger'
|
410
|
+
|
411
|
+
#########
|
412
|
+
protected
|
413
|
+
#########
|
414
|
+
|
415
|
+
### Return the Arrow::Logger object for the receiving class.
|
416
|
+
def log
|
417
|
+
Arrow::Logger[ self.class ]
|
418
|
+
end
|
419
|
+
|
420
|
+
end # module Loggable
|
421
|
+
|
422
|
+
|
423
|
+
end # module Arrow
|
424
|
+
|
425
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'arrow/constants'
|
4
|
+
|
5
|
+
#
|
6
|
+
# A (hopefully) minimal collection of extensions to core classes.
|
7
|
+
#
|
8
|
+
# == Authors
|
9
|
+
#
|
10
|
+
# * Martin Chase <mchase@rubycrafters.com>
|
11
|
+
# * Michael Granger <mgranger@rubycrafters.com>
|
12
|
+
# * David McCorkhill <dmccorkhill@rubycrafters.com>
|
13
|
+
#
|
14
|
+
# Please see the file LICENSE in the top-level directory for licensing details.
|
15
|
+
#
|
16
|
+
|
17
|
+
### Add some operator methods to regular expression objects for catenation,
|
18
|
+
### union, etc.
|
19
|
+
module Arrow::RegexpOperators
|
20
|
+
|
21
|
+
### Append the given +other+ Regexp (or String) onto a copy of the receiving
|
22
|
+
### one and return it.
|
23
|
+
def +( other )
|
24
|
+
return self.class.new( self.to_s + other.to_s )
|
25
|
+
end
|
26
|
+
|
27
|
+
### Create and return a new Regexp that is an alternation between the
|
28
|
+
### receiver and the +other+ Regexp.
|
29
|
+
def |( other )
|
30
|
+
return Regexp.new( "(?:%s|%s)" % [self.to_s, other.to_s] )
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Extended with Arrow::RegexpOperators
|
35
|
+
class Regexp # :nodoc:
|
36
|
+
include Arrow::RegexpOperators
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
### Add some stuff to the String class to allow easy transformation to Regexp
|
41
|
+
### and in-place interpolation.
|
42
|
+
module Arrow::StringExtensions
|
43
|
+
|
44
|
+
### Return the receiving String as a Regexp.
|
45
|
+
def to_re( casefold=false, extended=false )
|
46
|
+
return Regexp.new( self.dup )
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
### Ideas for String-interpolation stuff courtesy of Hal E. Fulton
|
51
|
+
### <hal9000@hypermetrics.com> via ruby-talk
|
52
|
+
|
53
|
+
### Interpolate any '#{...}' placeholders in the string within the given
|
54
|
+
### +scope+ (a Binding object).
|
55
|
+
def interpolate( scope )
|
56
|
+
unless scope.is_a?( Binding )
|
57
|
+
raise TypeError, "Argument to interpolate must be a Binding, not "\
|
58
|
+
"a #{scope.class.name}"
|
59
|
+
end
|
60
|
+
|
61
|
+
# $stderr.puts ">>> Interpolating '#{self}'..."
|
62
|
+
|
63
|
+
copy = self.gsub( /"/, %q:\": )
|
64
|
+
eval( '"' + copy + '"', scope )
|
65
|
+
rescue Exception => err
|
66
|
+
nicetrace = err.backtrace.find_all {|frame|
|
67
|
+
/in `(interpolate|eval)'/i !~ frame
|
68
|
+
}
|
69
|
+
Kernel.raise( err, err.message, nicetrace )
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
# Extended with Arrow::StringExtensions
|
75
|
+
class String # :nodoc:
|
76
|
+
include Arrow::StringExtensions
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
### Override RubyGem's use of values out of ENV since they're always tainted.
|
81
|
+
# module Gem
|
82
|
+
# def self::find_home
|
83
|
+
# if defined?( Apache )
|
84
|
+
# return Apache.server_root
|
85
|
+
# else
|
86
|
+
# homedir = ENV['HOME'].dup
|
87
|
+
# homedir.untaint
|
88
|
+
#
|
89
|
+
# return homedir
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
|
94
|
+
|