nitro 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/AUTHORS +4 -1
  2. data/ChangeLog +290 -7
  3. data/README +3 -3
  4. data/RELEASES +94 -0
  5. data/Rakefile +7 -7
  6. data/benchmark/og/bench.rb +11 -10
  7. data/{ChangeLog.1 → doc/ChangeLog.1} +0 -0
  8. data/doc/apache.txt +9 -0
  9. data/doc/architecture.txt +1 -27
  10. data/doc/bugs.txt +11 -4
  11. data/doc/config.txt +22 -0
  12. data/doc/og_config.txt +35 -0
  13. data/doc/og_tutorial.txt +595 -0
  14. data/doc/tutorial.txt +22 -0
  15. data/examples/blog/conf/apache.conf +30 -0
  16. data/examples/blog/conf/lhttpd.conf +2 -2
  17. data/examples/blog/lib/blog/controller.rb +11 -2
  18. data/examples/blog/log/apache.error_log +5528 -0
  19. data/examples/blog/root/fcgi.rb +1 -1
  20. data/examples/blog/run.rb +9 -3
  21. data/examples/flash/run.rb +2 -2
  22. data/examples/no_xsl_blog/conf/apache.conf +30 -0
  23. data/examples/no_xsl_blog/conf/lhttpd.conf +1 -1
  24. data/examples/no_xsl_blog/lib/blog/controller.rb +2 -2
  25. data/examples/no_xsl_blog/log/apache.error_log +68 -0
  26. data/examples/no_xsl_blog/root/fcgi.rb +2 -2
  27. data/examples/no_xsl_blog/run.rb +3 -3
  28. data/examples/og/run.rb +1 -1
  29. data/examples/tiny/conf/apache.conf +29 -4
  30. data/examples/tiny/conf/lhttpd.conf +1 -1
  31. data/examples/tiny/log/apache.error_log +30 -0
  32. data/examples/tiny/root/fcgi.rb +2 -2
  33. data/examples/tiny/root/index.xhtml +1 -1
  34. data/examples/tiny/run.rb +3 -2
  35. data/examples/wee_style/run.rb +7 -9
  36. data/examples/why_wiki/README +5 -0
  37. data/examples/why_wiki/run.rb +57 -0
  38. data/examples/why_wiki/wiki.yml +6 -0
  39. data/examples/wiki.yml +1 -0
  40. data/lib/glue/array.rb +14 -33
  41. data/lib/glue/hash.rb +32 -53
  42. data/lib/glue/pool.rb +9 -12
  43. data/lib/glue/property.rb +31 -9
  44. data/lib/nitro.rb +30 -8
  45. data/lib/nitro/adapters/cgi.rb +23 -3
  46. data/lib/nitro/adapters/webrick.rb +9 -3
  47. data/lib/nitro/builders/form.rb +21 -13
  48. data/lib/nitro/builders/rss.rb +20 -9
  49. data/lib/nitro/builders/table.rb +69 -10
  50. data/lib/nitro/builders/xhtml.rb +13 -4
  51. data/lib/nitro/component.rb +15 -0
  52. data/lib/nitro/conf.rb +4 -3
  53. data/lib/nitro/context.rb +22 -14
  54. data/lib/nitro/controller.rb +63 -5
  55. data/lib/nitro/dispatcher.rb +11 -6
  56. data/lib/nitro/output.rb +28 -0
  57. data/lib/nitro/render.rb +78 -59
  58. data/lib/nitro/request.rb +5 -1
  59. data/lib/nitro/runner.rb +20 -6
  60. data/lib/nitro/session.rb +89 -18
  61. data/lib/nitro/session/drb.rb +31 -0
  62. data/lib/nitro/session/drbserver.rb +71 -0
  63. data/lib/nitro/session/memory.rb +13 -0
  64. data/lib/nitro/simple.rb +7 -0
  65. data/lib/nitro/ui/date-select.rb +2 -5
  66. data/lib/nitro/ui/pager.rb +4 -4
  67. data/lib/nitro/ui/tabs.rb +2 -4
  68. data/lib/nitro/uri.rb +7 -4
  69. data/lib/og.rb +20 -12
  70. data/lib/og/adapter.rb +40 -13
  71. data/lib/og/adapters/filesys.rb +121 -0
  72. data/lib/og/adapters/mysql.rb +10 -5
  73. data/lib/og/adapters/oracle.rb +374 -0
  74. data/lib/og/adapters/psql.rb +10 -23
  75. data/lib/og/adapters/sqlite.rb +3 -3
  76. data/lib/og/backend.rb +2 -2
  77. data/lib/og/connection.rb +6 -6
  78. data/lib/og/database.rb +5 -5
  79. data/lib/og/enchant.rb +6 -2
  80. data/lib/og/meta.rb +56 -26
  81. data/lib/og/mock.rb +1 -1
  82. data/lib/og/typemacros.rb +23 -0
  83. data/lib/parts/content.rb +4 -10
  84. data/test/nitro/adapters/tc_cgi.rb +1 -1
  85. data/test/nitro/builders/tc_rss.rb +1 -1
  86. data/test/nitro/builders/tc_table.rb +30 -0
  87. data/test/nitro/tc_context.rb +4 -0
  88. data/test/nitro/tc_controller.rb +9 -2
  89. data/test/og/tc_filesys.rb +83 -0
  90. data/test/og/tc_meta.rb +55 -0
  91. data/test/tc_og.rb +115 -36
  92. data/vendor/README +11 -5
  93. data/vendor/breakpoint.rb +35 -38
  94. data/vendor/breakpoint_client.rb +119 -80
  95. data/vendor/composite_sexp_processor.rb +43 -0
  96. data/vendor/parse_tree.rb +745 -0
  97. data/vendor/sexp_processor.rb +453 -0
  98. metadata +34 -7
  99. data/examples/no_xsl_blog/conf/app.conf.rb +0 -47
