ltdtemplate 0.1.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/HISTORY.txt +15 -0
- data/TEMPLATE_MANUAL.html +838 -0
- data/lib/ltdtemplate/code/call.rb +46 -0
- data/lib/ltdtemplate/code/code_block.rb +30 -0
- data/lib/ltdtemplate/code/parameters.rb +40 -0
- data/lib/ltdtemplate/code/subscript.rb +81 -0
- data/lib/ltdtemplate/code/variable.rb +77 -0
- data/lib/ltdtemplate/code.rb +102 -0
- data/lib/ltdtemplate/value/array.rb +168 -0
- data/lib/ltdtemplate/value/boolean.rb +79 -0
- data/lib/ltdtemplate/value/code_block.rb +40 -0
- data/lib/ltdtemplate/value/namespace.rb +135 -0
- data/lib/ltdtemplate/value/nil.rb +27 -0
- data/lib/ltdtemplate/value/number.rb +93 -0
- data/lib/ltdtemplate/value/string.rb +121 -0
- data/lib/ltdtemplate.rb +451 -0
- data/ltdtemplate.gemspec +16 -0
- data/test/00class.rb +20 -0
- data/test/01instance.rb +30 -0
- data/test/02tpl_literal.rb +61 -0
- data/test/03tpl_singletons.rb +48 -0
- data/test/04number.rb +36 -0
- data/test/05string.rb +30 -0
- data/test/06array.rb +34 -0
- metadata +94 -0
data/lib/ltdtemplate.rb
ADDED
@@ -0,0 +1,451 @@
|
|
1
|
+
# LtdTemplate - Ltd (limited) Template
|
2
|
+
#
|
3
|
+
# A template system with limitable resource usage.
|
4
|
+
#
|
5
|
+
# @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
|
6
|
+
# @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
|
7
|
+
# @license MIT License
|
8
|
+
|
9
|
+
class LtdTemplate
|
10
|
+
|
11
|
+
TOKEN_MAP = {
|
12
|
+
?. => :dot, # method separator
|
13
|
+
'..' => :dotdot, # begin named values
|
14
|
+
?( => :lparen, # begin call parameters
|
15
|
+
?, => :comma, # next call parameter
|
16
|
+
?) => :rparen, # end call parameters
|
17
|
+
?[ => :lbrack, # begin array subscripts
|
18
|
+
?] => :rbrack, # end array subscripts
|
19
|
+
?{ => :lbrace, # begin code block
|
20
|
+
?} => :rbrace # end code block
|
21
|
+
}
|
22
|
+
|
23
|
+
# @!attribute [r] exceeded
|
24
|
+
# @return [Symbol, nil]
|
25
|
+
# The resource whose limit was being exceeded when an
|
26
|
+
# LtdTemplate::ResourceLimitExceeded exception was raised.
|
27
|
+
attr_reader :exceeded
|
28
|
+
|
29
|
+
# @!attribute [r] factory_singletons
|
30
|
+
# @return [Hash]
|
31
|
+
# A hash of factory singletons (e.g. nil, true, and false values)
|
32
|
+
# for this template.
|
33
|
+
attr_reader :factory_singletons
|
34
|
+
|
35
|
+
# @!attribute [r] limits
|
36
|
+
# @return [Hash]
|
37
|
+
# A hash of resource limits to enforce during parsing and rendering.
|
38
|
+
attr_reader :limits
|
39
|
+
|
40
|
+
# @!attribute [r] namespace
|
41
|
+
# @return [LtdTemplate::Value::Namespace, nil]
|
42
|
+
# The current namespace (at the bottom of the rendering namespace stack).
|
43
|
+
attr_reader :namespace
|
44
|
+
|
45
|
+
# @!attribute [r] options
|
46
|
+
# @return [Hash]
|
47
|
+
# Instance initialization options
|
48
|
+
attr_reader :options
|
49
|
+
|
50
|
+
# @!attribute [r] usage
|
51
|
+
# @return [Hash]
|
52
|
+
# A hash of resource usage. It is updated after calls to #parse and
|
53
|
+
# #render.
|
54
|
+
attr_reader :usage
|
55
|
+
|
56
|
+
# @!attribute [r] used
|
57
|
+
# @return [Hash]
|
58
|
+
# A hash of used resources for this template
|
59
|
+
attr_reader :used
|
60
|
+
|
61
|
+
# @@classes contains the default factory classes. These can be overridden
|
62
|
+
# globally using the #set_classes class method or per-template using the
|
63
|
+
# #set_classes instance method.
|
64
|
+
@@classes = {
|
65
|
+
#
|
66
|
+
# These represent storable values. Some may also occur as
|
67
|
+
# literals in code blocks.
|
68
|
+
#
|
69
|
+
:array => 'LtdTemplate::Value::Array',
|
70
|
+
:boolean => 'LtdTemplate::Value::Boolean',
|
71
|
+
:explicit_block => 'LtdTemplate::Value::Code_Block',
|
72
|
+
:namespace => 'LtdTemplate::Value::Namespace',
|
73
|
+
:nil => 'LtdTemplate::Value::Nil',
|
74
|
+
:number => 'LtdTemplate::Value::Number',
|
75
|
+
:string => 'LtdTemplate::Value::String',
|
76
|
+
|
77
|
+
#
|
78
|
+
# These only occur as part of code blocks.
|
79
|
+
#
|
80
|
+
:call => 'LtdTemplate::Code::Call',
|
81
|
+
:implied_block => 'LtdTemplate::Code::Code_Block',
|
82
|
+
:parameters => 'LtdTemplate::Code::Parameters',
|
83
|
+
:subscript => 'LtdTemplate::Code::Subscript',
|
84
|
+
:variable => 'LtdTemplate::Code::Variable',
|
85
|
+
}
|
86
|
+
|
87
|
+
# Change default factory classes globally
|
88
|
+
#
|
89
|
+
# @param classes [Hash] A hash of factory symbols and corresponding
|
90
|
+
# classes to be instantiated.
|
91
|
+
# @return [Hash] Returns the current class mapping.
|
92
|
+
def self.set_classes (classes)
|
93
|
+
@@classes.merge! classes
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize (options = {})
|
97
|
+
@classes = @@classes
|
98
|
+
@code = nil
|
99
|
+
@factory_singletons = {}
|
100
|
+
@limits = {}
|
101
|
+
@namespace = nil
|
102
|
+
@options = options
|
103
|
+
@usage = {}
|
104
|
+
@used = {}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Parse a template from a string.
|
108
|
+
#
|
109
|
+
# @param template [String] A template string. Templates look
|
110
|
+
# like <tt>'literal<<template code>>literal...'</tt>.
|
111
|
+
# @return [LtdTemplate]
|
112
|
+
def parse (template)
|
113
|
+
@usage = {}
|
114
|
+
tokens = []
|
115
|
+
literal = true
|
116
|
+
trim_next = false
|
117
|
+
|
118
|
+
# Template "text<<code>>text<<code>>..." =>
|
119
|
+
# (text, code, text, code, ...)
|
120
|
+
template.split(/<<(?!<)((?:[^<>]|<[^<]|>[^>])*)>>/s).each do |part|
|
121
|
+
if part.length > 0
|
122
|
+
if literal
|
123
|
+
part.sub!(/^\s+/s, '') if trim_next
|
124
|
+
tokens.push [ :ext_string, part ]
|
125
|
+
else
|
126
|
+
if part[0] == '.'
|
127
|
+
tokens[-1][1].sub!(/\s+$/s, '') if
|
128
|
+
tokens[0] and tokens[-1][0] == :ext_string
|
129
|
+
part = part[1..-1] if part.length > 1
|
130
|
+
end
|
131
|
+
part = part[0..-2] if trim_next = (part[-1] == '.')
|
132
|
+
tokens += get_tokens part
|
133
|
+
end
|
134
|
+
else
|
135
|
+
trim_next = false
|
136
|
+
end
|
137
|
+
literal = !literal
|
138
|
+
end
|
139
|
+
|
140
|
+
@code = parse_template tokens
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
# Change default factory classes for this template.
|
145
|
+
#
|
146
|
+
# @param classes [Hash] A hash of factory symbols and corresponding
|
147
|
+
# classes to be instantiated.
|
148
|
+
# @return [LtdTemplate]
|
149
|
+
def set_classes (classes)
|
150
|
+
@classes.merge! classes
|
151
|
+
self
|
152
|
+
end
|
153
|
+
|
154
|
+
# Generate new code/value objects.
|
155
|
+
#
|
156
|
+
# @param type [Symbol] The symbol for the type of object to generate,
|
157
|
+
# e.g. :number, :string, :implicit_block, etc.
|
158
|
+
# @param args [Array] Type-specific initialization parameters.
|
159
|
+
# @return Returns the new code/value object.
|
160
|
+
def factory (type, *args)
|
161
|
+
use :factory
|
162
|
+
type = @classes[type]
|
163
|
+
file = type.downcase.gsub '::', '/'
|
164
|
+
require file
|
165
|
+
eval(type).instance(self, *args)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Render the template.
|
169
|
+
#
|
170
|
+
# The options hash may include :parameters, which may be an array or
|
171
|
+
# hash. These values will form the parameter array "_" in the root
|
172
|
+
# namespace.
|
173
|
+
#
|
174
|
+
# @param options [Hash] Rendering options.
|
175
|
+
# @return [String] The result of rendering the template.
|
176
|
+
def render (options = {})
|
177
|
+
@exceeded = nil # No limit exceeded yet
|
178
|
+
@namespace = nil # Reset the namespace stack between runs
|
179
|
+
@usage = {} # Reset resource usage
|
180
|
+
@used = {} # No libraries used yet
|
181
|
+
|
182
|
+
#
|
183
|
+
# Accept template parameters from an array or hash.
|
184
|
+
#
|
185
|
+
parameters = factory :array
|
186
|
+
parameters.set_from_array options[:parameters] if
|
187
|
+
options[:parameters].is_a? Array
|
188
|
+
parameters.set_from_hash options[:parameters] if
|
189
|
+
options[:parameters].is_a? Hash
|
190
|
+
|
191
|
+
#
|
192
|
+
# Create the root namespace and evaluate the template code.
|
193
|
+
#
|
194
|
+
push_namespace 'render', parameters
|
195
|
+
@code ? @code.get_value.to_text : ''
|
196
|
+
end
|
197
|
+
|
198
|
+
# Push a new namespace onto the namespace stack.
|
199
|
+
#
|
200
|
+
# @param method [String] The (native) method string. This will be
|
201
|
+
# available as <tt>$.method</tt> within the template.
|
202
|
+
# @param parameters [Array<LtdTemplate::Code>] These are code blocks
|
203
|
+
# to set the namespace parameters (available as the "_" array in the
|
204
|
+
# template).
|
205
|
+
# @param opts [Hash] Options hash.
|
206
|
+
# @option opts [LtdTemplate::Value] :target The target of the method
|
207
|
+
# call. This will be available as <tt>$.target</tt> within the template.
|
208
|
+
def push_namespace (method, parameters = nil, opts = {})
|
209
|
+
use :namespaces
|
210
|
+
use :namespace_depth
|
211
|
+
@namespace = factory :namespace, method, parameters, @namespace
|
212
|
+
@namespace.target = opts[:target] if opts[:target]
|
213
|
+
end
|
214
|
+
|
215
|
+
# Pop the current namespace from the stack.
|
216
|
+
def pop_namespace
|
217
|
+
if @namespace.parent
|
218
|
+
@namespace = @namespace.parent
|
219
|
+
use :namespace_depth, -1
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Track incremental usage of a resource.
|
224
|
+
#
|
225
|
+
# @param resource [Symbol] The resource being used.
|
226
|
+
# @param amount [Integer] The additional amount of the resource being
|
227
|
+
# used (or released, if negative).
|
228
|
+
def use (resource, amount = 1)
|
229
|
+
@usage[resource] ||= 0
|
230
|
+
@usage[resource] += amount
|
231
|
+
check_limit resource
|
232
|
+
end
|
233
|
+
|
234
|
+
# Track peak usage of a resource.
|
235
|
+
#
|
236
|
+
# @param resource [Symbol] The resource being used.
|
237
|
+
# @param amount [Integer] The total amount of the resource currently
|
238
|
+
# being used.
|
239
|
+
def using (resource, amount)
|
240
|
+
@usage[resource] ||= 0
|
241
|
+
@usage[resource] = amount if amount > @usage[resource]
|
242
|
+
check_limit resource
|
243
|
+
end
|
244
|
+
|
245
|
+
# Throw an exception if a resource limit has been exceeded.
|
246
|
+
#
|
247
|
+
# @param resource [Symbol] The resource limit to be checked.
|
248
|
+
def check_limit (resource)
|
249
|
+
if @limits[resource] and @usage[resource] and
|
250
|
+
@usage[resource] > @limits[resource]
|
251
|
+
@exceeded = resource
|
252
|
+
raise LtdTemplate::ResourceLimitExceeded
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Convert a string into an array of parsing tokens.
|
257
|
+
#
|
258
|
+
# @param str [String] The string to split into parsing tokens.
|
259
|
+
# @return [Array<Array>]
|
260
|
+
def get_tokens (str)
|
261
|
+
tokens = []
|
262
|
+
str.split(%r{(
|
263
|
+
/\*.*?\*/ # /* comment */
|
264
|
+
|
|
265
|
+
'(?:\\.|[^\.,\[\](){}\s])+# 'string
|
266
|
+
|
|
267
|
+
"(?:\\"|[^"])*" # "string"
|
268
|
+
|
|
269
|
+
[@^][a-zA-Z0-9_]+ # root or parent identifier
|
270
|
+
|
|
271
|
+
[a-zA-Z_][a-zA-Z0-9_]*# alphanumeric identifier
|
272
|
+
|
|
273
|
+
-?\d+(?:\.\d+)? # integer or real numeric literal
|
274
|
+
|
|
275
|
+
\.\. # begin keyed values
|
276
|
+
|
|
277
|
+
[\.(,)\[\]{}] # methods, calls, elements, blocks
|
278
|
+
|
|
279
|
+
\s+
|
280
|
+
)}mx).grep(/\S/).each do |token|
|
281
|
+
if token =~ %r{^/\*.*\*/$}s
|
282
|
+
# Ignore comment
|
283
|
+
elsif token =~ /^'(.*)/s or token =~ /^"(.*)"$/s
|
284
|
+
# String literal
|
285
|
+
#tokens.push [ :string, $1.gsub(/\\(.)/, '\1') ]
|
286
|
+
tokens.push [ :string, parse_strlit($1) ]
|
287
|
+
elsif token =~ /^-?\d+(?:\.\d+)?/
|
288
|
+
# Numeric literal
|
289
|
+
tokens.push [ :number, token ]
|
290
|
+
elsif TOKEN_MAP[token]
|
291
|
+
# Delimiter
|
292
|
+
tokens.push [ TOKEN_MAP[token] ]
|
293
|
+
elsif token =~ /^[@^][a-z0-9_]|^[a-z_]|^\$$/i
|
294
|
+
# Variable or alphanumeric method name
|
295
|
+
tokens.push [ :name, token ]
|
296
|
+
else
|
297
|
+
# Punctuated method name
|
298
|
+
tokens.push [ :method, token ]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
tokens
|
303
|
+
end
|
304
|
+
|
305
|
+
# This is the top-level token parser.
|
306
|
+
#
|
307
|
+
# @param tokens [Array<Array>] The tokens to be parsed.
|
308
|
+
# @return [LtdTemplate::Code] The implementation code.
|
309
|
+
def parse_template (tokens); parse_block tokens; end
|
310
|
+
|
311
|
+
# Parse a code block, stopping at any stop token.
|
312
|
+
#
|
313
|
+
# @param tokens [Array<Array>] The raw token stream.
|
314
|
+
# @param stops [Array<Symbol>] An optional list of stop tokens, such
|
315
|
+
# as :comma (comma) or :rparen (right parenthesis).
|
316
|
+
# @return [LtdTemplate::Code]
|
317
|
+
def parse_block (tokens, stops = [])
|
318
|
+
code = []
|
319
|
+
while tokens[0]
|
320
|
+
break if stops.include? tokens[0][0]
|
321
|
+
|
322
|
+
token = tokens.shift# Consume the current token
|
323
|
+
case token[0]
|
324
|
+
when :string, :ext_string # string literal
|
325
|
+
code.push factory(:string, token[1])
|
326
|
+
when :number # numeric literal
|
327
|
+
code.push factory(:number, token[1])
|
328
|
+
when :name # variable
|
329
|
+
code.push factory(:variable, token[1])
|
330
|
+
when :lbrack # variable element subscripts
|
331
|
+
subs = parse_subscripts tokens
|
332
|
+
code.push factory(:subscript, code.pop, subs) if code[0]
|
333
|
+
when :dot # method call w/ or w/out parameters
|
334
|
+
case tokens[0][0]
|
335
|
+
when :name, :method, :string
|
336
|
+
method = tokens.shift
|
337
|
+
if tokens[0] and tokens[0][0] == :lparen
|
338
|
+
tokens.shift # Consume (
|
339
|
+
params = parse_parameters tokens
|
340
|
+
else
|
341
|
+
params = factory :parameters
|
342
|
+
end
|
343
|
+
code.push factory(:call, code.pop, method[1], params) if
|
344
|
+
code[0]
|
345
|
+
end if tokens[0]
|
346
|
+
when :method # punctuated method call
|
347
|
+
# Insert the implied dot and re-parse
|
348
|
+
tokens.unshift [ :dot ], token
|
349
|
+
when :lparen # call
|
350
|
+
params = parse_parameters tokens
|
351
|
+
code.push factory(:call, code.pop, 'call', params) if code[0]
|
352
|
+
when :lbrace # explicit code block
|
353
|
+
code.push factory(:explicit_block,
|
354
|
+
parse_block(tokens, [ :rbrace ]))
|
355
|
+
tokens.shift if tokens[0] # Consume }
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
(code.size == 1) ? code[0] : factory(:implied_block, code)
|
360
|
+
end
|
361
|
+
|
362
|
+
# Parse subscripts after the opening left bracket
|
363
|
+
#
|
364
|
+
# @param tokens [Array<Array>]] The token stream.
|
365
|
+
# @return [Array<LtdTemplate::Code>]
|
366
|
+
def parse_subscripts (tokens)
|
367
|
+
subs = parse_list tokens, [ :rbrack ], [ :lbrack ]
|
368
|
+
tokens.shift # Consume ]
|
369
|
+
subs
|
370
|
+
end
|
371
|
+
|
372
|
+
# Parse a positional and/or named parameter list
|
373
|
+
#
|
374
|
+
# @param tokens [Array<Array>] The token stream.
|
375
|
+
# @return [LtdTemplate::Code::Parameters]
|
376
|
+
def parse_parameters (tokens)
|
377
|
+
positional = parse_list tokens, [ :dotdot, :rparen ]
|
378
|
+
|
379
|
+
if tokens[0] and tokens[0][0] == :dotdot
|
380
|
+
tokens.shift # Consume ..
|
381
|
+
named = parse_list tokens, [ :rparen ]
|
382
|
+
else
|
383
|
+
named = nil
|
384
|
+
end
|
385
|
+
|
386
|
+
tokens.shift # Consume )
|
387
|
+
factory :parameters, positional, named
|
388
|
+
end
|
389
|
+
|
390
|
+
# Common code for parsing various lists.
|
391
|
+
#
|
392
|
+
# @param tokens [Array<Array>] The remaining unparsed tokens.
|
393
|
+
# @param stops [Array<Symbol>] The list of tokens that stop parsing
|
394
|
+
# of the list.
|
395
|
+
# @param resume [Array<Symbol>] The list of tokens that will resume
|
396
|
+
# parsing if they occur after a stop token (e.g. subscript parsing
|
397
|
+
# stops at ']' but resumes if followed by '[').
|
398
|
+
# @return [Array<LtdTemplate::Code>]
|
399
|
+
def parse_list (tokens, stops, resume = [])
|
400
|
+
list = []
|
401
|
+
block_stops = stops + [ :comma ]
|
402
|
+
while tokens[0]
|
403
|
+
if !stops.include? tokens[0][0]
|
404
|
+
block = parse_block tokens, block_stops
|
405
|
+
list.push block if block
|
406
|
+
tokens.shift if tokens[0] and tokens[0][0] == :comma
|
407
|
+
elsif tokens[1] and resume.include? tokens[1][0]
|
408
|
+
tokens.shift 2 # Consume stop and resume tokens
|
409
|
+
else
|
410
|
+
break
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
list
|
415
|
+
end
|
416
|
+
|
417
|
+
# Parse escape sequences in string literals.
|
418
|
+
#
|
419
|
+
# These are the same as in Ruby double-quoted strings:
|
420
|
+
# \M-\C-x meta-control-x
|
421
|
+
# \M-x meta-x
|
422
|
+
# \C-x, \cx control-x
|
423
|
+
# \udddd Unicode four-digit, 16-bit hexadecimal code point dddd
|
424
|
+
# \xdd two-digit, 8-bit hexadecimal dd
|
425
|
+
# \ddd one-, two-, or three-digit, 8-bit octal ddd
|
426
|
+
# \c any other character c is just itself
|
427
|
+
#
|
428
|
+
# @param raw [String] The original (raw) string.
|
429
|
+
# @return [String] The string after escape processing.
|
430
|
+
def parse_strlit (raw)
|
431
|
+
raw.split(%r{(\\M-\\C-.|\\M-.|\\C-.|\\c.|
|
432
|
+
\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-f]{2}|\\[0-7]{1,3}|\\.)}x).
|
433
|
+
map do |part|
|
434
|
+
case part
|
435
|
+
when /\\M-\\C-(.)/ then ($1.ord & 31 | 128).chr
|
436
|
+
when /\\M-(.)/ then ($1.ord | 128).chr
|
437
|
+
when /\\C-(.)/, /\\c(.)/ then ($1.ord & 31).chr
|
438
|
+
when /\\u(.*)/ then $1.to_i(16).chr(Encoding::UTF_16BE)
|
439
|
+
when /\\x(..)/ then $1.to_i(16).chr
|
440
|
+
when /\\([0-7]+)/ then $1.to_i(8).chr
|
441
|
+
when /\\(.)/ then "\a\b\e\f\n\r\s\t\v"["abefnrstv".
|
442
|
+
index($1) || 9] || $1
|
443
|
+
else part
|
444
|
+
end
|
445
|
+
end.join ''
|
446
|
+
end
|
447
|
+
|
448
|
+
end
|
449
|
+
|
450
|
+
# This exception is raised when a resource limit is exceeded.
|
451
|
+
class LtdTemplate::ResourceLimitExceeded < RuntimeError; end
|
data/ltdtemplate.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "ltdtemplate"
|
3
|
+
s.version = "0.1.0"
|
4
|
+
s.date = "2013-07-12"
|
5
|
+
s.authors = ["Brian Katzung"]
|
6
|
+
s.email = ["briank@kappacs.com"]
|
7
|
+
s.homepage = "http://rubygems.org/gems/ltdtemplate"
|
8
|
+
s.summary = "A template system with limitable resource usage"
|
9
|
+
s.description = "A template system with limitable resource usage, e.g. for administrator-editable message localization"
|
10
|
+
s.license = "MIT"
|
11
|
+
|
12
|
+
s.files = Dir.glob("lib/**/*") +
|
13
|
+
%w{ltdtemplate.gemspec HISTORY.txt TEMPLATE_MANUAL.html}
|
14
|
+
s.test_files = Dir.glob("test/**/[0-9]*.rb")
|
15
|
+
s.require_path = 'lib'
|
16
|
+
end
|
data/test/00class.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ltdtemplate'
|
3
|
+
|
4
|
+
class TestLtdTemplate_00 < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def test_cmethod_new
|
7
|
+
assert_respond_to LtdTemplate, :new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_cmethod_set_classes
|
11
|
+
assert_respond_to LtdTemplate, :set_classes
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_new_1
|
15
|
+
assert LtdTemplate.new, "Failed to create new LtdTemplate"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
# END
|
data/test/01instance.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ltdtemplate'
|
3
|
+
|
4
|
+
class TestLtdTemplate_01 < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@tpl = LtdTemplate.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_imethods_user_api
|
11
|
+
[
|
12
|
+
:set_classes, :parse, :render
|
13
|
+
].each { |method| assert_respond_to @tpl, method }
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_imethods_library_api
|
17
|
+
[
|
18
|
+
:push_namespace, :pop_namespace, :use, :using, :get_tokens
|
19
|
+
].each { |method| assert_respond_to @tpl, method }
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_imethods_internal_api
|
23
|
+
[
|
24
|
+
:check_limit
|
25
|
+
].each { |method| assert_respond_to @tpl, method }
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# END
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ltdtemplate'
|
3
|
+
|
4
|
+
class TestLtdTemplate_02 < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@tpl = LtdTemplate.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_template_literals1
|
11
|
+
@tpl.parse '<<>>'
|
12
|
+
assert_equal '', @tpl.render
|
13
|
+
|
14
|
+
@tpl.parse 'literal1'
|
15
|
+
assert_equal 'literal1', @tpl.render
|
16
|
+
|
17
|
+
@tpl.parse 'literal2<<>>'
|
18
|
+
assert_equal 'literal2', @tpl.render
|
19
|
+
|
20
|
+
@tpl.parse '<<>>literal3'
|
21
|
+
assert_equal 'literal3', @tpl.render
|
22
|
+
|
23
|
+
@tpl.parse 'literal<<>>4'
|
24
|
+
assert_equal 'literal4', @tpl.render
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_template_literals2
|
28
|
+
@tpl.parse '<<<<>>'
|
29
|
+
assert_equal '<<', @tpl.render
|
30
|
+
|
31
|
+
@tpl.parse '<<>>>>'
|
32
|
+
assert_equal '>>', @tpl.render
|
33
|
+
|
34
|
+
@tpl.parse '<<<<>>>>'
|
35
|
+
assert_equal '<<>>', @tpl.render
|
36
|
+
|
37
|
+
@tpl.parse 'a<<b<<c>>d>>e'
|
38
|
+
assert_equal 'a<<bd>>e', @tpl.render
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_template_trim
|
43
|
+
@tpl.parse " \n <<'x>> \n "
|
44
|
+
assert_equal " \n x \n ", @tpl.render
|
45
|
+
|
46
|
+
@tpl.parse " \n <<.'x>> \n "
|
47
|
+
assert_equal "x \n ", @tpl.render
|
48
|
+
|
49
|
+
@tpl.parse " \n <<'x.>> \n "
|
50
|
+
assert_equal " \n x", @tpl.render
|
51
|
+
|
52
|
+
@tpl.parse " \n <<.'x.>> \n "
|
53
|
+
assert_equal "x", @tpl.render
|
54
|
+
|
55
|
+
@tpl.parse " \n <<.>> \n "
|
56
|
+
assert_equal "", @tpl.render
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
# END
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ltdtemplate'
|
3
|
+
|
4
|
+
class TestLtdTemplate_03 < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@tpl1 = LtdTemplate.new
|
8
|
+
@tpl2 = LtdTemplate.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_nil
|
12
|
+
nil1a = @tpl1.factory :nil
|
13
|
+
nil1b = @tpl1.factory :nil
|
14
|
+
assert_equal nil1a.object_id, nil1b.object_id, "Shared nil in tpl1"
|
15
|
+
nil2a = @tpl2.factory :nil
|
16
|
+
nil2b = @tpl2.factory :nil
|
17
|
+
assert_equal nil2a.object_id, nil2b.object_id, "Shared nil in tpl2"
|
18
|
+
refute_equal nil1a.object_id, nil2a.object_id,
|
19
|
+
"Different nil in tpl1, tpl2"
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_boolean
|
23
|
+
true1a = @tpl1.factory :boolean, true
|
24
|
+
true1b = @tpl1.factory :boolean, true
|
25
|
+
assert_equal true1a.object_id, true1b.object_id,
|
26
|
+
"Shared true in tpl1"
|
27
|
+
true2a = @tpl2.factory :boolean, true
|
28
|
+
true2b = @tpl2.factory :boolean, true
|
29
|
+
assert_equal true2a.object_id, true2b.object_id,
|
30
|
+
"Shared true in tpl2"
|
31
|
+
refute_equal true1a.object_id, true2a.object_id,
|
32
|
+
"Different true in tpl1, tpl2"
|
33
|
+
|
34
|
+
false1a = @tpl1.factory :boolean, false
|
35
|
+
false1b = @tpl1.factory :boolean, false
|
36
|
+
assert_equal false1a.object_id, false1b.object_id,
|
37
|
+
"Shared false in tpl1"
|
38
|
+
false2a = @tpl2.factory :boolean, false
|
39
|
+
false2b = @tpl2.factory :boolean, false
|
40
|
+
assert_equal false2a.object_id, false2b.object_id
|
41
|
+
"Shared false in tpl2"
|
42
|
+
refute_equal false1a.object_id, false2a.object_id
|
43
|
+
"Different false in tpl1, tpl2"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
# END
|
data/test/04number.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ltdtemplate'
|
3
|
+
|
4
|
+
class TestLtdTemplate_04 < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@tpl = LtdTemplate.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_imethods
|
11
|
+
num = @tpl.factory :number
|
12
|
+
[
|
13
|
+
:get_value, :to_boolean, :to_native, :to_text
|
14
|
+
].each { |method| assert_respond_to num, method }
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_basic
|
18
|
+
@tpl.parse '<<1>>'
|
19
|
+
assert_equal("1", @tpl.render, "literal")
|
20
|
+
@tpl.parse '<<1.type>>'
|
21
|
+
assert_equal("number", @tpl.render, "type")
|
22
|
+
@tpl.parse '<<1+(2,3)>>'
|
23
|
+
assert_equal("6", @tpl.render, "1+2+3")
|
24
|
+
@tpl.parse '<<1->>'
|
25
|
+
assert_equal("-1", @tpl.render, "neg 1")
|
26
|
+
@tpl.parse '<<1-(2,3)>>'
|
27
|
+
assert_equal("-4", @tpl.render, "1-2-3")
|
28
|
+
@tpl.parse '<<2*(3,4)>>'
|
29
|
+
assert_equal("24", @tpl.render, "2*3*4")
|
30
|
+
@tpl.parse '<<24/(2,3)>>'
|
31
|
+
assert_equal("4", @tpl.render, "24/2/3")
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# END
|
data/test/05string.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'ltdtemplate'
|
3
|
+
|
4
|
+
class TestLtdTemplate_05 < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@tpl = LtdTemplate.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_imethods
|
11
|
+
num = @tpl.factory :string, ''
|
12
|
+
[
|
13
|
+
:get_value, :to_boolean, :to_native, :to_text
|
14
|
+
].each { |method| assert_respond_to num, method }
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_basic
|
18
|
+
@tpl.parse '<<"str">>'
|
19
|
+
assert_equal("str", @tpl.render, "literal")
|
20
|
+
@tpl.parse '<<"str".type>>'
|
21
|
+
assert_equal("string", @tpl.render, "type")
|
22
|
+
@tpl.parse '<<"str"+("ing","value")>>'
|
23
|
+
assert_equal("stringvalue", @tpl.render, "string +")
|
24
|
+
@tpl.parse '<<"str"*(3)>>'
|
25
|
+
assert_equal("strstrstr", @tpl.render, "string *")
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# END
|