PageTemplate 2.0.0 → 2.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/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
|