@@ -0,0 +1,453 @@
1
+
2
+ $TESTING = false unless defined? $TESTING
3
+
4
+ class Object
5
+
6
+ ##
7
+ # deep_clone is the usual Marshalling hack to make a deep copy.
8
+ # It is rather slow, so use it sparingly. Helps with debugging
9
+ # SexpProcessors since you usually shift off sexps.
10
+
11
+ def deep_clone
12
+ Marshal.load(Marshal.dump(self))
13
+ end
14
+ end
15
+
16
+ ##
17
+ # Sexps are the basic storage mechanism of SexpProcessor. Sexps have
18
+ # a +type+ (to be renamed +node_type+) which is the first element of
19
+ # the Sexp. The type is used by SexpProcessor to determine whom to
20
+ # dispatch the Sexp to for processing.
21
+
22
+ class Sexp < Array # ZenTest FULL
23
+
24
+ @@array_types = [ :array, :args, ]
25
+
26
+ ##
27
+ # Named positional parameters.
28
+ # Use with +SexpProcessor.require_empty=false+.
29
+ attr_accessor :accessors
30
+
31
+ ##
32
+ # Create a new Sexp containing +args+.
33
+
34
+ def initialize(*args)
35
+ @accessors = []
36
+ super(args)
37
+ end
38
+
39
+ def self.from_array(a)
40
+ ary = Array === a ? a : [a]
41
+
42
+ result = self.new
43
+
44
+ ary.each do |x|
45
+ case x
46
+ when Sexp
47
+ result << x
48
+ when Array
49
+ result << self.from_array(x)
50
+ else
51
+ result << x
52
+ end
53
+ end
54
+
55
+ result
56
+ end
57
+
58
+ ##
59
+ # Returns true if the node_type is +array+ or +args+.
60
+ #
61
+ # REFACTOR: to TypedSexp - we only care when we have units.
62
+
63
+ def array_type?
64
+ type = self.first
65
+ @@array_types.include? type
66
+ end
67
+
68
+ ##
69
+ # Enumeratates the sexp yielding to +b+ when the node_type == +t+.
70
+
71
+ def each_of_type(t, &b)
72
+ each do | elem |
73
+ if Sexp === elem then
74
+ elem.each_of_type(t, &b)
75
+ b.call(elem) if elem.first == t
76
+ end
77
+ end
78
+ end
79
+
80
+ ##
81
+ # Replaces all elements whose node_type is +from+ with +to+. Used
82
+ # only for the most trivial of rewrites.
83
+
84
+ def find_and_replace_all(from, to)
85
+ each_with_index do | elem, index |
86
+ if Sexp === elem then
87
+ elem.find_and_replace_all(from, to)
88
+ else
89
+ self[index] = to if elem == from
90
+ end
91
+ end
92
+ end
93
+
94
+ ##
95
+ # Fancy-Schmancy method used to implement named positional accessors
96
+ # via +accessors+.
97
+ #
98
+ # Example:
99
+ #
100
+ # class MyProcessor < SexpProcessor
101
+ # def initialize
102
+ # super
103
+ # self.require_empty = false
104
+ # self.sexp_accessors = {
105
+ # :call => [:lhs, :name, :rhs]
106
+ # }
107
+ # ...
108
+ # end
109
+ #
110
+ # def process_call(exp)
111
+ # lhs = exp.lhs
112
+ # name = exp.name
113
+ # rhs = exp.rhs
114
+ # ...
115
+ # end
116
+ # end
117
+
118
+ def method_missing(meth, *a, &b)
119
+ super unless @accessors.include? meth
120
+
121
+ index = @accessors.index(meth) + 1 # skip type
122
+ return self.at(index)
123
+ end
124
+
125
+ ##
126
+ # Returns the Sexp without the node_type.
127
+
128
+ def sexp_body
129
+ self[1..-1]
130
+ end
131
+
132
+ ##
133
+ # Returnes the bare bones structure of the sexp.
134
+ # s(:a, :b, s(:c, :d), :e) => s(:a, s(:c))
135
+
136
+ def structure
137
+ result = self.class.new
138
+ if Array === self.first then
139
+ result = self.first.structure
140
+ else
141
+ result << self.shift
142
+ self.grep(Array).each do |subexp|
143
+ result << subexp.structure
144
+ end
145
+ end
146
+ result
147
+ end
148
+
149
+ def ==(obj) # :nodoc:
150
+ case obj
151
+ when Sexp
152
+ super
153
+ else
154
+ false
155
+ end
156
+ end
157
+
158
+ def to_a # :nodoc:
159
+ self.map { |o| Sexp === o ? o.to_a : o }
160
+ end
161
+
162
+ def inspect # :nodoc:
163
+ sexp_str = self.map {|x|x.inspect}.join(', ')
164
+ return "s(#{sexp_str})"
165
+ end
166
+
167
+ def pretty_print(q) # :nodoc:
168
+ q.group(1, 's(', ')') do
169
+ q.seplist(self) {|v| q.pp v }
170
+ end
171
+ end
172
+
173
+ def to_s # :nodoc:
174
+ inspect
175
+ end
176
+
177
+ ##
178
+ # If run with debug, Sexp will raise if you shift on an empty
179
+ # Sexp. Helps with debugging.
180
+
181
+ def shift
182
+ raise "I'm empty" if self.empty?
183
+ super
184
+ end if $DEBUG or $TESTING
185
+
186
+ end
187
+
188
+ ##
189
+ # This is just a stupid shortcut to make indentation much cleaner.
190
+
191
+ def s(*args)
192
+ Sexp.new(*args)
193
+ end
194
+
195
+ ##
196
+ # Raised by SexpProcessor if it sees a node type listed in its
197
+ # unsupported list.
198
+
199
+ class UnsupportedNodeError < SyntaxError; end
200
+
201
+ ##
202
+ # Raised by SexpProcessor if it is in strict mode and sees a node for
203
+ # which there is no processor available.
204
+
205
+ class UnknownNodeError < SyntaxError; end
206
+
207
+ ##
208
+ # Raised by SexpProcessor if a processor did not process every node in
209
+ # a sexp and @require_empty is true.
210
+
211
+ class NotEmptyError < SyntaxError; end
212
+
213
+ ##
214
+ # SexpProcessor provides a uniform interface to process Sexps.
215
+ #
216
+ # In order to create your own SexpProcessor subclass you'll need
217
+ # to call super in the initialize method, then set any of the
218
+ # Sexp flags you want to be different from the defaults.
219
+ #
220
+ # SexpProcessor uses a Sexp's type to determine which process method
221
+ # to call in the subclass. For Sexp <code>s(:lit, 1)</code>
222
+ # SexpProcessor will call #process_lit, if it is defined.
223
+ #
224
+ # You can also specify a default method to call for any Sexp types
225
+ # without a process_<type> method or use the default processor provided to
226
+ # skip over them.
227
+ #
228
+ # Here is a simple example:
229
+ #
230
+ # class MyProcessor < SexpProcessor
231
+ # def initialize
232
+ # super
233
+ # self.strict = false
234
+ # end
235
+ #
236
+ # def process_lit(exp)
237
+ # val = exp.shift
238
+ # return val
239
+ # end
240
+ # end
241
+
242
+ class SexpProcessor
243
+
244
+ ##
245
+ # A default method to call if a process_<type> method is not found
246
+ # for the Sexp type.
247
+
248
+ attr_accessor :default_method
249
+
250
+ ##
251
+ # Emit a warning when the method in #default_method is called.
252
+
253
+ attr_accessor :warn_on_default
254
+
255
+ ##
256
+ # Automatically shifts off the Sexp type before handing the
257
+ # Sexp to process_<type>
258
+
259
+ attr_accessor :auto_shift_type
260
+
261
+ ##
262
+ # An array that specifies node types that are unsupported by this
263
+ # processor. SexpProcesor will raise UnsupportedNodeError if you try
264
+ # to process one of those node types.
265
+
266
+ attr_accessor :unsupported
267
+
268
+ ##
269
+ # Raise an exception if no process_<type> method is found for a Sexp.
270
+
271
+ attr_accessor :strict
272
+
273
+ ##
274
+ # A Hash of Sexp types and Regexp.
275
+ #
276
+ # Print a debug message if the Sexp type matches the Hash key
277
+ # and the Sexp's #inspect output matches the Regexp.
278
+
279
+ attr_accessor :debug
280
+
281
+ ##
282
+ # Expected result class
283
+
284
+ attr_accessor :expected
285
+
286
+ ##
287
+ # Raise an exception if the Sexp is not empty after processing
288
+
289
+ attr_accessor :require_empty
290
+
291
+ ##
292
+ # Adds accessor methods to the Sexp
293
+
294
+ attr_accessor :sexp_accessors
295
+
296
+ ##
297
+ # Creates a new SexpProcessor. Use super to invoke this
298
+ # initializer from SexpProcessor subclasses, then use the
299
+ # attributes above to customize the functionality of the
300
+ # SexpProcessor
301
+
302
+ def initialize
303
+ @collection = []
304
+ @default_method = nil
305
+ @warn_on_default = true
306
+ @auto_shift_type = false
307
+ @strict = false
308
+ @unsupported = []
309
+ @debug = {}
310
+ @expected = Sexp
311
+ @require_empty = true
312
+ @sexp_accessors = {}
313
+
314
+ # we do this on an instance basis so we can subclass it for
315
+ # different processors.
316
+ @processors = {}
317
+ @rewriters = {}
318
+
319
+ public_methods.each do |name|
320
+ case name
321
+ when /^process_(.*)/ then
322
+ @processors[$1.intern] = name.intern
323
+ when /^rewrite_(.*)/ then
324
+ @rewriters[$1.intern] = name.intern
325
+ end
326
+ end
327
+ end
328
+
329
+ ##
330
+ # Default Sexp processor. Invokes process_<type> methods matching
331
+ # the Sexp type given. Performs additional checks as specified by
332
+ # the initializer.
333
+
334
+ def process(exp)
335
+ return nil if exp.nil?
336
+
337
+ exp_orig = exp.deep_clone if $DEBUG
338
+ result = self.expected.new
339
+
340
+ type = exp.first
341
+
342
+ if @debug.include? type then
343
+ str = exp.inspect
344
+ puts "// DEBUG: #{str}" if str =~ @debug[type]
345
+ end
346
+
347
+ if Sexp === exp then
348
+ if @sexp_accessors.include? type then
349
+ exp.accessors = @sexp_accessors[type]
350
+ else
351
+ exp.accessors = [] # clean out accessor list in case it changed
352
+ end
353
+ end
354
+
355
+ raise UnsupportedNodeError, "'#{type}' is not a supported node type" if @unsupported.include? type
356
+
357
+ # do a pass through the rewriter first, if any, reassign back to exp
358
+ meth = @rewriters[type]
359
+ if meth then
360
+ new_exp = self.send(meth, exp)
361
+ # REFACTOR: duplicated from below
362
+ if @require_empty and not exp.empty? then
363
+ msg = "exp not empty after #{self.class}.#{meth} on #{exp.inspect}"
364
+ if $DEBUG then
365
+ msg += " from #{exp_orig.inspect}"
366
+ end
367
+ raise NotEmptyError, msg
368
+ end
369
+ exp = new_exp
370
+ end
371
+
372
+ # now do a pass with the real processor (or generic
373
+ meth = @processors[type] || @default_method
374
+ if meth then
375
+
376
+ if @warn_on_default and meth == @default_method then
377
+ $stderr.puts "WARNING: Using default method #{meth} for #{type}"
378
+ end
379
+
380
+ exp.shift if @auto_shift_type and meth != @default_method
381
+
382
+ result = self.send(meth, exp)
383
+ raise TypeError, "Result must be a #{@expected}, was #{result.class}:#{result.inspect}" unless @expected === result
384
+
385
+ if @require_empty and not exp.empty? then
386
+ msg = "exp not empty after #{self.class}.#{meth} on #{exp.inspect}"
387
+ if $DEBUG then
388
+ msg += " from #{exp_orig.inspect}"
389
+ end
390
+ raise NotEmptyError, msg
391
+ end
392
+ else
393
+ unless @strict then
394
+ until exp.empty? do
395
+ sub_exp = exp.shift
396
+ sub_result = nil
397
+ if Array === sub_exp then
398
+ sub_result = process(sub_exp)
399
+ raise "Result is a bad type" unless Array === sub_exp
400
+ raise "Result does not have a type in front: #{sub_exp.inspect}" unless Symbol === sub_exp.first unless sub_exp.empty?
401
+ else
402
+ sub_result = sub_exp
403
+ end
404
+ result << sub_result
405
+ end
406
+
407
+ # NOTE: this is costly, but we are in the generic processor
408
+ # so we shouldn't hit it too much with RubyToC stuff at least.
409
+ #if Sexp === exp and not exp.sexp_type.nil? then
410
+ begin
411
+ result.sexp_type = exp.sexp_type
412
+ rescue Exception
413
+ # nothing to do, on purpose
414
+ end
415
+ else
416
+ raise UnknownNodeError, "Bug! Unknown type #{type.inspect} to #{self.class}"
417
+ end
418
+ end
419
+ result
420
+ end
421
+
422
+ def generate # :nodoc:
423
+ raise "not implemented yet"
424
+ end
425
+
426
+ ##
427
+ # Raises unless the Sexp type for +list+ matches +typ+
428
+
429
+ def assert_type(list, typ)
430
+ raise TypeError, "Expected type #{typ.inspect} in #{list.inspect}" if
431
+ list.first != typ
432
+ end
433
+
434
+ ##
435
+ # A fairly generic processor for a dummy node. Dummy nodes are used
436
+ # when your processor is doing a complicated rewrite that replaces
437
+ # the current sexp with multiple sexps.
438
+ #
439
+ # Bogus Example:
440
+ #
441
+ # def process_something(exp)
442
+ # return s(:dummy, process(exp), s(:extra, 42))
443
+ # end
444
+
445
+ def process_dummy(exp)
446
+ result = @expected.new(:dummy)
447
+ until exp.empty? do
448
+ result << self.process(exp.shift)
449
+ end
450
+ result
451
+ end
452
+ end
453
+