nitro 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+