PageTemplate 1.2.0 → 2.0.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.
Files changed (150) hide show
  1. data/Changes +24 -0
  2. data/MANIFEST +0 -1
  3. data/README.txt +10 -8
  4. data/Rakefile +30 -9
  5. data/lib/PageTemplate/case.rb +72 -0
  6. data/lib/PageTemplate/commands.rb +500 -0
  7. data/lib/PageTemplate/parser.rb +373 -0
  8. data/lib/PageTemplate.rb +47 -991
  9. data/setup-usage.txt +129 -0
  10. data/setup.rb +1360 -0
  11. data/tdata/dummy.txt +2 -0
  12. data/test.rb +616 -0
  13. metadata +13 -169
  14. data/CVS/Entries +0 -10
  15. data/CVS/Repository +0 -1
  16. data/CVS/Root +0 -1
  17. data/Changes~ +0 -7
  18. data/InstalledFiles +0 -7
  19. data/Rakefile~ +0 -54
  20. data/TC_PageTemplate.rb +0 -974
  21. data/TC_PageTemplate.rb~ +0 -965
  22. data/config.save +0 -12
  23. data/doc/classes/BlockCommand.html +0 -229
  24. data/doc/classes/BlockCommand.src/M000004.html +0 -18
  25. data/doc/classes/BlockCommand.src/M000005.html +0 -25
  26. data/doc/classes/BlockCommand.src/M000006.html +0 -18
  27. data/doc/classes/BlockCommand.src/M000007.html +0 -23
  28. data/doc/classes/BlockCommand.src/M000008.html +0 -24
  29. data/doc/classes/Command.html +0 -167
  30. data/doc/classes/Command.src/M000032.html +0 -19
  31. data/doc/classes/Command.src/M000033.html +0 -18
  32. data/doc/classes/CommentCommand.html +0 -146
  33. data/doc/classes/CommentCommand.src/M000009.html +0 -18
  34. data/doc/classes/IfCommand.html +0 -193
  35. data/doc/classes/IfCommand.src/M000042.html +0 -19
  36. data/doc/classes/IfCommand.src/M000043.html +0 -20
  37. data/doc/classes/IfCommand.src/M000044.html +0 -20
  38. data/doc/classes/IfElseCommand.html +0 -189
  39. data/doc/classes/IfElseCommand.src/M000025.html +0 -19
  40. data/doc/classes/IfElseCommand.src/M000026.html +0 -23
  41. data/doc/classes/IfElseCommand.src/M000027.html +0 -20
  42. data/doc/classes/IncludeCommand.html +0 -171
  43. data/doc/classes/IncludeCommand.src/M000019.html +0 -26
  44. data/doc/classes/IncludeCommand.src/M000020.html +0 -28
  45. data/doc/classes/IncludeCommand.src/M000021.html +0 -18
  46. data/doc/classes/IncludeCommandError.html +0 -118
  47. data/doc/classes/LoopCommand.html +0 -197
  48. data/doc/classes/LoopCommand.src/M000001.html +0 -19
  49. data/doc/classes/LoopCommand.src/M000002.html +0 -42
  50. data/doc/classes/LoopCommand.src/M000003.html +0 -20
  51. data/doc/classes/LoopElseCommand.html +0 -190
  52. data/doc/classes/LoopElseCommand.src/M000022.html +0 -19
  53. data/doc/classes/LoopElseCommand.src/M000023.html +0 -23
  54. data/doc/classes/LoopElseCommand.src/M000024.html +0 -20
  55. data/doc/classes/Namespace.html +0 -297
  56. data/doc/classes/Namespace.src/M000034.html +0 -19
  57. data/doc/classes/Namespace.src/M000035.html +0 -18
  58. data/doc/classes/Namespace.src/M000036.html +0 -18
  59. data/doc/classes/Namespace.src/M000037.html +0 -19
  60. data/doc/classes/Namespace.src/M000038.html +0 -36
  61. data/doc/classes/Namespace.src/M000039.html +0 -21
  62. data/doc/classes/Namespace.src/M000040.html +0 -18
  63. data/doc/classes/Namespace.src/M000041.html +0 -18
  64. data/doc/classes/PageTemplate.html +0 -380
  65. data/doc/classes/PageTemplate.src/M000010.html +0 -49
  66. data/doc/classes/PageTemplate.src/M000011.html +0 -18
  67. data/doc/classes/PageTemplate.src/M000012.html +0 -29
  68. data/doc/classes/PageTemplate.src/M000013.html +0 -18
  69. data/doc/classes/PageTemplate.src/M000014.html +0 -18
  70. data/doc/classes/PageTemplate.src/M000015.html +0 -18
  71. data/doc/classes/PageTemplate.src/M000016.html +0 -18
  72. data/doc/classes/PageTemplate.src/M000017.html +0 -18
  73. data/doc/classes/PageTemplate.src/M000018.html +0 -19
  74. data/doc/classes/Syntax/CachedParser.html +0 -163
  75. data/doc/classes/Syntax/CachedParser.src/M000048.html +0 -19
  76. data/doc/classes/Syntax/CachedParser.src/M000049.html +0 -53
  77. data/doc/classes/Syntax/Glossary.html +0 -251
  78. data/doc/classes/Syntax/Glossary.src/M000050.html +0 -20
  79. data/doc/classes/Syntax/Glossary.src/M000051.html +0 -29
  80. data/doc/classes/Syntax/Parser.html +0 -261
  81. data/doc/classes/Syntax/Parser.src/M000052.html +0 -22
  82. data/doc/classes/Syntax/Parser.src/M000053.html +0 -36
  83. data/doc/classes/Syntax/Parser.src/M000054.html +0 -18
  84. data/doc/classes/Syntax/Parser.src/M000055.html +0 -21
  85. data/doc/classes/Syntax/Parser.src/M000056.html +0 -53
  86. data/doc/classes/Syntax/Parser.src/M000057.html +0 -111
  87. data/doc/classes/Syntax.html +0 -139
  88. data/doc/classes/TextCommand.html +0 -185
  89. data/doc/classes/TextCommand.src/M000045.html +0 -18
  90. data/doc/classes/TextCommand.src/M000046.html +0 -18
  91. data/doc/classes/TextCommand.src/M000047.html +0 -18
  92. data/doc/classes/UnlessCommand.html +0 -151
  93. data/doc/classes/UnlessCommand.src/M000028.html +0 -20
  94. data/doc/classes/ValueCommand.html +0 -186
  95. data/doc/classes/ValueCommand.src/M000029.html +0 -18
  96. data/doc/classes/ValueCommand.src/M000030.html +0 -18
  97. data/doc/classes/ValueCommand.src/M000031.html +0 -20
  98. data/doc/created.rid +0 -1
  99. data/doc/files/README_txt.html +0 -224
  100. data/doc/files/lib/PageTemplate_rb.html +0 -119
  101. data/doc/fr_class_index.html +0 -44
  102. data/doc/fr_file_index.html +0 -28
  103. data/doc/fr_method_index.html +0 -83
  104. data/doc/index.html +0 -24
  105. data/doc/rdoc-style.css +0 -208
  106. data/install.rb +0 -1015
  107. data/lib/CVS/Entries +0 -2
  108. data/lib/CVS/Repository +0 -1
  109. data/lib/CVS/Root +0 -1
  110. data/lib/PageTemplate.rb~ +0 -1004
  111. data/pkg/PageTemplate-1.2.0.gem +0 -0
  112. data/tdata/CVS/Entries +0 -28
  113. data/tdata/CVS/Repository +0 -1
  114. data/tdata/CVS/Root +0 -1
  115. data/tdata/cm.txt +0 -3
  116. data/tdata/complex.txt +0 -116
  117. data/tdata/i1.txt +0 -1
  118. data/tdata/i2.txt +0 -7
  119. data/tdata/ib1.txt +0 -7
  120. data/tdata/ib2.txt +0 -13
  121. data/tdata/ie1.txt +0 -4
  122. data/tdata/ie2.txt +0 -15
  123. data/tdata/include.1.txt +0 -1
  124. data/tdata/include.2.txt +0 -1
  125. data/tdata/include.3.txt +0 -1
  126. data/tdata/include.4.nf.out.txt +0 -2
  127. data/tdata/include.4.out.txt +0 -3
  128. data/tdata/include.4.txt +0 -2
  129. data/tdata/include.4a.txt +0 -1
  130. data/tdata/include.4b.txt +0 -2
  131. data/tdata/include.5.txt +0 -2
  132. data/tdata/l1.txt +0 -5
  133. data/tdata/l2.txt +0 -7
  134. data/tdata/metadata.1.txt +0 -5
  135. data/tdata/metadata.2.txt +0 -5
  136. data/tdata/metadata.3.txt +0 -5
  137. data/tdata/metadata.4.txt +0 -5
  138. data/tdata/nm.txt +0 -4
  139. data/tdata/p/CVS/Entries +0 -5
  140. data/tdata/p/CVS/Repository +0 -1
  141. data/tdata/p/CVS/Root +0 -1
  142. data/tdata/p/CVS/b2.txt,t +0 -0
  143. data/tdata/p/_b1.txt +0 -0
  144. data/tdata/p/b1.txt +0 -4
  145. data/tdata/p/b2.txt +0 -4
  146. data/tdata/p/b2.txt~ +0 -4
  147. data/tdata/p/cb1.txt +0 -4
  148. data/tdata/t.txt +0 -2
  149. data/tdata/v1.txt +0 -11
  150. data/tdata/v2.txt +0 -11
