bijou 0.1.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 (53) hide show
  1. data/ChangeLog.txt +4 -0
  2. data/LICENSE.txt +58 -0
  3. data/README.txt +48 -0
  4. data/Rakefile +105 -0
  5. data/doc/INSTALL.rdoc +260 -0
  6. data/doc/README.rdoc +314 -0
  7. data/doc/releases/bijou-0.1.0.rdoc +60 -0
  8. data/examples/birthday/birthday.rb +34 -0
  9. data/examples/holiday/holiday.rb +61 -0
  10. data/examples/holiday/letterhead.txt +4 -0
  11. data/examples/holiday/signature.txt +9 -0
  12. data/examples/phishing/letter.txt +29 -0
  13. data/examples/phishing/letterhead.txt +4 -0
  14. data/examples/phishing/phishing.rb +21 -0
  15. data/examples/phishing/signature.txt +9 -0
  16. data/examples/profile/profile.rb +46 -0
  17. data/lib/bijou.rb +15 -0
  18. data/lib/bijou/backend.rb +542 -0
  19. data/lib/bijou/cgi/adapter.rb +201 -0
  20. data/lib/bijou/cgi/handler.rb +5 -0
  21. data/lib/bijou/cgi/request.rb +37 -0
  22. data/lib/bijou/common.rb +12 -0
  23. data/lib/bijou/component.rb +108 -0
  24. data/lib/bijou/config.rb +60 -0
  25. data/lib/bijou/console/adapter.rb +167 -0
  26. data/lib/bijou/console/handler.rb +4 -0
  27. data/lib/bijou/console/request.rb +26 -0
  28. data/lib/bijou/context.rb +431 -0
  29. data/lib/bijou/diagnostics.rb +87 -0
  30. data/lib/bijou/errorformatter.rb +322 -0
  31. data/lib/bijou/exception.rb +39 -0
  32. data/lib/bijou/filters.rb +107 -0
  33. data/lib/bijou/httprequest.rb +108 -0
  34. data/lib/bijou/httpresponse.rb +268 -0
  35. data/lib/bijou/lexer.rb +513 -0
  36. data/lib/bijou/minicgi.rb +159 -0
  37. data/lib/bijou/parser.rb +1026 -0
  38. data/lib/bijou/processor.rb +404 -0
  39. data/lib/bijou/prstringio.rb +400 -0
  40. data/lib/bijou/webrick/adapter.rb +174 -0
  41. data/lib/bijou/webrick/handler.rb +32 -0
  42. data/lib/bijou/webrick/request.rb +45 -0
  43. data/script/cgi.rb +25 -0
  44. data/script/console.rb +7 -0
  45. data/script/server.rb +7 -0
  46. data/test/t1.cfg +5 -0
  47. data/test/tc_config.rb +26 -0
  48. data/test/tc_filter.rb +25 -0
  49. data/test/tc_lexer.rb +120 -0
  50. data/test/tc_response.rb +103 -0
  51. data/test/tc_ruby.rb +62 -0
  52. data/test/tc_stack.rb +50 -0
  53. metadata +121 -0
