PageTemplate 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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