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
@@ -0,0 +1,343 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
require 'arrow/object'
|
5
|
+
require 'arrow/mixins'
|
6
|
+
|
7
|
+
|
8
|
+
module Arrow
|
9
|
+
|
10
|
+
# The Arrow::HTMLTokenizer class -- a simple HTML parser that can be used to break HTML
|
11
|
+
# down into tokens.
|
12
|
+
#
|
13
|
+
# Some of the code and design were stolen from the excellent HTMLTokenizer
|
14
|
+
# library by Ben Giddings <bg@infofiend.com>.
|
15
|
+
#
|
16
|
+
# == VCS Id
|
17
|
+
#
|
18
|
+
# $Id$
|
19
|
+
#
|
20
|
+
# == Authors
|
21
|
+
#
|
22
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
23
|
+
#
|
24
|
+
# :include: LICENSE
|
25
|
+
#
|
26
|
+
#--
|
27
|
+
#
|
28
|
+
# Please see the file LICENSE in the top-level directory for licensing details.
|
29
|
+
#
|
30
|
+
class HTMLTokenizer < Arrow::Object
|
31
|
+
include Enumerable
|
32
|
+
|
33
|
+
# SVN Revision
|
34
|
+
SVNRev = %q$Rev$
|
35
|
+
|
36
|
+
# SVN Id
|
37
|
+
SVNId = %q$Id$
|
38
|
+
|
39
|
+
|
40
|
+
### Create a new Arrow::HtmlTokenizer object.
|
41
|
+
def initialize( source )
|
42
|
+
@source = source
|
43
|
+
@scanner = StringScanner.new( source )
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
######
|
48
|
+
public
|
49
|
+
######
|
50
|
+
|
51
|
+
# The HTML source being tokenized
|
52
|
+
attr_reader :source
|
53
|
+
|
54
|
+
# The StringScanner doing the tokenizing
|
55
|
+
attr_reader :scanner
|
56
|
+
|
57
|
+
|
58
|
+
### Enumerable interface: Iterates over parsed tokens, calling the
|
59
|
+
### supplied block with each one.
|
60
|
+
def each
|
61
|
+
@scanner.reset
|
62
|
+
|
63
|
+
until @scanner.empty?
|
64
|
+
if @scanner.peek(1) == '<'
|
65
|
+
tag = @scanner.scan_until( />/ )
|
66
|
+
|
67
|
+
case tag
|
68
|
+
when /^<!--/
|
69
|
+
token = HTMLComment.new( tag )
|
70
|
+
when /^<!/
|
71
|
+
token = DocType.new( tag )
|
72
|
+
when /^<\?/
|
73
|
+
token = ProcessingInstruction.new( tag )
|
74
|
+
else
|
75
|
+
token = HTMLTag.new( tag )
|
76
|
+
end
|
77
|
+
else
|
78
|
+
text = @scanner.scan( /[^<]+/ )
|
79
|
+
token = HTMLText.new( text )
|
80
|
+
end
|
81
|
+
|
82
|
+
yield( token )
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
#########
|
89
|
+
protected
|
90
|
+
#########
|
91
|
+
|
92
|
+
|
93
|
+
end # class HTMLTokenizer
|
94
|
+
|
95
|
+
|
96
|
+
### Base class for HTML tokens output by Arrow::HTMLTokenizer.
|
97
|
+
class HTMLToken < Arrow::Object # :nodoc:
|
98
|
+
|
99
|
+
### Initialize a token with the +raw+ source of it.
|
100
|
+
def initialize( raw ) # :notnew:
|
101
|
+
super()
|
102
|
+
@raw = raw
|
103
|
+
end
|
104
|
+
|
105
|
+
# The raw source of the token
|
106
|
+
attr_accessor :raw
|
107
|
+
alias_method :to_s, :raw
|
108
|
+
|
109
|
+
### Return an HTML fragment that can be used to represent the token
|
110
|
+
### symbolically in a web-based introspection interface.
|
111
|
+
def to_html
|
112
|
+
content = nil
|
113
|
+
|
114
|
+
if block_given?
|
115
|
+
content = yield
|
116
|
+
# self.log.debug "content = %p" % content
|
117
|
+
else
|
118
|
+
content = self.escape_html( @raw )
|
119
|
+
end
|
120
|
+
|
121
|
+
tokenclass = self.css_class
|
122
|
+
|
123
|
+
%q{<span class="token %s">%s</span>} % [
|
124
|
+
tokenclass,
|
125
|
+
content,
|
126
|
+
]
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
### Return the HTML element class attribute that corresponds to this node.
|
131
|
+
def css_class
|
132
|
+
tokenclass = self.class.name.
|
133
|
+
sub( /Arrow::(HTML)?/i, '').
|
134
|
+
gsub( /::/, '-' ).
|
135
|
+
gsub( /([a-z])([A-Z])/, "\\1-\\2" ).
|
136
|
+
gsub( /[^-\w]+/, '' ).
|
137
|
+
downcase
|
138
|
+
tokenclass << "-token" unless /-token$/.match( tokenclass )
|
139
|
+
|
140
|
+
return tokenclass
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
### Escape special characters in the given +string+ for display in an
|
145
|
+
### HTML inspection interface. This escapes common invisible characters
|
146
|
+
### like tabs and carriage-returns in additional to the regular HTML
|
147
|
+
### escapes.
|
148
|
+
def escape_html( string )
|
149
|
+
return "nil" if string.nil?
|
150
|
+
string = string.inspect unless string.is_a?( String )
|
151
|
+
string.
|
152
|
+
gsub(/&/, '&').
|
153
|
+
gsub(/</, '<').
|
154
|
+
gsub(/>/, '>').
|
155
|
+
gsub(/\r?\n/, %Q{<br />\n}).
|
156
|
+
gsub(/\t/, ' ')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
### Class for tokens output by Arrow::HTMLTokenizer for the text bits of an
|
162
|
+
### HTML document.
|
163
|
+
class HTMLText < HTMLToken # :nodoc:
|
164
|
+
|
165
|
+
### Return an HTML fragment that can be used to represent the token
|
166
|
+
### symbolically in a web-based introspection interface.
|
167
|
+
def to_html
|
168
|
+
marked = self.escape_html( @raw )
|
169
|
+
marked.gsub( /(&[^;]+;)/ ) {|ent|
|
170
|
+
%Q{<span class="entity">#{ent}</span>}
|
171
|
+
}
|
172
|
+
super { marked }
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
### Class for tokens output by Arrow::HTMLTokenizer for HTML comments.
|
179
|
+
class HTMLComment < HTMLToken # :nodoc:
|
180
|
+
CommentPattern = /^<!--((?:[^-]|-(?!-))*)-->$/
|
181
|
+
|
182
|
+
def initialize( raw )
|
183
|
+
super
|
184
|
+
|
185
|
+
unless (( match = CommentPattern.match(raw) ))
|
186
|
+
raise ArgumentError,
|
187
|
+
"Malformed comment %p" % raw
|
188
|
+
end
|
189
|
+
|
190
|
+
@contents = match[1]
|
191
|
+
end
|
192
|
+
|
193
|
+
attr_accessor :contents
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
### Class for tokens output by Arrow::HTMLTokenizer for the tags in an HTML
|
198
|
+
### document.
|
199
|
+
class HTMLTag < HTMLToken # :nodoc:
|
200
|
+
|
201
|
+
# The pattern for matching tag attribute key-value pairs
|
202
|
+
AttributePattern = %r{
|
203
|
+
\s*([-A-Za-z:]+)
|
204
|
+
(?:\s*=\s*(
|
205
|
+
"(?:[^"]|\\.)*" | # Match strings quoted with "
|
206
|
+
'(?:[^']|\\.)*' | # Match strings quoted with '
|
207
|
+
\S+ # Match non-whitespace
|
208
|
+
))?
|
209
|
+
}mx
|
210
|
+
|
211
|
+
#############################################################
|
212
|
+
### I N S T A N C E M E T H O D S
|
213
|
+
#############################################################
|
214
|
+
|
215
|
+
### Create a new HTMLTag from the specified raw source.
|
216
|
+
def initialize( raw )
|
217
|
+
unless (( match = /<\s*(\/)?(\w+)\s*([^>]*)>/.match(raw) ))
|
218
|
+
raise ArgumentError,
|
219
|
+
"Malformed HTMLTag: %p" % raw
|
220
|
+
end
|
221
|
+
|
222
|
+
@endtag = !match[1].nil?
|
223
|
+
@tagname = match[2]
|
224
|
+
@rawattrs = match[3] || ''
|
225
|
+
@attrs = nil
|
226
|
+
|
227
|
+
super
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
######
|
232
|
+
public
|
233
|
+
######
|
234
|
+
|
235
|
+
# The name of the tag
|
236
|
+
attr_reader :tagname
|
237
|
+
|
238
|
+
### Returns +true+ if this tag is an closing tag
|
239
|
+
def endtag?; @endtag; end
|
240
|
+
|
241
|
+
|
242
|
+
### Return the Hash of tag attributes belonging to this token.
|
243
|
+
def attrs
|
244
|
+
unless @attrs
|
245
|
+
@attrs = {}
|
246
|
+
@rawattrs.scan( AttributePattern ) {|name,value|
|
247
|
+
ns = nil
|
248
|
+
if /:/ =~ name
|
249
|
+
ns, name = name.split(/:/, 2)
|
250
|
+
if ns == 'html' then ns = nil end
|
251
|
+
end
|
252
|
+
cname = name.gsub(/-/, '_').downcase
|
253
|
+
cval = value.nil? ? true : value.gsub(/^["']|['"]$/, '')
|
254
|
+
|
255
|
+
if ns.nil?
|
256
|
+
@attrs[ cname.to_sym ] = cval
|
257
|
+
else
|
258
|
+
@attrs[ ns.to_sym ] ||= {}
|
259
|
+
@attrs[ ns.to_sym ][ name.to_sym ] = cval
|
260
|
+
end
|
261
|
+
}
|
262
|
+
end
|
263
|
+
|
264
|
+
return @attrs
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
### Return the tag attribute with the specified name (if it exists).
|
269
|
+
def []( name )
|
270
|
+
self.attrs[ name.gsub(/-/, '_').downcase.to_sym ]
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
### Return an HTML fragment that can be used to represent the token
|
275
|
+
### symbolically in a web-based introspection interface.
|
276
|
+
def to_html
|
277
|
+
tagopen, tagbody = @raw.split( /\s+/, 2 )
|
278
|
+
# self.log.debug "tagopen = %p, tagbody = %p" % [ tagopen, tagbody ]
|
279
|
+
|
280
|
+
tagopen = self.escape_html( tagopen ).sub( %r{^<(/)?(\w+)} ) {|match|
|
281
|
+
%Q{<#$1<span class="tag-token-name">#$2</span>}
|
282
|
+
}
|
283
|
+
|
284
|
+
unless tagbody.nil?
|
285
|
+
tagbody.sub!( />$/, '' )
|
286
|
+
tagbody = self.escape_html( tagbody ).gsub( AttributePattern ) {|match|
|
287
|
+
name, mid, val = match.split(/(\s*=\s*)/, 2)
|
288
|
+
|
289
|
+
val.gsub!( /(\[\?(?:[^\?]|\?(?!\]))+\?\])/s ) {|m|
|
290
|
+
%q{<span class="%s">%s</span>} %
|
291
|
+
[ 'tag-attr-directive', m ]
|
292
|
+
}
|
293
|
+
|
294
|
+
%q{<span class="%s">%s</span>%s<span class="%s">%s</span>} % [
|
295
|
+
'tag-token-attr-name',
|
296
|
+
name,
|
297
|
+
mid,
|
298
|
+
'tag-token-attr-value',
|
299
|
+
val,
|
300
|
+
]
|
301
|
+
}
|
302
|
+
tagbody << '>'
|
303
|
+
end
|
304
|
+
|
305
|
+
#self.log.debug "tagopen = %p; tagbody = %p" %
|
306
|
+
# [ tagopen, tagbody ]
|
307
|
+
super { [tagopen, tagbody].compact.join(" ") }
|
308
|
+
end
|
309
|
+
|
310
|
+
### Escape special characters in the given +string+ for display in an
|
311
|
+
### HTML inspection interface.
|
312
|
+
def escape_html( string )
|
313
|
+
return "nil" if string.nil?
|
314
|
+
string = string.inspect unless string.is_a?( String )
|
315
|
+
string.
|
316
|
+
gsub(/&/, '&').
|
317
|
+
gsub(/</, '<').
|
318
|
+
gsub(/>/, '>')
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
### Class for tokens output by Arrow::HTMLTokenizer for the processing
|
324
|
+
### instructions contained in an HTML document.
|
325
|
+
class ProcessingInstruction < HTMLToken # :nodoc:
|
326
|
+
def initialize( raw )
|
327
|
+
@instruction, @body = raw.gsub(/^\?|\?$/, '').split( /\s+/, 2 )
|
328
|
+
super
|
329
|
+
end
|
330
|
+
|
331
|
+
attr_accessor :instruction, :body
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
### Class for tokens output by Arrow::HTMLTokenizer for the doctype
|
336
|
+
### declaration of an HTML document.
|
337
|
+
class DocType < HTMLToken # :nodoc:
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
end # module Arrow
|
342
|
+
|
343
|
+
|
data/lib/arrow/logger.rb
ADDED
@@ -0,0 +1,488 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'configurability'
|
4
|
+
require 'arrow/mixins'
|
5
|
+
|
6
|
+
# A hierarchical logging class for the Arrow framework. It provides a
|
7
|
+
# generalized means of logging from inside Arrow classes, and then selectively
|
8
|
+
# outputting/formatting log messages from points within the hierarchy.
|
9
|
+
#
|
10
|
+
# A lot of concepts in this class were stolen from Log4r, though it's all
|
11
|
+
# original code, and works a bit differently.
|
12
|
+
#
|
13
|
+
# == Synopsis
|
14
|
+
#
|
15
|
+
# require 'arrow/object'
|
16
|
+
# require 'arrow/logger'
|
17
|
+
#
|
18
|
+
# logger = Arrow::Logger.global
|
19
|
+
# logfile = File.open( "global.log", "a" )
|
20
|
+
# logger.outputters << Arrow::Logger::Outputter.new(logfile)
|
21
|
+
# logger.level = :debug
|
22
|
+
#
|
23
|
+
# class MyClass < Arrow::Object
|
24
|
+
#
|
25
|
+
# def self::fooMethod
|
26
|
+
# Arrow::Logger.debug( "In server start routine" )
|
27
|
+
# Arrow::Logger.info( "Server is not yet configured." )
|
28
|
+
# Arrow::Logger.notice( "Server is starting up." )
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def initialize
|
32
|
+
# self.log.info( "Initializing another MyClass object." )
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# == VCS Id
|
37
|
+
#
|
38
|
+
# $Id: logger.rb,v ba725438313d 2010/08/09 02:08:40 ged $
|
39
|
+
#
|
40
|
+
# == Authors
|
41
|
+
#
|
42
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
43
|
+
#
|
44
|
+
# Please see the file LICENSE in the top-level directory for licensing details.
|
45
|
+
#
|
46
|
+
class Arrow::Logger
|
47
|
+
extend Configurability
|
48
|
+
|
49
|
+
config_key :logging
|
50
|
+
|
51
|
+
require 'arrow/logger/outputter'
|
52
|
+
|
53
|
+
|
54
|
+
# Construct a log levels Hash on the fly
|
55
|
+
LEVELS = [
|
56
|
+
:debug,
|
57
|
+
:info,
|
58
|
+
:notice,
|
59
|
+
:warning,
|
60
|
+
:error,
|
61
|
+
:crit,
|
62
|
+
:alert,
|
63
|
+
:emerg,
|
64
|
+
].inject({}) {|hsh, sym| hsh[ sym ] = hsh.length; hsh}
|
65
|
+
LEVEL_NAMES = LEVELS.invert
|
66
|
+
|
67
|
+
### Module for adding internals debugging to the Logger class
|
68
|
+
module DebugLogger # :nodoc:
|
69
|
+
def debug_msg( *parts ) # :nodoc:
|
70
|
+
# $deferr.puts parts.join('') if $DEBUG
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
include DebugLogger
|
75
|
+
extend DebugLogger
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
#############################################################
|
80
|
+
### C L A S S M E T H O D S
|
81
|
+
#############################################################
|
82
|
+
|
83
|
+
# Loggers for Modules, keyed by Module
|
84
|
+
@logger_map = Hash.new do |h,mod|
|
85
|
+
h[ mod ] = self.new( mod )
|
86
|
+
end
|
87
|
+
class << self; attr_reader :logger_map ; end
|
88
|
+
|
89
|
+
|
90
|
+
### Configure logging from the 'logging' section of the config.
|
91
|
+
def self::configure( config )
|
92
|
+
|
93
|
+
self.reset
|
94
|
+
apacheoutputter = Arrow::Logger::Outputter.create( 'apache' )
|
95
|
+
|
96
|
+
config.each do |klass, setting|
|
97
|
+
level, uri = self.parse_log_setting( setting )
|
98
|
+
|
99
|
+
# Use the Apache log as the outputter if none is configured
|
100
|
+
if uri.nil?
|
101
|
+
outputter = apacheoutputter
|
102
|
+
else
|
103
|
+
outputter = Arrow::Logger::Outputter.create( uri )
|
104
|
+
end
|
105
|
+
|
106
|
+
# The 'global' entry configures the global logger
|
107
|
+
if klass == :global
|
108
|
+
self.global.level = level
|
109
|
+
self.global.outputters << outputter
|
110
|
+
next
|
111
|
+
end
|
112
|
+
|
113
|
+
# If the class bit is something like 'applet', then transform
|
114
|
+
# it into 'Arrow::Applet'
|
115
|
+
if klass.to_s.match( /^[a-z][a-zA-Z]+$/ )
|
116
|
+
realclass = "Arrow::%s" % klass.to_s.sub(/^([a-z])/){ $1.upcase }
|
117
|
+
else
|
118
|
+
realclass = klass.to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
Arrow::Logger[ realclass ].level = level
|
122
|
+
Arrow::Logger[ realclass ].outputters << outputter
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
### Parse the configuration for a given class's logger. The configuration
|
129
|
+
### is in the form:
|
130
|
+
### <level> [<outputter_uri>]
|
131
|
+
### where +level+ is one of the logging levels defined by this class (see
|
132
|
+
### the LEVELS constant), and the optional +outputter_uri+ indicates which
|
133
|
+
### outputter to use, and how it should be configured. See
|
134
|
+
### Arrow::Logger::Outputter for more info.
|
135
|
+
###
|
136
|
+
### Examples:
|
137
|
+
### notice
|
138
|
+
### debug file:///tmp/broker-debug.log
|
139
|
+
### error dbi://www:password@localhost/www.errorlog?driver=postgresql
|
140
|
+
###
|
141
|
+
def self::parse_log_setting( setting )
|
142
|
+
level, rawuri = setting.split( ' ', 2 )
|
143
|
+
uri = rawuri.nil? ? nil : URI.parse( rawuri )
|
144
|
+
|
145
|
+
return level.to_sym, uri
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
### Return the Arrow::Logger for the given module +mod+, which can be a
|
150
|
+
### Module object, a Symbol, or a String.
|
151
|
+
def self::[]( mod=nil )
|
152
|
+
return self.global if mod.nil?
|
153
|
+
|
154
|
+
case mod
|
155
|
+
when Module
|
156
|
+
return self.logger_map[ mod ]
|
157
|
+
|
158
|
+
# If it's a String, try to map it to a class name, falling back on the global
|
159
|
+
# logger if that fails
|
160
|
+
when String
|
161
|
+
mod = mod.split('::').
|
162
|
+
inject( Object ) {|k, modname| k.const_get(modname) } rescue Object
|
163
|
+
return self.logger_map[ mod ]
|
164
|
+
else
|
165
|
+
|
166
|
+
return self.logger_map[ mod.class ]
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
### Return the global Arrow logger, setting it up if it hasn't been
|
173
|
+
### already.
|
174
|
+
def self::global
|
175
|
+
self.logger_map[ Object ]
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
### Reset the logging subsystem. Clears out any registered loggers and
|
180
|
+
### their associated outputters.
|
181
|
+
def self::reset
|
182
|
+
self.logger_map.clear
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
### Autoload global logging methods for the log levels
|
187
|
+
def self::method_missing( sym, *args )
|
188
|
+
return super unless LEVELS.key?( sym )
|
189
|
+
|
190
|
+
self.global.debug( "Autoloading class log method '#{sym}'." )
|
191
|
+
(class << self; self; end).class_eval do
|
192
|
+
define_method( sym ) do |*args|
|
193
|
+
self.global.send( sym, *args )
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
self.global.send( sym, *args )
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
#############################################################
|
202
|
+
### I N S T A N C E M E T H O D S
|
203
|
+
#############################################################
|
204
|
+
|
205
|
+
### Create and return a new Arrow::Logger object for the given +mod+ (a Module object). If
|
206
|
+
### It will be configured at the given +level+. Any +outputters+ that are specified will be
|
207
|
+
### added.
|
208
|
+
def initialize( mod, level=:info, *outputters )
|
209
|
+
@module = mod
|
210
|
+
@outputters = outputters
|
211
|
+
@trace = false
|
212
|
+
@level = nil
|
213
|
+
|
214
|
+
# Cached Array of modules and classes between
|
215
|
+
# this logger's module and Object
|
216
|
+
@supermods = nil
|
217
|
+
|
218
|
+
# Level to force messages written to this logger to
|
219
|
+
@forced_level = nil
|
220
|
+
|
221
|
+
self.level = level
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
######
|
226
|
+
public
|
227
|
+
######
|
228
|
+
|
229
|
+
# The module this logger is associated with
|
230
|
+
attr_reader :module
|
231
|
+
|
232
|
+
# The outputters attached to this branch of the logger tree.
|
233
|
+
attr_accessor :outputters
|
234
|
+
|
235
|
+
# Set to a true value to turn tracing on
|
236
|
+
attr_accessor :trace
|
237
|
+
|
238
|
+
# The integer level of the logger.
|
239
|
+
attr_reader :level
|
240
|
+
|
241
|
+
# The level to force messages written to this logger to
|
242
|
+
attr_accessor :forced_level
|
243
|
+
|
244
|
+
|
245
|
+
### Return a human-readable string representation of the object.
|
246
|
+
def inspect
|
247
|
+
"#<%s:0x%0x %s [level: %s, outputters: %d, trace: %s]>" % [
|
248
|
+
self.class.name,
|
249
|
+
self.object_id * 2,
|
250
|
+
self.readable_name,
|
251
|
+
self.readable_level,
|
252
|
+
self.outputters.length,
|
253
|
+
self.trace ? "on" : "off",
|
254
|
+
]
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
### Return a (more-detailed) human-readable string representation of the object.
|
259
|
+
def inspect_details( level=0 )
|
260
|
+
indent = ' ' * (level + 1)
|
261
|
+
|
262
|
+
prelude = "<< %s [level: %s, trace: %s] >>" % [
|
263
|
+
self.readable_name,
|
264
|
+
self.readable_level,
|
265
|
+
self.trace ? "on" : "off",
|
266
|
+
]
|
267
|
+
|
268
|
+
details = []
|
269
|
+
unless self.outputters.empty?
|
270
|
+
details << "Outputters:" << self.outputters.map {|op| op.inspect }
|
271
|
+
end
|
272
|
+
details = details.flatten.compact.map {|line| indent + line }
|
273
|
+
|
274
|
+
if level.zero?
|
275
|
+
return [ prelude, *details ].join( "\n" )
|
276
|
+
else
|
277
|
+
return [ prelude, *details ]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
### Return the name of the logger formatted to be suitable for reading.
|
283
|
+
def readable_name
|
284
|
+
return '(global)' if self.module == Object
|
285
|
+
return self.module.inspect if self.module.name == ''
|
286
|
+
return self.module.name
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
### Return the logger's level as a Symbol.
|
291
|
+
def readable_level
|
292
|
+
return LEVEL_NAMES[ @level ]
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
### Set the level of this logger to +level+. The +level+ can be a
|
297
|
+
### String, a Symbol, or an Integer.
|
298
|
+
def level=( level )
|
299
|
+
# debug_msg ">>> Setting log level for %s to %p" %
|
300
|
+
# [ self.name.empty? ? "[Global]" : self.name, level ]
|
301
|
+
|
302
|
+
case level
|
303
|
+
when String
|
304
|
+
@level = LEVELS[ level.to_sym ]
|
305
|
+
when Symbol
|
306
|
+
@level = LEVELS[ level ]
|
307
|
+
when Integer
|
308
|
+
@level = level
|
309
|
+
else
|
310
|
+
@level = nil
|
311
|
+
end
|
312
|
+
|
313
|
+
# If the level wasn't set correctly, raise an error after setting
|
314
|
+
# the level to something reasonable.
|
315
|
+
if @level.nil?
|
316
|
+
@level = LEVELS[ :notice ]
|
317
|
+
raise ArgumentError, "Illegal log level specification: %p for %s" %
|
318
|
+
[ level, self.name ]
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
|
323
|
+
### Return the Arrow::Logger for this instance's module's parent class if it's a Class,
|
324
|
+
### and the global logger otherwise.
|
325
|
+
def superlogger
|
326
|
+
if @module == Object
|
327
|
+
return nil
|
328
|
+
elsif @module.respond_to?( :superclass )
|
329
|
+
Arrow::Logger[ @module.superclass ]
|
330
|
+
else
|
331
|
+
Arrow::Logger.global
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
### Return the Array of modules and classes the receiver's module includes
|
337
|
+
### or inherits, inclusive of the receiver's module itself.
|
338
|
+
def supermods
|
339
|
+
unless @supermods
|
340
|
+
objflag = false
|
341
|
+
@supermods = self.module.ancestors.partition {|mod| objflag ||= (mod == Object) }.last
|
342
|
+
@supermods << Object
|
343
|
+
end
|
344
|
+
|
345
|
+
return @supermods
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
|
350
|
+
### Return a uniquified Array of the loggers which are more-generally related
|
351
|
+
### hierarchically to the receiver, inclusive, and whose level is +level+ or
|
352
|
+
### lower.
|
353
|
+
def hierloggers( level=:emerg )
|
354
|
+
level = LEVELS[ level ] if level.is_a?( Symbol )
|
355
|
+
|
356
|
+
loggers = []
|
357
|
+
self.supermods.each do |mod|
|
358
|
+
logger = self.class.logger_map[ mod ]
|
359
|
+
next unless logger.level <= level
|
360
|
+
|
361
|
+
loggers << logger
|
362
|
+
yield( logger ) if block_given?
|
363
|
+
end
|
364
|
+
|
365
|
+
return loggers
|
366
|
+
end
|
367
|
+
|
368
|
+
|
369
|
+
### Return a uniquified Array of all outputters for this logger and all of the
|
370
|
+
### loggers above it in the logging hierarchy that are set to +level+ or lower.
|
371
|
+
### If called with a block, it will be called once for each outputter and the first
|
372
|
+
### logger to which it is attached.
|
373
|
+
def hieroutputters( level=LEVELS[:emerg] )
|
374
|
+
outputters = []
|
375
|
+
|
376
|
+
# Look for loggers which are higher in the hierarchy
|
377
|
+
self.hierloggers( level ) do |logger|
|
378
|
+
outpary = logger.outputters || []
|
379
|
+
newoutpary = outpary - (outpary & outputters)
|
380
|
+
|
381
|
+
# If there are any outputters which haven't already been seen,
|
382
|
+
# output to them.
|
383
|
+
unless newoutpary.empty?
|
384
|
+
# debug_msg "hieroutputters: adding: %s" %
|
385
|
+
# newoutpary.collect {|outp| outp.description}.join(", ")
|
386
|
+
if block_given?
|
387
|
+
newoutpary.each {|outputter| yield(outputter, logger)}
|
388
|
+
end
|
389
|
+
outputters += newoutpary
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
return outputters
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
### Write the given +args+ to any connected outputters if +level+ is
|
398
|
+
### less than or equal to this logger's level.
|
399
|
+
def write( level, *args )
|
400
|
+
# debug_msg "Writing message at %p from %s: %p" % [ level, caller(2).first, args ]
|
401
|
+
|
402
|
+
msg, frame = nil, nil
|
403
|
+
time = Time.now
|
404
|
+
|
405
|
+
# If tracing is turned on, pick the first frame in the stack that
|
406
|
+
# isn't in this file, or the last one if that fails to yield one.
|
407
|
+
if @trace
|
408
|
+
frame = caller(1).find {|fr| fr !~ %r{arrow/logger\.rb} } ||
|
409
|
+
caller(1).last
|
410
|
+
end
|
411
|
+
|
412
|
+
level = @forced_level if @forced_level
|
413
|
+
|
414
|
+
# Find the outputters that need to be written to, then write to them.
|
415
|
+
self.hieroutputters( level ) do |outp, logger|
|
416
|
+
# debug_msg "Got outputter %p" % outp
|
417
|
+
msg ||= args.collect {|obj| self.stringify_object(obj) }.join
|
418
|
+
outp.write( time, level, self.readable_name, frame, msg )
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
|
423
|
+
### Append the given +obj+ to the logger at +:debug+ level. This is for
|
424
|
+
### compatibility with objects that append to $stderr for their logging
|
425
|
+
### (e.g., net/protocols-based libraries).
|
426
|
+
def <<( obj )
|
427
|
+
self.write( :debug, obj )
|
428
|
+
return self
|
429
|
+
end
|
430
|
+
|
431
|
+
|
432
|
+
#########
|
433
|
+
protected
|
434
|
+
#########
|
435
|
+
|
436
|
+
### Dump the given object for output in the log.
|
437
|
+
def stringify_object( obj )
|
438
|
+
return case obj
|
439
|
+
when Exception
|
440
|
+
"%s:\n %s" % [ obj.message, obj.backtrace.join("\n ") ]
|
441
|
+
when String
|
442
|
+
obj
|
443
|
+
else
|
444
|
+
obj.inspect
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
### Auto-install logging methods (ie., methods whose names match one of
|
450
|
+
### Arrow::Logger::LEVELS.
|
451
|
+
def method_missing( sym, *args )
|
452
|
+
name = sym.to_s
|
453
|
+
level = name[/\w+/].to_sym
|
454
|
+
return super unless Arrow::Logger::LEVELS.member?( level )
|
455
|
+
code = nil
|
456
|
+
|
457
|
+
case name
|
458
|
+
when /^\w+\?/
|
459
|
+
code = self.make_level_predicate_method( level )
|
460
|
+
|
461
|
+
when /^\w+$/
|
462
|
+
code = self.make_writer_method( level )
|
463
|
+
|
464
|
+
else
|
465
|
+
return super
|
466
|
+
end
|
467
|
+
|
468
|
+
self.class.send( :define_method, sym, &code )
|
469
|
+
return self.method( sym ).call( *args )
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
### Return a Proc suitable for installing as a predicate method for the given
|
474
|
+
### logging level.
|
475
|
+
def make_level_predicate_method( level )
|
476
|
+
numeric_level = LEVELS[level]
|
477
|
+
Proc.new { self.level < numeric_level }
|
478
|
+
end
|
479
|
+
|
480
|
+
|
481
|
+
### Return a Proc suitable for installing as a log-writing method for the given
|
482
|
+
### logging level.
|
483
|
+
def make_writer_method( level )
|
484
|
+
Proc.new {|*args| self.write(level, *args)}
|
485
|
+
end
|
486
|
+
|
487
|
+
end # class Arrow::Logger
|
488
|
+
|