@@ -0,0 +1,4 @@
1
+
2
+ require 'bijou/console/adapter'
3
+
4
+ Bijou::Console::Adapter.handle
@@ -0,0 +1,26 @@
1
+
2
+ require 'bijou/minicgi'
3
+ require 'bijou/httprequest'
4
+
5
+ module Bijou
6
+ module Console
7
+ #
8
+ # A very simple request object that takes a query string from the command
9
+ # line and feeds it to the component.
10
+ #
11
+ class Request < Bijou::HttpRequest
12
+ def initialize(qs)
13
+ super()
14
+
15
+ query_string = ::CGI::parse(qs) || {}
16
+
17
+ @query_string = Bijou::MiniCGI.singularize(query_string)
18
+
19
+ @form = @query_string.clone
20
+ @params = @query_string.clone
21
+
22
+ @http_method = 'GET'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,431 @@
1
+ #
2
+ # Copyright (c) 2007-2008 Todd Lucas. All rights reserved.
3
+ #
4
+ # context.rb - Contains Context and related classes.
5
+ #
6
+ require 'bijou/common'
7
+ require 'bijou/config'
8
+
9
+ module Bijou
10
+ # Represents a single component in a component/container chain.
11
+ class Frame
12
+ attr_reader :output
13
+ attr_reader :args
14
+ attr_reader :component
15
+
16
+ def initialize(component)
17
+ @component = component
18
+ @output = ''
19
+ @args = {}
20
+ end
21
+
22
+ def flush
23
+ s = @output
24
+ @output = ''
25
+ s
26
+ end
27
+ end
28
+
29
+ # Each comonent/container chain is represented by a Stack object. The stack
30
+ # class contains a list of Frame objects and an output buffer.
31
+ class Stack
32
+ def initialize()
33
+ @frame_index = 0
34
+ @frames = []
35
+ @buffer = ''
36
+ end
37
+
38
+ attr_reader :buffer
39
+ attr_reader :frames
40
+
41
+ def index
42
+ @frame_index
43
+ end
44
+
45
+ def to_s
46
+ s = ''
47
+ @frames.each {|f|
48
+ s << "'#{f.component}': "
49
+ s << "'#{f.output}' "
50
+ }
51
+ s << " -> '#{@buffer}'"
52
+ s
53
+ end
54
+
55
+ def push_frame(component)
56
+ if @frames.length > 0
57
+ @buffer << @frames[-1].flush
58
+ end
59
+
60
+ @frames.push(Frame.new(component))
61
+ end
62
+
63
+ def start
64
+ @frame_index = @frames.length - 1
65
+ end
66
+
67
+ def top_frame
68
+ @frames[@frame_index]
69
+ end
70
+
71
+ def next_frame
72
+ @frame_index -= 1
73
+ top_frame
74
+ end
75
+
76
+ def peek_frame(offset = 1)
77
+ @frames[@frame_index - offset]
78
+ end
79
+
80
+ def flush_frame()
81
+ @buffer << top_frame.flush
82
+ end
83
+
84
+ def pop_frame()
85
+ flush_frame()
86
+ # @buffer << self.output
87
+ @frames.pop()
88
+ end
89
+
90
+ def output
91
+ @frames[@frame_index].output
92
+ end
93
+
94
+ def flush
95
+ s = @buffer
96
+ @buffer = ''
97
+ s
98
+ end
99
+ end
100
+
101
+ # Represents execution state shared by the execution contexts.
102
+ class Environment
103
+ def initialize()
104
+ @component = nil
105
+ @trace_level = Log::None
106
+ @trace_buffer = true
107
+ @log = ''
108
+ @trace = ''
109
+ end
110
+
111
+ attr_accessor :component, :trace_level, :trace_buffer
112
+
113
+ # Logs with carriage return.
114
+ def log(level, str)
115
+ @log << str + "\n"
116
+ end
117
+
118
+ # Logs without carriage return.
119
+ def log_(level, str)
120
+ @log << str
121
+ end
122
+
123
+ def get_log
124
+ @log
125
+ end
126
+
127
+ # Trace with carriage return.
128
+ def trace(level, str)
129
+ if level <= @trace_level
130
+ if @trace_buffer
131
+ @trace << str + "\n"
132
+ else
133
+ puts str
134
+ end
135
+ end
136
+ end
137
+
138
+ # Trace without carriage return.
139
+ def trace_(level, str)
140
+ if level <= @trace_level
141
+ if @trace_buffer
142
+ @trace << str
143
+ else
144
+ print str
145
+ end
146
+ end
147
+ end
148
+
149
+ def get_trace
150
+ @trace
151
+ end
152
+ end
153
+
154
+ #
155
+ # When a Bijou file is loaded by the processor, each component that is
156
+ # encountered causes a Bijou::Component class to be instantiated in
157
+ # the context. This context can then be executed with a set of input
158
+ # arguments, causing each component to be evaluated and the output to be
159
+ # rendered. A context may be used to invoke the same arrangement of
160
+ # components any number of times with different arguments.
161
+ #
162
+ class Context
163
+ attr_accessor :component_callback
164
+ attr_accessor :container_callback
165
+ attr_reader :stack
166
+ attr_reader :config
167
+ attr_reader :environment
168
+
169
+ attr_accessor :cgi
170
+ attr_accessor :request
171
+ attr_accessor :response
172
+
173
+ def initialize(config, environment=nil)
174
+ if !environment
175
+ environment = Environment.new
176
+ environment.trace_level = config.trace_level
177
+ environment.trace_buffer = config.trace_buffer
178
+ end
179
+
180
+ @args = {}
181
+ @stack = Stack.new
182
+ @config = config
183
+ @environment = environment
184
+ @container_callback = nil
185
+ @component_callback = nil
186
+
187
+ @request = nil
188
+ @response = nil
189
+
190
+ @cgi = nil
191
+ end
192
+
193
+ # Called after clone
194
+ def reset()
195
+ @args = {}
196
+ @stack = Stack.new
197
+ end
198
+
199
+ def write(str)
200
+ @stack.output << str
201
+ end
202
+
203
+ def writeline(str)
204
+ @stack.output << str + "\n"
205
+ end
206
+
207
+ def output
208
+ @stack.buffer
209
+ end
210
+
211
+ def clear
212
+ @stack.flush
213
+ end
214
+
215
+ # Used to retrieve the last active frame in the event of an exception.
216
+ def source_filename
217
+ if @environment.component
218
+ @environment.component.class.source_filename
219
+ else
220
+ nil
221
+ end
222
+ end
223
+
224
+ # Used to retrieve the last active frame in the event of an exception.
225
+ def cache_filename
226
+ if @environment.component
227
+ @environment.component.class.cache_filename
228
+ else
229
+ nil
230
+ end
231
+ end
232
+
233
+ def add_component(component)
234
+ @stack.push_frame(component)
235
+
236
+ # If the owner registered a container load handler, invoke after the
237
+ # component has been added.
238
+ if component.class.container && @container_callback
239
+ @container_callback.call(self, component.class.container)
240
+ end
241
+ end
242
+
243
+ def render(args)
244
+ trace Bijou::Log::Info, "render init"
245
+
246
+ prev_component = @environment.component
247
+
248
+ # Init may render, after the headers but before the page text.
249
+ @stack.frames.each { |frame|
250
+ @environment.component = frame.component
251
+ frame.component.init(args)
252
+ }
253
+
254
+ @environment.component = prev_component
255
+
256
+ # Render the component chain to the response target.
257
+ render_component(args)
258
+
259
+ trace Bijou::Log::Info, "render fini"
260
+
261
+ # Fini may still render to the stream, at the end after the page text.
262
+ @stack.frames.each { |frame|
263
+ @environment.component = frame.component
264
+ frame.component.fini()
265
+ }
266
+
267
+ @environment.component = prev_component
268
+
269
+ return @output
270
+ end
271
+
272
+ # The render method renders the component chain.
273
+ def render_component(args)
274
+ if @stack.frames.length == 0
275
+ return
276
+ end
277
+
278
+ # Set top-level args so they are available to all components.
279
+ @args = args
280
+
281
+ # Start with the outer-most container at the end of the list.
282
+ @stack.start
283
+
284
+ prev_component = @environment.component
285
+
286
+ component = @stack.top_frame.component
287
+ @environment.component = component
288
+ component.render(@args)
289
+ @environment.component = prev_component
290
+
291
+ # This flushes the component's output to the stack's buffer.
292
+ @stack.flush_frame
293
+ end
294
+
295
+ # Container modules render their callers by calling the content method
296
+ # once at the location where the caller's output should be rendered.
297
+ def call_next(extra={})
298
+ if @stack.index == 0
299
+ raise 'next called too many times'
300
+ end
301
+
302
+ # Flush the first part of the component's output to the stack's buffer.
303
+ @stack.flush_frame
304
+
305
+ prev_component = @environment.component
306
+
307
+ # REVIEW: Argument merge and override is experimental.
308
+ # Render the child content.
309
+ component = @stack.next_frame.component
310
+ @environment.component = component
311
+ component.render(@args.merge(extra))
312
+ @environment.component = prev_component
313
+
314
+ # The remainder of the content will be flushed at the end of render.
315
+ # @stack.flush_frame
316
+ end
317
+
318
+ def try_fetch_next
319
+ if @stack.index == 0
320
+ nil
321
+ else
322
+ @stack.peek_frame
323
+ end
324
+ end
325
+
326
+ def fetch_next
327
+ try_fetch_next || raise('fetch_next cannot find next component')
328
+ end
329
+
330
+ def try_fetch_remainder
331
+ if @stack.index == 0
332
+ nil
333
+ else
334
+ @stack.frames[0, @stack.index].reverse
335
+ end
336
+ end
337
+
338
+ def fetch_remainder
339
+ try_fetch_remainder || raise('fetch_remainder cannot find next component')
340
+ end
341
+
342
+ # Used to invoke a method or a component. The output is rendered to a
343
+ # string, which is returned as the result. If no match is found returns nil.
344
+ def sinvoke(name, args)
345
+ # Does this look like a method? We don't include setters, etc. (?!=).
346
+ if name =~ /[_a-z][_a-z0-9]*/
347
+ prev_component = @environment.component
348
+
349
+ # Do any of the components support this method?
350
+ @stack.frames.each { |f|
351
+ if f.component.respond_to?(name)
352
+ @stack.flush_frame
353
+ @environment.component = f.component
354
+ m = f.component.method(name)
355
+ m.call(args)
356
+ @environment.component = prev_component
357
+ return @stack.top_frame.flush
358
+ end
359
+ }
360
+
361
+ @environment.component = prev_component
362
+ end
363
+
364
+ # Delegate to context owner
365
+ if @component_callback
366
+ # The subcontext wraps a new stack for the component, but shares
367
+ # common data. This allows us to encapsulate the rendered output
368
+ # into a separate buffer chain.
369
+ subcontext = self.clone
370
+ subcontext.reset
371
+
372
+ # TODO: Handle new context and buffer merging.
373
+ @component_callback.call(subcontext, name, args)
374
+
375
+ return subcontext.stack.flush
376
+ end
377
+
378
+ nil
379
+ end
380
+
381
+ # Used to invoke a method or a component. The output is rendered to the
382
+ # buffer. Returns true if a match was found.
383
+ def invoke(name, args)
384
+ if name == 'content'
385
+ # REVIEW: This is an experimental feature. An alternative (without
386
+ # argument support) is <%content>, but it is an unclosed tag.
387
+ return call_next(args)
388
+ end
389
+
390
+ if text = sinvoke(name, args)
391
+ write text
392
+ return true
393
+ end
394
+
395
+ false
396
+ end
397
+
398
+ def argument_exception(method, name)
399
+ # BUGBUG: We need to print the page (component) name.
400
+ raise "Expected argument '#{name}' to method '#{method}'"
401
+ end
402
+
403
+ # Logs with carriage return.
404
+ def log(level, str)
405
+ @environment.log(level, str)
406
+ end
407
+
408
+ # Logs without carriage return.
409
+ def log_(level, str)
410
+ @environment.log_(level, str)
411
+ end
412
+
413
+ def get_log()
414
+ @environment.get_log()
415
+ end
416
+
417
+ # Trace with carriage return.
418
+ def trace(level, str)
419
+ @environment.trace(level, str)
420
+ end
421
+
422
+ # Trace without carriage return.
423
+ def trace_(level, str)
424
+ @environment.trace_(level, str)
425
+ end
426
+
427
+ def get_trace()
428
+ @environment.get_trace()
429
+ end
430
+ end
431
+ end