PageTemplate 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changes +24 -0
- data/MANIFEST +0 -1
- data/README.txt +10 -8
- data/Rakefile +30 -9
- data/lib/PageTemplate/case.rb +72 -0
- data/lib/PageTemplate/commands.rb +500 -0
- data/lib/PageTemplate/parser.rb +373 -0
- data/lib/PageTemplate.rb +47 -991
- data/setup-usage.txt +129 -0
- data/setup.rb +1360 -0
- data/tdata/dummy.txt +2 -0
- data/test.rb +616 -0
- metadata +13 -169
- data/CVS/Entries +0 -10
- data/CVS/Repository +0 -1
- data/CVS/Root +0 -1
- data/Changes~ +0 -7
- data/InstalledFiles +0 -7
- data/Rakefile~ +0 -54
- data/TC_PageTemplate.rb +0 -974
- data/TC_PageTemplate.rb~ +0 -965
- data/config.save +0 -12
- data/doc/classes/BlockCommand.html +0 -229
- data/doc/classes/BlockCommand.src/M000004.html +0 -18
- data/doc/classes/BlockCommand.src/M000005.html +0 -25
- data/doc/classes/BlockCommand.src/M000006.html +0 -18
- data/doc/classes/BlockCommand.src/M000007.html +0 -23
- data/doc/classes/BlockCommand.src/M000008.html +0 -24
- data/doc/classes/Command.html +0 -167
- data/doc/classes/Command.src/M000032.html +0 -19
- data/doc/classes/Command.src/M000033.html +0 -18
- data/doc/classes/CommentCommand.html +0 -146
- data/doc/classes/CommentCommand.src/M000009.html +0 -18
- data/doc/classes/IfCommand.html +0 -193
- data/doc/classes/IfCommand.src/M000042.html +0 -19
- data/doc/classes/IfCommand.src/M000043.html +0 -20
- data/doc/classes/IfCommand.src/M000044.html +0 -20
- data/doc/classes/IfElseCommand.html +0 -189
- data/doc/classes/IfElseCommand.src/M000025.html +0 -19
- data/doc/classes/IfElseCommand.src/M000026.html +0 -23
- data/doc/classes/IfElseCommand.src/M000027.html +0 -20
- data/doc/classes/IncludeCommand.html +0 -171
- data/doc/classes/IncludeCommand.src/M000019.html +0 -26
- data/doc/classes/IncludeCommand.src/M000020.html +0 -28
- data/doc/classes/IncludeCommand.src/M000021.html +0 -18
- data/doc/classes/IncludeCommandError.html +0 -118
- data/doc/classes/LoopCommand.html +0 -197
- data/doc/classes/LoopCommand.src/M000001.html +0 -19
- data/doc/classes/LoopCommand.src/M000002.html +0 -42
- data/doc/classes/LoopCommand.src/M000003.html +0 -20
- data/doc/classes/LoopElseCommand.html +0 -190
- data/doc/classes/LoopElseCommand.src/M000022.html +0 -19
- data/doc/classes/LoopElseCommand.src/M000023.html +0 -23
- data/doc/classes/LoopElseCommand.src/M000024.html +0 -20
- data/doc/classes/Namespace.html +0 -297
- data/doc/classes/Namespace.src/M000034.html +0 -19
- data/doc/classes/Namespace.src/M000035.html +0 -18
- data/doc/classes/Namespace.src/M000036.html +0 -18
- data/doc/classes/Namespace.src/M000037.html +0 -19
- data/doc/classes/Namespace.src/M000038.html +0 -36
- data/doc/classes/Namespace.src/M000039.html +0 -21
- data/doc/classes/Namespace.src/M000040.html +0 -18
- data/doc/classes/Namespace.src/M000041.html +0 -18
- data/doc/classes/PageTemplate.html +0 -380
- data/doc/classes/PageTemplate.src/M000010.html +0 -49
- data/doc/classes/PageTemplate.src/M000011.html +0 -18
- data/doc/classes/PageTemplate.src/M000012.html +0 -29
- data/doc/classes/PageTemplate.src/M000013.html +0 -18
- data/doc/classes/PageTemplate.src/M000014.html +0 -18
- data/doc/classes/PageTemplate.src/M000015.html +0 -18
- data/doc/classes/PageTemplate.src/M000016.html +0 -18
- data/doc/classes/PageTemplate.src/M000017.html +0 -18
- data/doc/classes/PageTemplate.src/M000018.html +0 -19
- data/doc/classes/Syntax/CachedParser.html +0 -163
- data/doc/classes/Syntax/CachedParser.src/M000048.html +0 -19
- data/doc/classes/Syntax/CachedParser.src/M000049.html +0 -53
- data/doc/classes/Syntax/Glossary.html +0 -251
- data/doc/classes/Syntax/Glossary.src/M000050.html +0 -20
- data/doc/classes/Syntax/Glossary.src/M000051.html +0 -29
- data/doc/classes/Syntax/Parser.html +0 -261
- data/doc/classes/Syntax/Parser.src/M000052.html +0 -22
- data/doc/classes/Syntax/Parser.src/M000053.html +0 -36
- data/doc/classes/Syntax/Parser.src/M000054.html +0 -18
- data/doc/classes/Syntax/Parser.src/M000055.html +0 -21
- data/doc/classes/Syntax/Parser.src/M000056.html +0 -53
- data/doc/classes/Syntax/Parser.src/M000057.html +0 -111
- data/doc/classes/Syntax.html +0 -139
- data/doc/classes/TextCommand.html +0 -185
- data/doc/classes/TextCommand.src/M000045.html +0 -18
- data/doc/classes/TextCommand.src/M000046.html +0 -18
- data/doc/classes/TextCommand.src/M000047.html +0 -18
- data/doc/classes/UnlessCommand.html +0 -151
- data/doc/classes/UnlessCommand.src/M000028.html +0 -20
- data/doc/classes/ValueCommand.html +0 -186
- data/doc/classes/ValueCommand.src/M000029.html +0 -18
- data/doc/classes/ValueCommand.src/M000030.html +0 -18
- data/doc/classes/ValueCommand.src/M000031.html +0 -20
- data/doc/created.rid +0 -1
- data/doc/files/README_txt.html +0 -224
- data/doc/files/lib/PageTemplate_rb.html +0 -119
- data/doc/fr_class_index.html +0 -44
- data/doc/fr_file_index.html +0 -28
- data/doc/fr_method_index.html +0 -83
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
- data/install.rb +0 -1015
- data/lib/CVS/Entries +0 -2
- data/lib/CVS/Repository +0 -1
- data/lib/CVS/Root +0 -1
- data/lib/PageTemplate.rb~ +0 -1004
- data/pkg/PageTemplate-1.2.0.gem +0 -0
- data/tdata/CVS/Entries +0 -28
- data/tdata/CVS/Repository +0 -1
- data/tdata/CVS/Root +0 -1
- data/tdata/cm.txt +0 -3
- data/tdata/complex.txt +0 -116
- data/tdata/i1.txt +0 -1
- data/tdata/i2.txt +0 -7
- data/tdata/ib1.txt +0 -7
- data/tdata/ib2.txt +0 -13
- data/tdata/ie1.txt +0 -4
- data/tdata/ie2.txt +0 -15
- data/tdata/include.1.txt +0 -1
- data/tdata/include.2.txt +0 -1
- data/tdata/include.3.txt +0 -1
- data/tdata/include.4.nf.out.txt +0 -2
- data/tdata/include.4.out.txt +0 -3
- data/tdata/include.4.txt +0 -2
- data/tdata/include.4a.txt +0 -1
- data/tdata/include.4b.txt +0 -2
- data/tdata/include.5.txt +0 -2
- data/tdata/l1.txt +0 -5
- data/tdata/l2.txt +0 -7
- data/tdata/metadata.1.txt +0 -5
- data/tdata/metadata.2.txt +0 -5
- data/tdata/metadata.3.txt +0 -5
- data/tdata/metadata.4.txt +0 -5
- data/tdata/nm.txt +0 -4
- data/tdata/p/CVS/Entries +0 -5
- data/tdata/p/CVS/Repository +0 -1
- data/tdata/p/CVS/Root +0 -1
- data/tdata/p/CVS/b2.txt,t +0 -0
- data/tdata/p/_b1.txt +0 -0
- data/tdata/p/b1.txt +0 -4
- data/tdata/p/b2.txt +0 -4
- data/tdata/p/b2.txt~ +0 -4
- data/tdata/p/cb1.txt +0 -4
- data/tdata/t.txt +0 -2
- data/tdata/v1.txt +0 -11
- 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
|