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 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.0.0"
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.
@@ -14,15 +14,16 @@ class PageTemplate
14
14
  # [% else %]
15
15
  # [% end %]
16
16
  class CaseCommand < StackableCommand
17
- attr_reader :current_case
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(command)
23
- /^case ((\w+)(\.\w+)*)$/i =~ command
24
+ def initialize(value)
24
25
  @called_as = 'case'
25
- @value = $1
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 modifies?(cmd)
40
- case cmd
41
- when /^when (.+)$/
42
- @current_case = $1
43
- @blocks[$1] = BlockCommand.new unless @blocks.has_key?($1)
44
- true
45
- when /^else$/
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
- DEFAULTGLOSSARY.define(/^case (\w+)(\.\w+)*$/,CaseCommand)
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 = @parser.glossary.lookup(@command)
62
- case
63
- when cls == UnknownCommand
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
- p = cls.dup
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,parser)
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(command=nil)
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.namespace. Otherwise, a new namespace is created, a
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
- @namespace.parent = @parser.namespace
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(@namespace)
182
+ super(self)
183
+ when object.is_a?(NamespaceItem)
184
+ @parent = object
185
+ super(self)
193
186
  else
194
- namespace = Namespace.new(@namespace)
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(cmd,parser)
252
- @parser = parser
253
- if /^(?:var )?((?:\w+)(?:\.\w+\??)*)(?:\s+:(\w+))?$/i =~ cmd
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 ||= @parser.namespace
266
- preprocessor = @parser.preprocessor
267
- processor = @processor || @parser.default_processor
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(command)
295
- if /^(unless|if) (\w+(\.\w+\??)*)$/i =~ command
296
- @called_as = $1
297
- @value = $2
298
- @trueCommands = BlockCommand.new
299
- @falseCommands = BlockCommand.new
300
- @in_else = ($1 == 'unless')
301
- @switched = false
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 modifies?(cmd)
319
- if cmd =~ /^else$/i
320
- raise ArgumentError, "More than one 'else' to IfCommand" if @switched
321
- @in_else = ! @in_else
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
- if namespace.true?(@value)
333
- @trueCommands.output(namespace)
334
- else
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 = "[ If (#{@value}): #{@trueCommands}"
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(cmd)
375
- if /^(in|loop) (\w+(?:\.\w+\??)*)(?:\:\s+(\w+))?$/i =~ cmd
376
- @called_as = $1
377
- @value = $2
378
- @iterator = $3
379
- @switched = false
380
- @commands = BlockCommand.new
381
- @elseCommands = BlockCommand.new
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 modifies?(cmd)
390
- if cmd =~ /^else|no|empty$/i
391
- raise ArgumentError, "More than one 'else' to IfCommand" if @switched
392
- @in_else = ! @in_else
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
- vals.map { |item|
434
- ns.clear_cache
435
- ns.object = item
436
- ns['__FIRST__'] = (i == 1) ? true : false
437
- ns['__LAST__'] = (i == len) ? true : false
438
- ns['__ODD__'] = odd
439
- ns[@iterator] = item if @iterator
440
- odd = ! odd
441
- i += 1
442
- @commands.output(ns)
443
- }.join('')
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(cmd,parser)
468
- if /^include ((\w+)(\.\w+\??)*)$/i =~ cmd
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 @parser.compile because we need to know when something
485
+ # We don't use parser.compile because we need to know when something
487
486
  # doesn't exist.
488
- body = @parser.source.get(@value)
487
+ parser = namespace.parser
488
+ body = parser.source.get(@value)
489
489
  unless body
490
490
  fn = namespace.get(@value)
491
- body = @parser.source.get(fn) if fn
491
+ body = parser.source.get(fn) if fn
492
492
  end
493
493
  if body
494
- @parser.parse(body).output(namespace)
494
+ parser.parse(body).output(namespace)
495
495
  else
496
496
  "[ Template '#{fn || @value}' not found ]"
497
497
  end