@@ -0,0 +1,500 @@
1
+ # commands.rb
2
+ #
3
+ # This contains the various Command objects that are the building
4
+ # blocks of PageTemplate.
5
+ #
6
+
7
+ ############################################################
8
+
9
+
10
+
11
+ class PageTemplate
12
+ # Command classes generate text output based on conditions which vary
13
+ # between each class. Command provides an abstract base class to show
14
+ # interface.
15
+ class Command
16
+
17
+ # Subclasses of Command use the output method to generate their text
18
+ # output. +namespace+ is a Namespace object, which may be
19
+ # required by a particular subclass.
20
+ #
21
+ # Command#output must return a string
22
+ def output(namespace = nil)
23
+ raise NotImplementedError,
24
+ "output() must be implemented by subclasses"
25
+ end
26
+
27
+ # to_s of a Command prints out class and command information, for
28
+ # debugging and visual summary of the template without parsing the
29
+ # outputs.
30
+ def to_s
31
+ "[ #{self.class} ]"
32
+ end
33
+ end
34
+
35
+ # CommentCommand is a command that just returns nothing for its
36
+ # output, allowing the designer to populate their pages with
37
+ # PageTemplate-style comments as well.
38
+ class CommentCommand < Command
39
+ # output returns nothing.
40
+ def output(namespace=nil)
41
+ ''
42
+ end
43
+ # Save the +comment+ for to_s
44
+ def initialize(comment='')
45
+ @comment = comment
46
+ end
47
+ def to_s
48
+ "[ Comment: #{@comment} ]"
49
+ end
50
+ end
51
+
52
+ # An UnknownCommand is exactly that: A command we know nothing
53
+ # about.
54
+ #
55
+ # We keep this and save it for future use in case we will know
56
+ # something about it before output is called.
57
+ class UnknownCommand < Command
58
+ # If the command that UnknownCommand is set to exists, find out if
59
+ # the command exists in the Parser's glossary.
60
+ def output(namespace)
61
+ cls = @parser.glossary.lookup(@command)
62
+ case
63
+ when cls == UnknownCommand
64
+ "[ 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
+ else
72
+ p = cls.dup
73
+ p.set_command @command
74
+ p.output(namespace)
75
+ end
76
+ end
77
+ # Save the +command+ and the +parser+, so we can look it
78
+ # up and hopefully
79
+ def initialize(command,parser)
80
+ @command = command
81
+ @parser = parser
82
+ end
83
+ def to_s
84
+ "[ UnknownCommand: #{@command} ]"
85
+ end
86
+ end
87
+
88
+ # StackableCommand is the parent class of any command that creates a
89
+ # branch in the logic: such as If, Loop, etcetera.
90
+ #
91
+ # Any child that wants to do more than stand alone must inherit from
92
+ # StackableCommand. Also recommended is setting @called_as, so
93
+ # all of StackableCommand's ways of closing blocks work.
94
+ class StackableCommand < Command
95
+ # @called_as is set to the command which this is called as. This
96
+ # allows a number of [% end %], [% end name %], [% endname %] or
97
+ # even [% /name %] to close StackableCommands.
98
+ def initialize(command=nil)
99
+ @called_as = 'stackable'
100
+ raise ArgumentError, 'StackableCommand should not be called directly'
101
+ end
102
+ def add(block)
103
+ raise ArgumentError, 'StackableCommand should not be called directly'
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
+ def to_s
118
+ "[ #{@called_as} ]"
119
+ end
120
+ end
121
+
122
+ # BlockCommand provides a single interface to multiple Command
123
+ # objects.
124
+ # This should probably never be called by the designer or
125
+ # programmer, but by StackableCommands.
126
+ class BlockCommand < Command
127
+ def initialize()
128
+ @commandBlock = []
129
+ end
130
+
131
+ # Return Commands held, as a string
132
+ def to_s
133
+ '[ Blocks: ' + @commandBlock.map{ |i| "[#{i.to_s}]" }.join(' ') + ']'
134
+ end
135
+
136
+ # Returns the number of Commands held in this BlockCommand
137
+ def length
138
+ @commandBlock.length
139
+ end
140
+
141
+ # Adds +command+ to the end of the BlockCommand's
142
+ # chain of Commands.
143
+ #
144
+ # A TypeError is raised if the object being added is not a ((<Command>)).
145
+ def add(command)
146
+ unless command.is_a?(Command)
147
+ raise TypeError, 'BlockCommand.add: Attempt to add non-Command object'
148
+ end
149
+ @commandBlock << command
150
+ end
151
+
152
+ # Calls Command#output(namespace) on each Command contained in this
153
+ # object. The output is returned as a single string. If no output
154
+ # is generated, returns an empty string.
155
+ def output(namespace = nil)
156
+ @commandBlock.map{|x| x.output(namespace)}.join('')
157
+ end
158
+ end
159
+
160
+ # Template is the top-level blockcommand object
161
+ # It is what is returned on Template.load() or
162
+ # Template.parse()
163
+ #
164
+ # Template should only be called by the Parser, and never
165
+ # by anything else.
166
+ class Template < BlockCommand
167
+ # Template must know about the parser so it can access its
168
+ # namespace.
169
+ def initialize(parser)
170
+ @parser = parser
171
+ @namespace = Namespace.new
172
+ super()
173
+ end
174
+ def to_s
175
+ '[ Template: ' + @commandBlock.map{ |i| "[#{i.to_s}]" }.join(' ') + ']'
176
+ end
177
+ # Template#output is a special case for a Command. Because
178
+ # Template is what's returned by Parser.load() or Parser.parse(),
179
+ # the programmer may well call Template#output(anything).
180
+ #
181
+ # If +object+ is a Namespace, then treat output as a typical
182
+ # BlockCommand. If +object+ is nil, then namespace is
183
+ # @parser.namespace. Otherwise, a new namespace is created, a
184
+ # child of @namespace, and is assigned +object+ as its namespace
185
+ def output(object=nil)
186
+ @namespace.parent = @parser.namespace
187
+ case
188
+ when object.is_a?(Namespace)
189
+ @namespace.parent = object
190
+ super(@namespace)
191
+ when object.nil?
192
+ super(@namespace)
193
+ else
194
+ namespace = Namespace.new(@namespace)
195
+ namespace.object = object
196
+ super(namespace)
197
+ end
198
+ 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
+ end
214
+
215
+ # A very simple Command which outputs a static string of text
216
+ class TextCommand < Command
217
+
218
+ # Creates a TextCommand object, saving +text+ for future output.
219
+ def initialize(text)
220
+ @text = text
221
+ end
222
+
223
+ def to_s
224
+ @text
225
+ end
226
+
227
+ # Returns the string provided during this object's creation.
228
+ def output(namespace = nil)
229
+ @text
230
+ end
231
+ end
232
+
233
+ # ValueCommand will print out a variable name, possibly passing its
234
+ # value through a preprocessor.
235
+ #
236
+ # [% var variable [:processor] %]
237
+ #
238
+ # +variable+ is first plucked out of the Namespace,
239
+ #
240
+ # The to_s of the output from Namespace#get is passed through
241
+ # parser's preprocessor. If :processor is defined, then it is
242
+ # parser.preprocessor.send(:processor,body) is called, allowing the
243
+ # designer to choose a particular format for the output. If
244
+ # :processor is not given, then parser's default processor is used.
245
+ class ValueCommand < Command
246
+
247
+ # Creates the ValueCommand, with +value+ as the name of the variable
248
+ # to be inserted during output. +parser+ is the Parser object,
249
+ # which we save so we can access its Preprocessor and its default
250
+ # 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
259
+ end
260
+
261
+ # Requests the value of this object's saved value name from
262
+ # +namespace+, and returns the string representation of that
263
+ # value to the caller, after passing it through the preprocessor.
264
+ def output(namespace = nil)
265
+ namespace ||= @parser.namespace
266
+ preprocessor = @parser.preprocessor
267
+ processor = @processor || @parser.default_processor
268
+ if preprocessor.respond_to?(processor)
269
+ preprocessor.send(processor,namespace.get(@value).to_s)
270
+ else
271
+ "[ ValueCommand: unknown preprocessor #{@processor} ]"
272
+ end
273
+ end
274
+
275
+ def to_s
276
+ str = "[ Value: #{@value} "
277
+ str << ":#{@processor} " if (@processor)
278
+ str << ']'
279
+ str
280
+ end
281
+ end
282
+
283
+ # IfCommand is a StackableCommand. It requires an opening:
284
+ # [% if variable %] or [% unless variable %].
285
+ #
286
+ # On execution, if +variable+ is true, then the contents of
287
+ # IfCommand's @commands is printed.
288
+ #
289
+ # [% else %] may be specified for either if or unless, after which
290
+ # objects are added to @falseCommands, and are printed if +variable+
291
+ # is false, instead.
292
+ class IfCommand < StackableCommand
293
+ # +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
305
+ end
306
+ # Add the command to the @trueCommands or @falseCommands block,
307
+ # depending on if the command is an 'if' or 'unless' and if 'else'
308
+ # has been called.
309
+ def add(command)
310
+ unless @in_else
311
+ @trueCommands.add command
312
+ else
313
+ @falseCommands.add command
314
+ end
315
+ end
316
+ # an 'else' will modify the command, switching it from adding to
317
+ # @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
327
+ end
328
+ # If @value is true within the context of +namespace+, then print
329
+ # the output of @trueCommands. Otherwise, print the output of
330
+ # @falseCommands
331
+ def output(namespace=nil)
332
+ if namespace.true?(@value)
333
+ @trueCommands.output(namespace)
334
+ else
335
+ @falseCommands.output(namespace)
336
+ end
337
+ end
338
+ def to_s
339
+ str = ''
340
+ if @called_as == 'if'
341
+ str = "[ If (#{@value}): #{@trueCommands}"
342
+ if @falseCommands.length > 0
343
+ str << " else: #{@falseCommands}"
344
+ end
345
+ else
346
+ str = "[ Unless (#{@value}): #{@falseCommands}"
347
+ if @trueCommands.length > 0
348
+ str << " else: #{@trueCommands}"
349
+ end
350
+ end
351
+ str << ' ]'
352
+ str
353
+ end
354
+ end
355
+
356
+ # LoopCommand is a StackableCommand. It requires an opening:
357
+ # [% if variable %] or [% unless variable %].
358
+ #
359
+ # +variable+ is fetched from the namespace.
360
+ #
361
+ # On execution, if +variable+ is true, and non-empty, then
362
+ # @commands is printed once with each item in the list placed in
363
+ # its own namespace and passed to the loop commands.
364
+ # (a list is defined as an object that responds to :map)
365
+ #
366
+ # If +variable+ is true, but does not respond to :map, then
367
+ # the list is called once
368
+ #
369
+ # [% else %] may be specified, modifying LoopCommand to print out
370
+ # @elseCommands in case +variable+ is false, or empty.
371
+ class LoopCommand < StackableCommand
372
+ # [% in variable %] or [% loop variable %]
373
+ # 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
386
+ end
387
+ # An 'else' defines a list of commands to call when the loop is
388
+ # 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
398
+ end
399
+ # adds to @commands for the loop, or @elseCommands if 'else' has
400
+ # been passed.
401
+ def add(command)
402
+ unless @in_else
403
+ @commands.add command
404
+ else
405
+ @elseCommands.add command
406
+ end
407
+ end
408
+ # If +variable+ is true, and non-empty, then @commands is printed
409
+ # once with each item in the list placed in its own namespace and
410
+ # passed to the loop commands. (a list is defined as an object
411
+ # that responds to :map)
412
+ #
413
+ # If +variable+ is true, but does not respond to :map, then
414
+ # the list is called once
415
+ def output(namespace)
416
+
417
+ vals = namespace.get(@value)
418
+ ns = nil
419
+ return @elseCommands.output(namespace) unless vals
420
+ unless vals.respond_to?(:map) && vals.respond_to?(:length)
421
+ vals = [vals]
422
+ end
423
+
424
+ return @elseCommands.output(namespace) if vals.empty?
425
+
426
+ # Unfortunately, no "map_with_index"
427
+ len = vals.length
428
+ i = 1
429
+ odd = true
430
+ ns = Namespace.new(namespace)
431
+
432
+ # 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('')
444
+ end
445
+
446
+ def to_s
447
+ str = "[ Loop: #{@value} " + @commands.to_s
448
+ str << " Else: " + @elseCommands.to_s if @elseCommands.length > 0
449
+ str << ' ]'
450
+ str
451
+ end
452
+ end
453
+
454
+ # IncludeCommand allows the designer to include a template from
455
+ # another source.
456
+ #
457
+ # [% include variable %] or [% include literal %]
458
+ #
459
+ # If +literal+ exists in parser.source, then it is called without
460
+ # passing it to its namespace. If it does not exist, then it is
461
+ # evaluated within the context of its namespace and then passed to
462
+ # parser.source for fetching the body of the file/source.
463
+ #
464
+ # The body returned by the Source is then passed to Parser for
465
+ # compilation.
466
+ 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
474
+ end
475
+ def to_s
476
+ "[ Include: #{@value} ]"
477
+ end
478
+ # If @value exists in parser.source, then it is called without
479
+ # passing it to its namespace. If it does not exist, then it is
480
+ # evaluated within the context of its namespace and then passed to
481
+ # parser.source for fetching the body of the file/source.
482
+ #
483
+ # The body returned by the Source is then passed to Parser for
484
+ # compilation.
485
+ def output(namespace)
486
+ # We don't use @parser.compile because we need to know when something
487
+ # doesn't exist.
488
+ body = @parser.source.get(@value)
489
+ unless body
490
+ fn = namespace.get(@value)
491
+ body = @parser.source.get(fn) if fn
492
+ end
493
+ if body
494
+ @parser.parse(body).output(namespace)
495
+ else
496
+ "[ Template '#{fn || @value}' not found ]"
497
+ end
498
+ end
499
+ end
500
+ end