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.
- data/ChangeLog.txt +4 -0
- data/LICENSE.txt +58 -0
- data/README.txt +48 -0
- data/Rakefile +105 -0
- data/doc/INSTALL.rdoc +260 -0
- data/doc/README.rdoc +314 -0
- data/doc/releases/bijou-0.1.0.rdoc +60 -0
- data/examples/birthday/birthday.rb +34 -0
- data/examples/holiday/holiday.rb +61 -0
- data/examples/holiday/letterhead.txt +4 -0
- data/examples/holiday/signature.txt +9 -0
- data/examples/phishing/letter.txt +29 -0
- data/examples/phishing/letterhead.txt +4 -0
- data/examples/phishing/phishing.rb +21 -0
- data/examples/phishing/signature.txt +9 -0
- data/examples/profile/profile.rb +46 -0
- data/lib/bijou.rb +15 -0
- data/lib/bijou/backend.rb +542 -0
- data/lib/bijou/cgi/adapter.rb +201 -0
- data/lib/bijou/cgi/handler.rb +5 -0
- data/lib/bijou/cgi/request.rb +37 -0
- data/lib/bijou/common.rb +12 -0
- data/lib/bijou/component.rb +108 -0
- data/lib/bijou/config.rb +60 -0
- data/lib/bijou/console/adapter.rb +167 -0
- data/lib/bijou/console/handler.rb +4 -0
- data/lib/bijou/console/request.rb +26 -0
- data/lib/bijou/context.rb +431 -0
- data/lib/bijou/diagnostics.rb +87 -0
- data/lib/bijou/errorformatter.rb +322 -0
- data/lib/bijou/exception.rb +39 -0
- data/lib/bijou/filters.rb +107 -0
- data/lib/bijou/httprequest.rb +108 -0
- data/lib/bijou/httpresponse.rb +268 -0
- data/lib/bijou/lexer.rb +513 -0
- data/lib/bijou/minicgi.rb +159 -0
- data/lib/bijou/parser.rb +1026 -0
- data/lib/bijou/processor.rb +404 -0
- data/lib/bijou/prstringio.rb +400 -0
- data/lib/bijou/webrick/adapter.rb +174 -0
- data/lib/bijou/webrick/handler.rb +32 -0
- data/lib/bijou/webrick/request.rb +45 -0
- data/script/cgi.rb +25 -0
- data/script/console.rb +7 -0
- data/script/server.rb +7 -0
- data/test/t1.cfg +5 -0
- data/test/tc_config.rb +26 -0
- data/test/tc_filter.rb +25 -0
- data/test/tc_lexer.rb +120 -0
- data/test/tc_response.rb +103 -0
- data/test/tc_ruby.rb +62 -0
- data/test/tc_stack.rb +50 -0
- metadata +121 -0
@@ -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
|