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.
- 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
|