PageTemplate 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changes +20 -0
- data/Rakefile +6 -1
- data/lib/PageTemplate.rb +1 -1
- data/lib/PageTemplate/case.rb +21 -18
- data/lib/PageTemplate/commands.rb +131 -131
- data/lib/PageTemplate/htglossary.rb +70 -0
- data/lib/PageTemplate/parser.rb +228 -80
- data/test.rb +175 -113
- metadata +3 -2
data/Changes
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
2.1
|
2
|
+
Commands:
|
3
|
+
Changed: IfCommand now accepts elseif, elsif, else if.
|
4
|
+
Changed: LoopCommand accepts multiple iterators.
|
5
|
+
Changed: Commands don't accept 'commandname arguments' syntax
|
6
|
+
Changed: Commands take arguments they want: called_as, value, iterators
|
7
|
+
Changed: Commands no longer recieve parsers on initialize
|
8
|
+
Changed: Commands can access parser via namespace.parser
|
9
|
+
Removed: modifies? and closes?
|
10
|
+
Added: @modifier = sym, @closer = sym. These are syms for the
|
11
|
+
SyntaxGlossary's lookups for else, when, end, etc.
|
12
|
+
Parser:
|
13
|
+
Added: NamespaceItem module
|
14
|
+
Changed: Namespace is now just an includer of NamespaceItem
|
15
|
+
Changed: Parser and Templare are now both NamespaceItems
|
16
|
+
Changed: Namespace#get returns an error message instead of erroring.
|
17
|
+
Changed: obj.accessor now also has obj.arrayindex and obj.hashkey
|
18
|
+
Changed: SyntaxGlossary is now a singleton, and is more flexible.
|
19
|
+
Added: htglossary.rb, providing an HTML::Template HTGlossary
|
20
|
+
Fixed: Parser#load() accepts full path names now
|
1
21
|
2.0
|
2
22
|
Major changes:
|
3
23
|
Entire code refactored and rewritten.
|
data/Rakefile
CHANGED
@@ -39,6 +39,11 @@ task :install do
|
|
39
39
|
ruby %{setup.rb}
|
40
40
|
end
|
41
41
|
|
42
|
+
task :uninstall do
|
43
|
+
puts "This is only set up for Brian's machine"
|
44
|
+
sh %{rm -rf /usr/local/lib/ruby/site_ruby/1.8/PageTemplate}
|
45
|
+
end
|
46
|
+
|
42
47
|
task :doc do
|
43
48
|
sh %{rdoc lib/PageTemplate.rb README.txt lib/PageTemplate/*.rb}
|
44
49
|
end
|
@@ -49,7 +54,7 @@ end
|
|
49
54
|
|
50
55
|
spec = Gem::Specification.new do |s|
|
51
56
|
s.name = "PageTemplate"
|
52
|
-
s.version = "2.
|
57
|
+
s.version = "2.1.0"
|
53
58
|
s.author = "Brian Wisti"
|
54
59
|
s.email = "brianwisti@rubyforge.org"
|
55
60
|
s.homepage = "http://coolnamehere.com/products/pagetemplate"
|
data/lib/PageTemplate.rb
CHANGED
@@ -20,8 +20,8 @@
|
|
20
20
|
|
21
21
|
############################################################
|
22
22
|
|
23
|
-
require 'PageTemplate/commands'
|
24
23
|
require 'PageTemplate/parser'
|
24
|
+
require 'PageTemplate/commands'
|
25
25
|
|
26
26
|
# PageTemplate is just the namespace for all of its real code, so as
|
27
27
|
# not to caues confusion or clashes with the programmer's code.
|
data/lib/PageTemplate/case.rb
CHANGED
@@ -14,15 +14,16 @@ class PageTemplate
|
|
14
14
|
# [% else %]
|
15
15
|
# [% end %]
|
16
16
|
class CaseCommand < StackableCommand
|
17
|
-
|
17
|
+
@modifier = :when
|
18
|
+
@closer = :end
|
18
19
|
|
20
|
+
attr_reader :current_case
|
19
21
|
# +command+ is a variable that is evaluated against the namespace
|
20
22
|
# on execution, and then tested against the literals of when. It
|
21
23
|
# must return a string literal.
|
22
|
-
def initialize(
|
23
|
-
/^case ((\w+)(\.\w+)*)$/i =~ command
|
24
|
+
def initialize(value)
|
24
25
|
@called_as = 'case'
|
25
|
-
@value =
|
26
|
+
@value = value
|
26
27
|
@blocks = {}
|
27
28
|
@current_case = nil
|
28
29
|
@default = BlockCommand.new
|
@@ -36,23 +37,18 @@ class PageTemplate
|
|
36
37
|
end
|
37
38
|
end
|
38
39
|
# 'when' and 'else' modify this command.
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@current_case = nil
|
47
|
-
true
|
48
|
-
else
|
49
|
-
false
|
50
|
-
end
|
40
|
+
def when(value)
|
41
|
+
@current_case = value
|
42
|
+
@blocks[value] = BlockCommand.new unless @blocks.has_key?(value)
|
43
|
+
end
|
44
|
+
def else
|
45
|
+
@current_case = nil
|
46
|
+
return true
|
51
47
|
end
|
52
48
|
# If namespace.get(@value) exists in the 'when' clauses, then
|
53
49
|
# print out that block.
|
54
50
|
def output(namespace=nil)
|
55
|
-
val = namespace.get(@value)
|
51
|
+
val = namespace.get(@value,false)
|
56
52
|
if @blocks.has_key?(val)
|
57
53
|
@blocks[val].output(namespace)
|
58
54
|
else
|
@@ -68,5 +64,12 @@ class PageTemplate
|
|
68
64
|
str << " ]"
|
69
65
|
end
|
70
66
|
end
|
71
|
-
|
67
|
+
DefaultGlossary.define(/^case (\w+(?:\.\w+)*)$/) { |match,parser|
|
68
|
+
CaseCommand.new(match[1])
|
69
|
+
}
|
70
|
+
if PageTemplate.constants.include? 'HTGlossary'
|
71
|
+
HTGlossary.define(/^tmpl_case (?:NAME=)?(\w+(?:\.\w+\?)*)$/) { |m,parser|
|
72
|
+
CaseCommand.new(m[1])
|
73
|
+
}
|
74
|
+
end
|
72
75
|
end
|
@@ -13,6 +13,10 @@ class PageTemplate
|
|
13
13
|
# between each class. Command provides an abstract base class to show
|
14
14
|
# interface.
|
15
15
|
class Command
|
16
|
+
attr_reader :called_as
|
17
|
+
class << self
|
18
|
+
attr_reader :modifier, :closer
|
19
|
+
end
|
16
20
|
|
17
21
|
# Subclasses of Command use the output method to generate their text
|
18
22
|
# output. +namespace+ is a Namespace object, which may be
|
@@ -58,27 +62,18 @@ class PageTemplate
|
|
58
62
|
# If the command that UnknownCommand is set to exists, find out if
|
59
63
|
# the command exists in the Parser's glossary.
|
60
64
|
def output(namespace)
|
61
|
-
cls =
|
62
|
-
case
|
63
|
-
when
|
65
|
+
cls = namespace.parser.glossary.lookup(@command)
|
66
|
+
case cls
|
67
|
+
when UnknownCommand
|
64
68
|
"[ Unknown Command: #{@command} ]"
|
65
|
-
when cls.is_a?(Class)
|
66
|
-
if cls.instance_method(:initialize).arity == 2
|
67
|
-
cls.new(@command,@parser).output(namespace)
|
68
|
-
else
|
69
|
-
cls.new(@command).output(namespace)
|
70
|
-
end
|
71
69
|
else
|
72
|
-
|
73
|
-
p.set_command @command
|
74
|
-
p.output(namespace)
|
70
|
+
cls.output(namespace)
|
75
71
|
end
|
76
72
|
end
|
77
73
|
# Save the +command+ and the +parser+, so we can look it
|
78
74
|
# up and hopefully
|
79
|
-
def initialize(command
|
75
|
+
def initialize(command)
|
80
76
|
@command = command
|
81
|
-
@parser = parser
|
82
77
|
end
|
83
78
|
def to_s
|
84
79
|
"[ UnknownCommand: #{@command} ]"
|
@@ -92,28 +87,21 @@ class PageTemplate
|
|
92
87
|
# StackableCommand. Also recommended is setting @called_as, so
|
93
88
|
# all of StackableCommand's ways of closing blocks work.
|
94
89
|
class StackableCommand < Command
|
90
|
+
@modifier = nil
|
91
|
+
@closer = :end
|
92
|
+
|
93
|
+
def end
|
94
|
+
end
|
95
|
+
|
95
96
|
# @called_as is set to the command which this is called as. This
|
96
97
|
# allows a number of [% end %], [% end name %], [% endname %] or
|
97
98
|
# even [% /name %] to close StackableCommands.
|
98
|
-
def initialize(
|
99
|
-
@called_as = 'stackable'
|
99
|
+
def initialize(called_as=nil)
|
100
100
|
raise ArgumentError, 'StackableCommand should not be called directly'
|
101
101
|
end
|
102
102
|
def add(block)
|
103
103
|
raise ArgumentError, 'StackableCommand should not be called directly'
|
104
104
|
end
|
105
|
-
# Does +command+ modify this StackableCommand? If this is true,
|
106
|
-
# PageTemplate will not lookup +command+, and instead continue on
|
107
|
-
# to what it next finds, it will be ignored and skipped by the
|
108
|
-
# PageTemplate compiler. It is up to the child to make the
|
109
|
-
# modifications caused by +command+.
|
110
|
-
def modifies?(command)
|
111
|
-
false
|
112
|
-
end
|
113
|
-
# Does +command+ close this StackableCommand? If
|
114
|
-
def closes?(command)
|
115
|
-
/^(end|\/)\s*(#{@called_as})?$/ =~ command
|
116
|
-
end
|
117
105
|
def to_s
|
118
106
|
"[ #{@called_as} ]"
|
119
107
|
end
|
@@ -164,52 +152,43 @@ class PageTemplate
|
|
164
152
|
# Template should only be called by the Parser, and never
|
165
153
|
# by anything else.
|
166
154
|
class Template < BlockCommand
|
155
|
+
@modifier = nil
|
156
|
+
@closer = nil
|
167
157
|
# Template must know about the parser so it can access its
|
168
158
|
# namespace.
|
169
159
|
def initialize(parser)
|
170
160
|
@parser = parser
|
171
|
-
@namespace = Namespace.new
|
172
161
|
super()
|
173
162
|
end
|
174
163
|
def to_s
|
175
164
|
'[ Template: ' + @commandBlock.map{ |i| "[#{i.to_s}]" }.join(' ') + ']'
|
176
165
|
end
|
166
|
+
|
167
|
+
# Template is also a NamespaceItem
|
168
|
+
include NamespaceItem
|
169
|
+
|
177
170
|
# Template#output is a special case for a Command. Because
|
178
171
|
# Template is what's returned by Parser.load() or Parser.parse(),
|
179
172
|
# the programmer may well call Template#output(anything).
|
180
173
|
#
|
181
174
|
# If +object+ is a Namespace, then treat output as a typical
|
182
175
|
# BlockCommand. If +object+ is nil, then namespace is
|
183
|
-
# @parser.
|
176
|
+
# @parser. Otherwise, a new namespace is created, a
|
184
177
|
# child of @namespace, and is assigned +object+ as its namespace
|
185
178
|
def output(object=nil)
|
186
|
-
@
|
179
|
+
@parent = @parser
|
187
180
|
case
|
188
|
-
when object.is_a?(Namespace)
|
189
|
-
@namespace.parent = object
|
190
|
-
super(@namespace)
|
191
181
|
when object.nil?
|
192
|
-
super(
|
182
|
+
super(self)
|
183
|
+
when object.is_a?(NamespaceItem)
|
184
|
+
@parent = object
|
185
|
+
super(self)
|
193
186
|
else
|
194
|
-
namespace = Namespace.new(
|
187
|
+
namespace = Namespace.new(self)
|
195
188
|
namespace.object = object
|
196
189
|
super(namespace)
|
197
190
|
end
|
198
191
|
end
|
199
|
-
def [](key)
|
200
|
-
@namespace[key]
|
201
|
-
end
|
202
|
-
def []=(key,val)
|
203
|
-
@namespace[key] = val
|
204
|
-
end
|
205
|
-
# This is used as a top-level in the stack, so is always false.
|
206
|
-
def modifies?(cmd)
|
207
|
-
false
|
208
|
-
end
|
209
|
-
# This is used as a top-level in the stack, so is always false.
|
210
|
-
def closes?(cmd)
|
211
|
-
false
|
212
|
-
end
|
213
192
|
end
|
214
193
|
|
215
194
|
# A very simple Command which outputs a static string of text
|
@@ -248,23 +227,20 @@ class PageTemplate
|
|
248
227
|
# to be inserted during output. +parser+ is the Parser object,
|
249
228
|
# which we save so we can access its Preprocessor and its default
|
250
229
|
# preprocessor
|
251
|
-
def initialize(
|
252
|
-
@
|
253
|
-
|
254
|
-
@value = $1
|
255
|
-
@processor = $2
|
256
|
-
else
|
257
|
-
raise ArgumentError, "ValueCommand.new('#{cmd}') - invalid syntax"
|
258
|
-
end
|
230
|
+
def initialize(value,preprocessor)
|
231
|
+
@value = value
|
232
|
+
@processor = preprocessor
|
259
233
|
end
|
260
234
|
|
261
235
|
# Requests the value of this object's saved value name from
|
262
236
|
# +namespace+, and returns the string representation of that
|
263
237
|
# value to the caller, after passing it through the preprocessor.
|
264
238
|
def output(namespace = nil)
|
265
|
-
namespace
|
266
|
-
|
267
|
-
|
239
|
+
parser = namespace.parser if namespace
|
240
|
+
parser = Parser.recent_parser unless parser
|
241
|
+
namespace ||= parser
|
242
|
+
preprocessor = parser.preprocessor
|
243
|
+
processor = @processor || parser.default_processor
|
268
244
|
if preprocessor.respond_to?(processor)
|
269
245
|
preprocessor.send(processor,namespace.get(@value).to_s)
|
270
246
|
else
|
@@ -290,55 +266,60 @@ class PageTemplate
|
|
290
266
|
# objects are added to @falseCommands, and are printed if +variable+
|
291
267
|
# is false, instead.
|
292
268
|
class IfCommand < StackableCommand
|
269
|
+
@modifier = :elsif
|
270
|
+
@closer = :end
|
271
|
+
|
293
272
|
# +command+ must match "if <value>" or "unless <value>"
|
294
|
-
def initialize(
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
else
|
303
|
-
raise ArgumentError, "IfCommand.new('#{command}') - invalid syntax"
|
304
|
-
end
|
273
|
+
def initialize(called_as, value)
|
274
|
+
@value = value
|
275
|
+
@called_as = called_as
|
276
|
+
@trueCommands = []
|
277
|
+
@trueCommands << [value,BlockCommand.new]
|
278
|
+
@falseCommands = BlockCommand.new
|
279
|
+
@in_else = (called_as == 'unless')
|
280
|
+
@switched = false
|
305
281
|
end
|
306
282
|
# Add the command to the @trueCommands or @falseCommands block,
|
307
283
|
# depending on if the command is an 'if' or 'unless' and if 'else'
|
308
284
|
# has been called.
|
309
285
|
def add(command)
|
310
286
|
unless @in_else
|
311
|
-
@trueCommands.add command
|
287
|
+
@trueCommands.last.last.add command
|
312
288
|
else
|
313
289
|
@falseCommands.add command
|
314
290
|
end
|
315
291
|
end
|
292
|
+
# an elseif will create a new condition.
|
293
|
+
def elsif(value)
|
294
|
+
raise Argumentrror, "'elsif' cannot be passed after 'else' or in an 'unless'" if @switched || @in_else
|
295
|
+
@trueCommands << [value,BlockCommand.new]
|
296
|
+
end
|
316
297
|
# an 'else' will modify the command, switching it from adding to
|
317
298
|
# @trueCommands to adding to @falseCommands, or vice versa.
|
318
|
-
def
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
@switched = true
|
323
|
-
true
|
324
|
-
else
|
325
|
-
false
|
326
|
-
end
|
299
|
+
def else
|
300
|
+
raise ArgumentError, "More than one 'else' to IfCommand" if @switched
|
301
|
+
@in_else = ! @in_else
|
302
|
+
@switched = true
|
327
303
|
end
|
328
304
|
# If @value is true within the context of +namespace+, then print
|
329
305
|
# the output of @trueCommands. Otherwise, print the output of
|
330
306
|
# @falseCommands
|
331
307
|
def output(namespace=nil)
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
@falseCommands.output(namespace)
|
308
|
+
val = ''
|
309
|
+
@trueCommands.each do |val,commands|
|
310
|
+
return commands.output(namespace) if namespace.true?(val)
|
336
311
|
end
|
312
|
+
@falseCommands.output(namespace)
|
337
313
|
end
|
338
314
|
def to_s
|
339
|
-
str = ''
|
315
|
+
str = '['
|
340
316
|
if @called_as == 'if'
|
341
|
-
str =
|
317
|
+
str = ' If'
|
318
|
+
label = ' If'
|
319
|
+
str += @trueCommands.map { |val,commands|
|
320
|
+
s = "#{label} (#{val}) #{commands}"
|
321
|
+
label = ' Elsif'
|
322
|
+
}.join('')
|
342
323
|
if @falseCommands.length > 0
|
343
324
|
str << " else: #{@falseCommands}"
|
344
325
|
end
|
@@ -369,32 +350,26 @@ class PageTemplate
|
|
369
350
|
# [% else %] may be specified, modifying LoopCommand to print out
|
370
351
|
# @elseCommands in case +variable+ is false, or empty.
|
371
352
|
class LoopCommand < StackableCommand
|
353
|
+
@modifier = :else
|
354
|
+
@closer = :end
|
355
|
+
|
372
356
|
# [% in variable %] or [% loop variable %]
|
373
357
|
# Or [% in variable: name %]
|
374
|
-
def initialize(
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
@in_else = false
|
383
|
-
else
|
384
|
-
raise ArgumentError, "LoopCommand.new('#{cmd}') - invalid syntax"
|
385
|
-
end
|
358
|
+
def initialize(called_as, value, iterators)
|
359
|
+
@called_as = called_as
|
360
|
+
@value = value
|
361
|
+
@iterators = iterators.strip.gsub(/\s+/,' ').split if iterators
|
362
|
+
@switched = false
|
363
|
+
@commands = BlockCommand.new
|
364
|
+
@elseCommands = BlockCommand.new
|
365
|
+
@in_else = false
|
386
366
|
end
|
387
367
|
# An 'else' defines a list of commands to call when the loop is
|
388
368
|
# empty.
|
389
|
-
def
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
@switched = true
|
394
|
-
true
|
395
|
-
else
|
396
|
-
false
|
397
|
-
end
|
369
|
+
def else
|
370
|
+
raise ArgumentError, "More than one 'else' to IfCommand" if @switched
|
371
|
+
@in_else = ! @in_else
|
372
|
+
@switched = true
|
398
373
|
end
|
399
374
|
# adds to @commands for the loop, or @elseCommands if 'else' has
|
400
375
|
# been passed.
|
@@ -430,17 +405,46 @@ class PageTemplate
|
|
430
405
|
ns = Namespace.new(namespace)
|
431
406
|
|
432
407
|
# Print the output of all the objects joined together.
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
408
|
+
case
|
409
|
+
when @iterators.nil? || @iterators.empty?
|
410
|
+
vals.map { |item|
|
411
|
+
ns.clear
|
412
|
+
ns.object = item
|
413
|
+
ns['__FIRST__'] = (i == 1) ? true : false
|
414
|
+
ns['__LAST__'] = (i == len) ? true : false
|
415
|
+
ns['__ODD__'] = odd
|
416
|
+
odd = ! odd
|
417
|
+
i += 1
|
418
|
+
@commands.output(ns)
|
419
|
+
}.join('')
|
420
|
+
when @iterators.size == 1
|
421
|
+
iterator = @iterators[0]
|
422
|
+
vals.map { |item|
|
423
|
+
ns.clear_cache
|
424
|
+
# We have an explicit iterator - don't set an object for it.
|
425
|
+
# ns.object = item
|
426
|
+
ns['__FIRST__'] = (i == 1) ? true : false
|
427
|
+
ns['__LAST__'] = (i == len) ? true : false
|
428
|
+
ns['__ODD__'] = odd
|
429
|
+
odd = ! odd
|
430
|
+
ns[iterator] = item
|
431
|
+
i += 1
|
432
|
+
@commands.output(ns)
|
433
|
+
}.join('')
|
434
|
+
else
|
435
|
+
vals.map { |list|
|
436
|
+
ns.clear_cache
|
437
|
+
ns['__FIRST__'] = (i == 1) ? true : false
|
438
|
+
ns['__LAST__'] = (i == len) ? true : false
|
439
|
+
ns['__ODD__'] = odd
|
440
|
+
@iterators.each_with_index do |iterator,index|
|
441
|
+
ns[iterator] = list[index]
|
442
|
+
end
|
443
|
+
odd = ! odd
|
444
|
+
i += 1
|
445
|
+
@commands.output(ns)
|
446
|
+
}.join('')
|
447
|
+
end
|
444
448
|
end
|
445
449
|
|
446
450
|
def to_s
|
@@ -464,13 +468,8 @@ class PageTemplate
|
|
464
468
|
# The body returned by the Source is then passed to Parser for
|
465
469
|
# compilation.
|
466
470
|
class IncludeCommand < Command
|
467
|
-
def initialize(
|
468
|
-
|
469
|
-
@value = $1
|
470
|
-
@parser = parser
|
471
|
-
else
|
472
|
-
raise ArgumentError, "IncludeCommand.new('#{cmd}') - invalid syntax"
|
473
|
-
end
|
471
|
+
def initialize(value)
|
472
|
+
@value = value
|
474
473
|
end
|
475
474
|
def to_s
|
476
475
|
"[ Include: #{@value} ]"
|
@@ -483,15 +482,16 @@ class PageTemplate
|
|
483
482
|
# The body returned by the Source is then passed to Parser for
|
484
483
|
# compilation.
|
485
484
|
def output(namespace)
|
486
|
-
# We don't use
|
485
|
+
# We don't use parser.compile because we need to know when something
|
487
486
|
# doesn't exist.
|
488
|
-
|
487
|
+
parser = namespace.parser
|
488
|
+
body = parser.source.get(@value)
|
489
489
|
unless body
|
490
490
|
fn = namespace.get(@value)
|
491
|
-
body =
|
491
|
+
body = parser.source.get(fn) if fn
|
492
492
|
end
|
493
493
|
if body
|
494
|
-
|
494
|
+
parser.parse(body).output(namespace)
|
495
495
|
else
|
496
496
|
"[ Template '#{fn || @value}' not found ]"
|
497
497
|
end
|