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,29 @@
|
|
1
|
+
<%! container='letterhead.txt' %>
|
2
|
+
<%args>
|
3
|
+
name => 'to whom it may concern'
|
4
|
+
</%args>
|
5
|
+
|
6
|
+
Subject: With the complements of Mrs. former 1st lady
|
7
|
+
|
8
|
+
Dear <%= name %>,
|
9
|
+
|
10
|
+
Good day and compliments. I am writing you regarding an urgent matter. May I
|
11
|
+
crave your indulgence to open this discussion by a formal letter of this sort.
|
12
|
+
I am making this contact on behalf of my sister, wife of the late general and
|
13
|
+
former head of state of our republic.
|
14
|
+
|
15
|
+
Recently, due to unfortunate circumstances, we have had to consider the
|
16
|
+
option of moving her assets of One Hundred and Fifty-Three Millions United
|
17
|
+
States Dollars out of the country. We are seeking a business partner with
|
18
|
+
banking coordinates capable of accommodating such huge amounts. We have put
|
19
|
+
in place an instrument of payment for USD $<%= 30 + rand(15) %>M, which is
|
20
|
+
now in a dedicated account. If you agree to a business proposition, this amount
|
21
|
+
will be transferred also into your account as remuneration for your assistance
|
22
|
+
with our heartfelt thanks. I implore your consideration of this grave matter.
|
23
|
+
|
24
|
+
I await your prompt response.
|
25
|
+
|
26
|
+
<& signature.txt, signed => 'Solicitor R.U. Naeve' &>
|
27
|
+
|
28
|
+
DISCLAIMER: This mock phishing letter is for demonstration purposes only.
|
29
|
+
---- cut here ----
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
$:.push '../../lib'
|
3
|
+
|
4
|
+
require 'bijou'
|
5
|
+
|
6
|
+
processor = Bijou::Processor.new
|
7
|
+
|
8
|
+
context = processor.load('letter.txt');
|
9
|
+
|
10
|
+
people = [ 'David', 'Richard', 'Tiffany' ]
|
11
|
+
|
12
|
+
people.each { |who|
|
13
|
+
args = { 'name' => who }
|
14
|
+
|
15
|
+
context.render(args)
|
16
|
+
|
17
|
+
print context.output;
|
18
|
+
|
19
|
+
# We reuse the context, so clear the ouput buffers.
|
20
|
+
context.clear
|
21
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
$:.push '../..'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'ruby-prof'
|
6
|
+
require 'bijou/processor'
|
7
|
+
|
8
|
+
RubyProf.start
|
9
|
+
|
10
|
+
parser = Bijou::Parser.new
|
11
|
+
|
12
|
+
class_name = "BirthdayView"
|
13
|
+
|
14
|
+
how_many = 1000
|
15
|
+
|
16
|
+
how_many.times {
|
17
|
+
class_text = parser.parse(class_name, <<EOS)
|
18
|
+
<%args>
|
19
|
+
name => 'to whom it may concern'
|
20
|
+
</%args>
|
21
|
+
|
22
|
+
Dear <%= name %>,
|
23
|
+
I\'m writing this letter today to send my best wishes for a happy birthday.
|
24
|
+
|
25
|
+
Sincerely,
|
26
|
+
Your boss
|
27
|
+
|
28
|
+
---- cut here ----
|
29
|
+
EOS
|
30
|
+
|
31
|
+
people = [ 'David' ]
|
32
|
+
|
33
|
+
people.each { |who|
|
34
|
+
args = { 'name' => who }
|
35
|
+
|
36
|
+
context = Bijou::Context.new(Bijou::Config.new)
|
37
|
+
|
38
|
+
letter = Bijou::Processor.execute(context, class_text, class_name, args)
|
39
|
+
|
40
|
+
# print letter
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
result = RubyProf.stop
|
45
|
+
printer = RubyProf::GraphHtmlPrinter.new(result)
|
46
|
+
printer.print(STDOUT, 0)
|
data/lib/bijou.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Bijou - A web page templating framework for Ruby.
|
4
|
+
#
|
5
|
+
# Copyright (c) 2007-2008 Todd Lucas. All rights reserved.
|
6
|
+
#
|
7
|
+
# Author: Todd Lucas <tl@dogandponyshow.org>
|
8
|
+
#
|
9
|
+
#++
|
10
|
+
#
|
11
|
+
# :include: doc/README.rdoc
|
12
|
+
# :title: Bijou - Web templates for Ruby
|
13
|
+
#
|
14
|
+
require 'bijou/component'
|
15
|
+
require 'bijou/processor'
|
@@ -0,0 +1,542 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2007-2008 Todd Lucas. All rights reserved.
|
3
|
+
#
|
4
|
+
# backend.rb - Parser backend code generator
|
5
|
+
#
|
6
|
+
require 'bijou/common'
|
7
|
+
require 'bijou/filters'
|
8
|
+
|
9
|
+
module Bijou
|
10
|
+
module Parse
|
11
|
+
|
12
|
+
#
|
13
|
+
# A parser backend component for rendering the %args collection.
|
14
|
+
#
|
15
|
+
class ArgumentCollection
|
16
|
+
def initialize()
|
17
|
+
super()
|
18
|
+
@args = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :args
|
22
|
+
|
23
|
+
def add_argument(name, expression, filename, line)
|
24
|
+
# TODO: Args should preserver order; change to array.
|
25
|
+
@args[name] = [ expression, filename, line ]
|
26
|
+
end
|
27
|
+
|
28
|
+
def render_args(method, use_markers)
|
29
|
+
result = ''
|
30
|
+
|
31
|
+
@args.each { |key, value|
|
32
|
+
expression = value[0]
|
33
|
+
filename = value[1]
|
34
|
+
line = value[2]
|
35
|
+
|
36
|
+
# REVIEW: This is a bit verbose.
|
37
|
+
if expression
|
38
|
+
result << "#line #{line} #{filename}\n" if use_markers
|
39
|
+
result << " #{key} = args.has_key?('#{key}') ? args['#{key}']" +
|
40
|
+
" : #{expression}\n"
|
41
|
+
else
|
42
|
+
result << "#line #{line} #{filename}\n" if use_markers
|
43
|
+
result << " #{key} = args.has_key?('#{key}') ? args['#{key}']" +
|
44
|
+
" : @context.argument_exception('#{method}', '#{key}')\n"
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
return result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# The base class for the backend code generator. Used to render a component
|
54
|
+
# or a piece of a component.
|
55
|
+
#
|
56
|
+
class Target
|
57
|
+
def initialize
|
58
|
+
@output = ''
|
59
|
+
@args = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_accessor :args
|
63
|
+
|
64
|
+
# Escape text for embedding in a single-quoted string.
|
65
|
+
def escape_single(text)
|
66
|
+
# We use the block because \' has special meaning in substitution strings.
|
67
|
+
return text.gsub(/[\'\\]/) { |ch| "\\" + ch }
|
68
|
+
end
|
69
|
+
private :escape_single
|
70
|
+
|
71
|
+
def render_part(text) # print 'text'
|
72
|
+
if text.length > 0
|
73
|
+
text = escape_single(text)
|
74
|
+
@output << " print '#{text}'\n"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def render_line(text) # puts 'text'
|
79
|
+
if text.length > 0
|
80
|
+
text = escape_single(text)
|
81
|
+
@output << " puts '#{text}'\n"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def render_expr(expr) # print expr
|
86
|
+
if expr.length > 0
|
87
|
+
@output << " print #{expr}\n"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def render_code_(code) # code
|
92
|
+
@output << "#{code}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def render_code(code) # code
|
96
|
+
@output << "#{code}\n"
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Render a Perl-oriented line marker comment , for use in generating stack
|
101
|
+
# traces at runtime. Any line information returned with an Exception object
|
102
|
+
# will correspond to the source code that was generated from the component.
|
103
|
+
# This line marker comment will allow the runtime to find the corresponding
|
104
|
+
# line in the component's markup text.
|
105
|
+
#
|
106
|
+
def render_marker(line, filename)
|
107
|
+
render_code("#line #{line} #{filename}")
|
108
|
+
end
|
109
|
+
|
110
|
+
def render_args(method, use_markers)
|
111
|
+
result = ''
|
112
|
+
if @args
|
113
|
+
result << @args.render_args(method, use_markers)
|
114
|
+
end
|
115
|
+
return result
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
class Def < Target
|
120
|
+
def initialize(name)
|
121
|
+
super()
|
122
|
+
@name = name
|
123
|
+
end
|
124
|
+
|
125
|
+
attr_reader :name
|
126
|
+
|
127
|
+
def renderMethod
|
128
|
+
result = ''
|
129
|
+
|
130
|
+
result << " def #{@name}\n"
|
131
|
+
result << @output
|
132
|
+
result << " end\n"
|
133
|
+
|
134
|
+
return result
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# The class for the backend code generator that is used to render the
|
140
|
+
# %method block. It may contain its own %args block.
|
141
|
+
#
|
142
|
+
class Method < Def
|
143
|
+
def initialize(name)
|
144
|
+
super(name)
|
145
|
+
end
|
146
|
+
|
147
|
+
def render_method(use_markers)
|
148
|
+
result = ''
|
149
|
+
|
150
|
+
if @name == 'fini'
|
151
|
+
result << " def #{@name}()\n"
|
152
|
+
else
|
153
|
+
result << " def #{@name}(args)\n"
|
154
|
+
end
|
155
|
+
|
156
|
+
if @name == 'init'
|
157
|
+
# For init, call super before derived code.
|
158
|
+
result << " super\n"
|
159
|
+
end
|
160
|
+
|
161
|
+
result << render_args(@name, use_markers)
|
162
|
+
result << @output
|
163
|
+
|
164
|
+
if @name == 'fini'
|
165
|
+
# For fini, call super after derived code.
|
166
|
+
result << " super\n"
|
167
|
+
end
|
168
|
+
result << " end\n"
|
169
|
+
|
170
|
+
return result
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# The class for the backend code generator that is used to render an entire
|
177
|
+
# component. It may contain a top-level %args block. This %args block is
|
178
|
+
# replicated for the %init block.
|
179
|
+
#
|
180
|
+
class Component < Target
|
181
|
+
def initialize(use_markers)
|
182
|
+
@name = ''
|
183
|
+
@componentBase = 'Bijou::Component'
|
184
|
+
@componentName = ''
|
185
|
+
@output = ''
|
186
|
+
@defs = []
|
187
|
+
@directives = {}
|
188
|
+
@source_filename = nil
|
189
|
+
@cache_filename = nil
|
190
|
+
@use_markers = use_markers
|
191
|
+
end
|
192
|
+
|
193
|
+
attr_accessor :directives, :source_filename, :cache_filename
|
194
|
+
|
195
|
+
def add_method(d)
|
196
|
+
@defs.push(d)
|
197
|
+
end
|
198
|
+
|
199
|
+
def component=(componentName)
|
200
|
+
@componentName = componentName
|
201
|
+
end
|
202
|
+
|
203
|
+
def component_base=(componentBase)
|
204
|
+
@componentBase = componentBase
|
205
|
+
end
|
206
|
+
|
207
|
+
def require_list=(list)
|
208
|
+
@requireList = list
|
209
|
+
end
|
210
|
+
|
211
|
+
def to_s
|
212
|
+
result = ''
|
213
|
+
|
214
|
+
if @directives.has_key?('base')
|
215
|
+
@componentBase = @directives['base']
|
216
|
+
end
|
217
|
+
|
218
|
+
if @args
|
219
|
+
@defs.each {|d|
|
220
|
+
if d.name == 'init'
|
221
|
+
# NOTE: The init block has the same argument expansion as render.
|
222
|
+
d.args = @args.clone
|
223
|
+
break
|
224
|
+
end
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
result << "require 'bijou/component'\n"
|
229
|
+
if @requireList
|
230
|
+
@requireList.each {|path|
|
231
|
+
result << "require '#{path}'\n"
|
232
|
+
}
|
233
|
+
end
|
234
|
+
result << "class #{@componentName} < #{@componentBase}\n"
|
235
|
+
if @directives.has_key?('container')
|
236
|
+
result << " def self.container\n"
|
237
|
+
result << " '#{@directives['container']}'\n"
|
238
|
+
result << " end\n"
|
239
|
+
end
|
240
|
+
|
241
|
+
# REVIEW: Do we need to escape? The filenames appear to always use
|
242
|
+
# forward slashes.
|
243
|
+
if @source_filename
|
244
|
+
result << " def self.source_filename\n"
|
245
|
+
result << " '#{@source_filename}'\n"
|
246
|
+
result << " end\n"
|
247
|
+
end
|
248
|
+
|
249
|
+
if @cache_filename
|
250
|
+
result << " def self.cache_filename\n"
|
251
|
+
result << " '#{@cache_filename}'\n"
|
252
|
+
result << " end\n"
|
253
|
+
end
|
254
|
+
|
255
|
+
result << " def render(args)\n"
|
256
|
+
result << render_args('render', @use_markers)
|
257
|
+
result << @output;
|
258
|
+
result << " end\n"
|
259
|
+
|
260
|
+
@defs.each {|d|
|
261
|
+
result << d.render_method(@use_markers)
|
262
|
+
}
|
263
|
+
|
264
|
+
result << "end\n"
|
265
|
+
|
266
|
+
return result
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
#
|
271
|
+
# The backend is used by the parser to generate a Ruby representation of a
|
272
|
+
# Bijou component.
|
273
|
+
#
|
274
|
+
class Backend
|
275
|
+
class ParseContext
|
276
|
+
Normal = 1
|
277
|
+
MethodTag = 2
|
278
|
+
end
|
279
|
+
|
280
|
+
def initialize(diagnostics, trace, use_markers)
|
281
|
+
@trace = trace
|
282
|
+
@use_markers = use_markers
|
283
|
+
|
284
|
+
@buffer = ''
|
285
|
+
@parseContext = ParseContext::Normal
|
286
|
+
|
287
|
+
@diagnostics = diagnostics
|
288
|
+
|
289
|
+
@currentComponent = @component = Bijou::Parse::Component.new(use_markers)
|
290
|
+
@argsCollection = nil
|
291
|
+
@isDefContext = false
|
292
|
+
|
293
|
+
# TODO: Make these installable.
|
294
|
+
@filters = {
|
295
|
+
'u' => Bijou::EncodeURL.new,
|
296
|
+
'h' => Bijou::EncodeHTML.new,
|
297
|
+
'a' => Bijou::EncodeAttributeValue.new,
|
298
|
+
't' => Bijou::EncodeTrim.new,
|
299
|
+
'w' => Bijou::EncodeWiki.new,
|
300
|
+
}
|
301
|
+
end
|
302
|
+
|
303
|
+
# Used to accumulate raw text from the parser.
|
304
|
+
def buffer(s)
|
305
|
+
@buffer << s
|
306
|
+
end
|
307
|
+
|
308
|
+
# Used to flush the buffer to the output stream with a newline
|
309
|
+
def puts_buffer()
|
310
|
+
if @isDefContext
|
311
|
+
@currentComponent.render_code(@buffer)
|
312
|
+
else
|
313
|
+
@currentComponent.render_line(@buffer)
|
314
|
+
end
|
315
|
+
|
316
|
+
@buffer = ''
|
317
|
+
end
|
318
|
+
|
319
|
+
# Flushes the buffer to the output stream without a newline. Often used
|
320
|
+
# before an inline tag <% ... %>.
|
321
|
+
def print_buffer()
|
322
|
+
if @isDefContext
|
323
|
+
@currentComponent.render_code_(@buffer)
|
324
|
+
else
|
325
|
+
@currentComponent.render_part(@buffer)
|
326
|
+
end
|
327
|
+
|
328
|
+
@buffer = ''
|
329
|
+
end
|
330
|
+
|
331
|
+
def message(s, line=nil, column=nil)
|
332
|
+
m = Bijou::Parse::Message.new
|
333
|
+
m.at(line, column)
|
334
|
+
m << s
|
335
|
+
@diagnostics.add_message(m)
|
336
|
+
end
|
337
|
+
|
338
|
+
def warning(s, line=nil, column=nil)
|
339
|
+
m = Bijou::Parse::Warning.new
|
340
|
+
m.at(line, column)
|
341
|
+
m << s
|
342
|
+
@diagnostics.add_warning(m)
|
343
|
+
end
|
344
|
+
|
345
|
+
def error(s, line=nil, column=nil)
|
346
|
+
m = Bijou::Parse::Error.new
|
347
|
+
m.at(line, column)
|
348
|
+
m << s
|
349
|
+
@diagnostics.add_error(m)
|
350
|
+
end
|
351
|
+
|
352
|
+
def trace(s)
|
353
|
+
puts s if @trace
|
354
|
+
end
|
355
|
+
|
356
|
+
#--
|
357
|
+
#
|
358
|
+
# Parser methods
|
359
|
+
#
|
360
|
+
#++
|
361
|
+
|
362
|
+
def render(component, source_filename=nil, cache_filename=nil,
|
363
|
+
component_base=nil, require_list=nil)
|
364
|
+
@component.component = component
|
365
|
+
@component.source_filename = source_filename
|
366
|
+
@component.cache_filename = cache_filename
|
367
|
+
if component_base
|
368
|
+
@component.component_base = component_base
|
369
|
+
end
|
370
|
+
if require_list
|
371
|
+
@component.require_list = require_list
|
372
|
+
end
|
373
|
+
@component.to_s
|
374
|
+
end
|
375
|
+
|
376
|
+
def markup_section(buffer, filename, line)
|
377
|
+
@currentComponent.render_marker(line, filename) if @use_markers
|
378
|
+
|
379
|
+
if @isDefContext
|
380
|
+
@currentComponent.render_code(buffer)
|
381
|
+
else
|
382
|
+
@currentComponent.render_part(buffer)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def tag_open(tagStart)
|
387
|
+
print_buffer
|
388
|
+
|
389
|
+
trace "\nopentag: #{tagStart}"
|
390
|
+
end
|
391
|
+
|
392
|
+
def named_start_tag(prefix, name, line, column)
|
393
|
+
case prefix
|
394
|
+
when 'method'
|
395
|
+
if @parseContext != ParseContext::Normal
|
396
|
+
error("method declared at nested scope", line, column)
|
397
|
+
return
|
398
|
+
end
|
399
|
+
|
400
|
+
@parseContext = ParseContext::MethodTag
|
401
|
+
@currentComponent = Bijou::Parse::Method.new(name)
|
402
|
+
@isDefContext = false
|
403
|
+
|
404
|
+
when 'init'
|
405
|
+
if @parseContext != ParseContext::Normal
|
406
|
+
error("init declared at nested scope", line, column)
|
407
|
+
return
|
408
|
+
end
|
409
|
+
|
410
|
+
@parseContext = ParseContext::MethodTag
|
411
|
+
@currentComponent = Bijou::Parse::Method.new(prefix)
|
412
|
+
@isDefContext = true
|
413
|
+
|
414
|
+
when 'fini'
|
415
|
+
if @parseContext != ParseContext::Normal
|
416
|
+
error("fini declared at nested scope", line, column)
|
417
|
+
return
|
418
|
+
end
|
419
|
+
|
420
|
+
@parseContext = ParseContext::MethodTag
|
421
|
+
@currentComponent = Bijou::Parse::Method.new(prefix)
|
422
|
+
@isDefContext = true
|
423
|
+
|
424
|
+
when 'args'
|
425
|
+
if @argsCollection
|
426
|
+
error("args may not be declared within another args section.", line, column)
|
427
|
+
return
|
428
|
+
end
|
429
|
+
|
430
|
+
# NOTE: We don't change parse context.
|
431
|
+
@argsCollection = Bijou::Parse::ArgumentCollection.new
|
432
|
+
|
433
|
+
if @currentComponent.args
|
434
|
+
warning("args section overrides previous args section.", line, column)
|
435
|
+
end
|
436
|
+
|
437
|
+
@currentComponent.args = @argsCollection
|
438
|
+
else
|
439
|
+
raise "unexpected named tag #{prefix}"
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
def named_end_tag(prefix)
|
444
|
+
if @argsCollection
|
445
|
+
@argsCollection = nil;
|
446
|
+
return
|
447
|
+
end
|
448
|
+
|
449
|
+
case @parseContext
|
450
|
+
when ParseContext::MethodTag;
|
451
|
+
@component.add_method(@currentComponent)
|
452
|
+
@currentComponent = @component
|
453
|
+
|
454
|
+
@parseContext = ParseContext::Normal
|
455
|
+
@isDefContext = false
|
456
|
+
when 'args'
|
457
|
+
if !@argsCollection
|
458
|
+
error("unexpected args end tag", line, column)
|
459
|
+
end
|
460
|
+
|
461
|
+
@argsCollection = nil
|
462
|
+
else
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
def inline_tag(text, filename, line)
|
467
|
+
@currentComponent.render_marker(line, filename) if @use_markers
|
468
|
+
@currentComponent.render_code(text)
|
469
|
+
end
|
470
|
+
|
471
|
+
def output_tag(text, filters, filename, line)
|
472
|
+
filtered = "(#{text}).to_s"
|
473
|
+
|
474
|
+
if filters
|
475
|
+
filters.each{|filter|
|
476
|
+
if @filters.has_key? filter
|
477
|
+
filtered = @filters[filter].render(filtered)
|
478
|
+
else
|
479
|
+
warning("unrecognized output tag filter '#{filter}'", filename, line)
|
480
|
+
end
|
481
|
+
}
|
482
|
+
end
|
483
|
+
|
484
|
+
@currentComponent.render_marker(line, filename) if @use_markers
|
485
|
+
@currentComponent.render_expr(filtered)
|
486
|
+
end
|
487
|
+
|
488
|
+
def call_tag(identifier, args, indirect, filename, line)
|
489
|
+
# Convert the argument list [[arg1, val1], [arg2, val2], ...] into
|
490
|
+
# a list of formatted string values.
|
491
|
+
list = []
|
492
|
+
|
493
|
+
args.each {|item|
|
494
|
+
list.push "'#{item[0]}' => #{item[1]}"
|
495
|
+
}
|
496
|
+
|
497
|
+
# Join the list of strings into a formatted hash.
|
498
|
+
text = "{#{list.join(', ')}}"
|
499
|
+
|
500
|
+
@currentComponent.render_marker(line, filename) if @use_markers
|
501
|
+
|
502
|
+
if indirect
|
503
|
+
# REVIEW: The indirection method currently shares the arguments with the
|
504
|
+
# method to be invoked. This may not be desirable. We would need an
|
505
|
+
# different call syntax to do otherwise. If different arguments are
|
506
|
+
# required, invoke may be called directly in a code block.
|
507
|
+
@currentComponent.render_code(" @context.invoke(#{identifier}(#{text}), #{text})")
|
508
|
+
else
|
509
|
+
@currentComponent.render_code(" @context.invoke('#{identifier}', #{text})")
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def directive_tag(hash, line, column)
|
514
|
+
trace "\nDirectives:"
|
515
|
+
hash.each { |key, value|
|
516
|
+
trace "#{key} => #{value}"
|
517
|
+
|
518
|
+
if value =~ /^[\'\"](.*)[\'\"]$/
|
519
|
+
value = $1
|
520
|
+
end
|
521
|
+
|
522
|
+
if @component.directives.has_key?(key)
|
523
|
+
warning("directive '#{key}' overrides previous definition", line, column)
|
524
|
+
end
|
525
|
+
@component.directives[key] = value
|
526
|
+
}
|
527
|
+
end
|
528
|
+
|
529
|
+
def add_argument(argName, argValue, filename, line, column)
|
530
|
+
if @argsCollection
|
531
|
+
if @argsCollection.args.has_key?(argName)
|
532
|
+
error("argument '#{argName}' overrides previous delcaration", line, column)
|
533
|
+
end
|
534
|
+
@argsCollection.add_argument(argName, argValue, filename, line)
|
535
|
+
else
|
536
|
+
error("unexpected argument", line, column)
|
537
|
+
end
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
end # module Parse
|
542
|
+
end # module Bijou
|