ltdtemplate 0.2.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +10 -1
- data/Gemfile +2 -1
- data/RESOURCES +18 -32
- data/TEMPLATE_MANUAL.html +126 -47
- data/lib/ltdtemplate.rb +352 -242
- data/lib/ltdtemplate/code.rb +14 -87
- data/lib/ltdtemplate/code/call.rb +20 -16
- data/lib/ltdtemplate/code/parameters.rb +28 -31
- data/lib/ltdtemplate/code/sequence.rb +39 -0
- data/lib/ltdtemplate/code/subscript.rb +57 -50
- data/lib/ltdtemplate/code/variable.rb +22 -39
- data/lib/ltdtemplate/proxy.rb +26 -0
- data/lib/ltdtemplate/proxy/array.rb +258 -0
- data/lib/ltdtemplate/proxy/boolean.rb +74 -0
- data/lib/ltdtemplate/proxy/match.rb +40 -0
- data/lib/ltdtemplate/proxy/nil.rb +27 -0
- data/lib/ltdtemplate/proxy/number.rb +77 -0
- data/lib/ltdtemplate/proxy/regexp.rb +74 -0
- data/lib/ltdtemplate/proxy/string.rb +196 -0
- data/lib/ltdtemplate/value.rb +94 -0
- data/lib/ltdtemplate/value/array_splat.rb +34 -0
- data/lib/ltdtemplate/value/code_block.rb +21 -17
- data/lib/ltdtemplate/value/namespace.rb +77 -79
- data/ltdtemplate.gemspec +2 -2
- data/test/04number.rb +0 -7
- data/test/05string.rb +0 -7
- data/test/06array.rb +0 -9
- data/test/07each.rb +3 -3
- data/test/08interpolate.rb +1 -1
- data/test/10missing_meth.rb +1 -1
- data/test/11classes.rb +9 -9
- metadata +15 -13
- data/lib/ltdtemplate/code/code_block.rb +0 -30
- data/lib/ltdtemplate/value/array.rb +0 -210
- data/lib/ltdtemplate/value/boolean.rb +0 -82
- data/lib/ltdtemplate/value/nil.rb +0 -30
- data/lib/ltdtemplate/value/number.rb +0 -96
- data/lib/ltdtemplate/value/string.rb +0 -215
- data/lib/test.rb +0 -10
- data/test/03tpl_singletons.rb +0 -48
data/CHANGELOG
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
2014-05-07 Version 1.0.0
|
2
|
+
|
3
|
+
Major rewrite to work with native types proxied into a template
|
4
|
+
Rubyverse.
|
5
|
+
|
6
|
+
RESOURCES file now built from shell script find_resources.
|
7
|
+
|
8
|
+
Added support for regexp.match(string) and string.match(regexp).
|
9
|
+
|
1
10
|
2014-03-15 Version 0.2.4
|
2
11
|
|
3
12
|
Fixed a bug in the parameter scalar assignment determination.
|
@@ -31,7 +40,7 @@
|
|
31
40
|
|
32
41
|
2013-07-28 Version 0.2.1
|
33
42
|
|
34
|
-
Added array
|
43
|
+
Added array.\{each,each_rnd,each_seq} methods (executing the code
|
35
44
|
block supplied as the first parameter with parameters key and
|
36
45
|
value).
|
37
46
|
|
data/Gemfile
CHANGED
data/RESOURCES
CHANGED
@@ -1,32 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
A string may only be converted to a regexp if limit regexp
|
20
|
-
is set to false before rendering
|
21
|
-
|
22
|
-
string_length
|
23
|
-
The combined total length of strings created
|
24
|
-
|
25
|
-
subscript_depth
|
26
|
-
The maximum subsubscript depth
|
27
|
-
|
28
|
-
subscripts
|
29
|
-
The total number of subscripts
|
30
|
-
|
31
|
-
use
|
32
|
-
The total number of "$.use" calls
|
1
|
+
array (proxy/array.rb): Total number of arrays created
|
2
|
+
array_growth (code/parameters.rb): Increases in array sizes
|
3
|
+
array_growth (proxy/array.rb): Increases in array sizes
|
4
|
+
array_size (proxy/array.rb): Size of largest modified array
|
5
|
+
call_depth (code/call.rb): The current method call depth
|
6
|
+
calls (code/call.rb): Total number of method calls
|
7
|
+
code_steps (code/sequence.rb): Total number of code steps executed
|
8
|
+
iterations (value/namespace.rb): Total loop iterations
|
9
|
+
string_length (code/sequence.rb): Length of longest modified string
|
10
|
+
string_length (proxy/array.rb): Length of longest modified string
|
11
|
+
string_length (proxy/regexp.rb): Length of longest modified string
|
12
|
+
string_length (proxy/string.rb): Length of longest modified string
|
13
|
+
string_total (proxy/array.rb): Combined length of computed strings
|
14
|
+
string_total (proxy/regexp.rb): Combined length of computed strings
|
15
|
+
string_total (proxy/string.rb): Combined length of computed strings
|
16
|
+
subscript_depth (code/subscript.rb): Deepest subscript depth
|
17
|
+
subscripts (code/subscript.rb): Total number of subscripts
|
18
|
+
use (value/namespace.rb): Total $.use invocations
|
data/TEMPLATE_MANUAL.html
CHANGED
@@ -11,7 +11,7 @@ td:first-child, th:first-child { padding-left: 0.3em }
|
|
11
11
|
</head>
|
12
12
|
<body>
|
13
13
|
<h1 id='top'>LtdTemplate Manual<br>
|
14
|
-
<span style='font-size: smaller'>Version 0.
|
14
|
+
<span style='font-size: smaller'>Version 1.0.0; 2014-05-07</span></h1>
|
15
15
|
|
16
16
|
<h2>Author(s)</h2>
|
17
17
|
|
@@ -100,8 +100,8 @@ themselves.</p>
|
|
100
100
|
<li><a href='#assignment_syntax'>Assignments</a></li>
|
101
101
|
<li><a href='#predefined_vars'>Pre-Defined Variables</a></li>
|
102
102
|
<li><a href='#codeseq_syntax'>Code Sequences</a></li>
|
103
|
-
<li><a href='#codeblock_syntax'>Code Blocks</a></li>
|
104
|
-
<li><a href='#subscript_syntax'>Subscripts
|
103
|
+
<li><a href='#codeblock_syntax'>Code Blocks And Bindings</a></li>
|
104
|
+
<li><a href='#subscript_syntax'>Subscripts</a></li>
|
105
105
|
</ul>
|
106
106
|
|
107
107
|
<h3 id='comment_syntax'>Comments</h3>
|
@@ -190,8 +190,8 @@ digits.</p>
|
|
190
190
|
|
191
191
|
<blockquote><code>0.125 1 -2 3.0 -4.5</code></blockquote>
|
192
192
|
|
193
|
-
<p>LtdTemplate does not support numeric literals in scientific
|
194
|
-
or bases other than ten.</p>
|
193
|
+
<p>LtdTemplate does not currently support numeric literals in scientific
|
194
|
+
notation or bases other than ten.</p>
|
195
195
|
|
196
196
|
<p>[<a href='#contents'>Contents</a>] [<a href='#code_syntax'>Template Code Syntax</a>]</p>
|
197
197
|
|
@@ -269,10 +269,12 @@ if not immediately preceded by punctuation.</p>
|
|
269
269
|
|
270
270
|
<h3 id='assignment_syntax'>Assignments</h3>
|
271
271
|
|
272
|
-
<p>Variables support two assignment
|
273
|
-
|
274
|
-
assignment does nothing if the variable is
|
275
|
-
reference to) a value
|
272
|
+
<p>Variables (and array subscripts, covered later) support two assignment
|
273
|
+
method calls: unconditional (<code>=</code>) and conditional
|
274
|
+
(<code>?=</code>). A conditional assignment does nothing if the variable is
|
275
|
+
already bound to (holds a reference to) a value other than the nil
|
276
|
+
value. (This is a change from versions prior to 1.0.0; in those versions
|
277
|
+
even a nil value prevented a conditional assignment.) Assignments do not
|
276
278
|
render in the template output.</p>
|
277
279
|
|
278
280
|
<p>If called with a single parameter and no "dot dot" symbol, the value of
|
@@ -310,11 +312,11 @@ others will be created in the current namespace.</p>
|
|
310
312
|
destroyed when the code block completes.</td></tr>
|
311
313
|
|
312
314
|
<tr><td><code>^</code></td>
|
313
|
-
<td>This variable refers to the previous namespace if there
|
314
|
-
or <a href='#nil_value'>nil</a> if there isn't.</td></tr>
|
315
|
+
<td>This variable refers to the previous (parent) namespace if there
|
316
|
+
is one, or <a href='#nil_value'>nil</a> if there isn't.</td></tr>
|
315
317
|
|
316
318
|
<tr><td><code>@</code></td>
|
317
|
-
<td>This variable refers to the first (also known as "root" or
|
319
|
+
<td>This variable refers to the first (also known as the "root" or
|
318
320
|
"top-level") namespace.</td></tr>
|
319
321
|
|
320
322
|
<tr><td><code>_</code></td>
|
@@ -333,20 +335,20 @@ expressions. <a href='#template_syntax'>Templates</a>,
|
|
333
335
|
<a href='#call_syntax'>method call</a> parameters, and
|
334
336
|
<a href='#codeblock_syntax'>code block</a> bodies are all code sequences.</p>
|
335
337
|
|
336
|
-
<p>If a code sequence consists of a single
|
337
|
-
|
338
|
-
|
338
|
+
<p>If a code sequence consists of a single value and does not constitute
|
339
|
+
the entire template code, it is retained as-is. Otherwise, the string
|
340
|
+
value of each element in the sequence, if any, is combined to
|
339
341
|
produce the result of the code sequence.</p>
|
340
342
|
|
341
343
|
<blockquote><code>'Hello $.true ", " num=(5) num-(4) " world!"<br>
|
342
|
-
/* 5 and 4 are single
|
344
|
+
/* 5 and 4 are single values in their code sequences */<br>
|
343
345
|
/* $.true and num=(5) render nothing */<br>
|
344
346
|
/* num-(4) returns the number 1 but it's string value gets used */<br>
|
345
347
|
/* => Hello, 1 world! */</code></blockquote>
|
346
348
|
|
347
349
|
<p>[<a href='#contents'>Contents</a>] [<a href='#code_syntax'>Template Code Syntax</a>]</p>
|
348
350
|
|
349
|
-
<h3 id='codeblock_syntax'>Code Blocks</h3>
|
351
|
+
<h3 id='codeblock_syntax'>Code Blocks And Bindings</h3>
|
350
352
|
|
351
353
|
<p>If you surround a <a href='#codeseq_syntax'>code sequence</a>
|
352
354
|
by braces (<code>{</code> and <code>}</code>), you create a code
|
@@ -358,9 +360,34 @@ the code sequence within the code block.</p>
|
|
358
360
|
render_method.hi " " render_method.there
|
359
361
|
/* => hi there */</code></blockquote>
|
360
362
|
|
363
|
+
<p>Code blocks (or other values) may be bound to values using the
|
364
|
+
<code>methods</code> method and subsequently called like
|
365
|
+
<a href='#call_syntax'>methods</a>.
|
366
|
+
|
367
|
+
<p>The <code>methods</code> method takes any number of name-and-binding
|
368
|
+
pairs to be added or updated. If there are an odd number of parameters
|
369
|
+
(i.e. there is a final name without a binding), the existing binding, if any,
|
370
|
+
associated with that name is returned. Specifying nil ($.nil) as the
|
371
|
+
binding will actually delete any existing binding for the given name.</p>
|
372
|
+
|
373
|
+
<blockquote><code>@.methods('greet,{"Hello, "_[0]"."})<br>
|
374
|
+
@.greet('Dave) /* => Hello, Dave. */</code></blockquote>
|
375
|
+
|
376
|
+
<p>You can also bind code blocks to proxy objects to be used by an entire
|
377
|
+
class of values. For convenience, binding to an unset variable automatically
|
378
|
+
causes the variable to be set to an empty string.</p>
|
379
|
+
|
380
|
+
<blockquote><code>@Array?=(') /* bind the Array proxy to a unique object
|
381
|
+
(before 1.0.0) */<br>
|
382
|
+
@Array.methods('list, { $.target.join(", ") }) /* bind the list method */<br>
|
383
|
+
$.*(1, 2, 3).list /* => 1, 2, 3 */</code></blockquote>
|
384
|
+
|
385
|
+
<p>Methods added this way are only called if there is no standard method
|
386
|
+
with the same name.</p>
|
387
|
+
|
361
388
|
<p>[<a href='#contents'>Contents</a>] [<a href='#code_syntax'>Template Code Syntax</a>]</p>
|
362
389
|
|
363
|
-
<h3 id='subscript_syntax'>Subscripts
|
390
|
+
<h3 id='subscript_syntax'>Subscripts</h3>
|
364
391
|
|
365
392
|
<p>Any expression that evaluates to an array (or tree of nested arrays)
|
366
393
|
may be subscripted to select a particular value or nested array from the
|
@@ -377,20 +404,13 @@ array_var['items][5] /* as above - alternate syntax */</code></blockquote>
|
|
377
404
|
<p>If the requested item does not exist, the special value
|
378
405
|
"<a href='#nil_value'>nil</a>" is used instead.</p>
|
379
406
|
|
380
|
-
<p>
|
381
|
-
<a href='#codeblock_syntax'>code blocks</a> to non-array values.
|
382
|
-
|
383
|
-
|
407
|
+
<p>Prior to version 1.0.0, subscript syntax could also be used to bind
|
408
|
+
<a href='#codeblock_syntax'>code blocks</a> to non-array values. The syntax
|
409
|
+
is shown here for historical reference but is <strong>no longer
|
410
|
+
supported</strong>:</p>
|
384
411
|
|
385
412
|
<blockquote><code>x['greeting]=({'hello}) /* where x is not an array
|
386
|
-
value */<br
|
387
|
-
|
388
|
-
<p>You can also bind code blocks to proxy objects to be used by all
|
389
|
-
array, boolean, number, or string values.</p>
|
390
|
-
|
391
|
-
<blockquote><code>@Array?=(') /* bind the array proxy to a unique object */<br>
|
392
|
-
@Array['list]=({ $.target.join(", ") }) /* bind the list method */<br>
|
393
|
-
$.*(1, 2, 3).list /* => 1, 2, 3 */</code></blockquote>
|
413
|
+
value */<br>/* this syntax is no longer supported! */</code></blockquote>
|
394
414
|
|
395
415
|
<p>[<a href='#contents'>Contents</a>] [<a href='#code_syntax'>Template Code Syntax</a>]</p>
|
396
416
|
|
@@ -404,7 +424,7 @@ $.*(1, 2, 3).list /* => 1, 2, 3 */</code></blockquote>
|
|
404
424
|
<li><a href='#nil_value'>Nil</a></li>
|
405
425
|
<li><a href='#number_values'>Numbers</a></li>
|
406
426
|
<li><a href='#string_values'>Strings</a></li>
|
407
|
-
<li><a href='#regexp_values'>Regular Expressions</a></li>
|
427
|
+
<li><a href='#regexp_values'>Regular Expressions And Matches</a></li>
|
408
428
|
</ul>
|
409
429
|
|
410
430
|
<h3 id='array_values'>Arrays</h3>
|
@@ -502,8 +522,7 @@ matrix[0, 0] /* => 1 */ matrix['type] /* => identity */</code></blockquote>
|
|
502
522
|
<p><a href='#codeblock_syntax'>Code blocks</a> may be bound as methods for
|
503
523
|
all array values as follows:</p>
|
504
524
|
|
505
|
-
<blockquote><code>@Array
|
506
|
-
@Array['</code><i>method_name</i><code>]=({</code>
|
525
|
+
<blockquote><code>@Array.methods('</code><i>method_name</i><code>, {</code>
|
507
526
|
<i>code sequence</i> <code>})</code></blockquote>
|
508
527
|
|
509
528
|
<p>[<a href='#contents'>Contents</a>] [<a href='#values_methods'>Values And Methods</a>]</p>
|
@@ -553,11 +572,11 @@ empty strings, are treated as true).</p>
|
|
553
572
|
for true, false, or both boolean
|
554
573
|
values as follows:</p>
|
555
574
|
|
556
|
-
<blockquote><code>$.true
|
575
|
+
<blockquote><code>$.true.methods('</code><i>method_name</i><code>, {</code>
|
557
576
|
<i>code sequence</i> <code>})<br>
|
558
|
-
$.false
|
577
|
+
$.false.methods('</code><i>method_name</i><code>, {</code>
|
559
578
|
<i>code sequence</i> <code>})<br>
|
560
|
-
@Boolean
|
579
|
+
@Boolean.methods('</code><i>method_name</i><code>, {</code>
|
561
580
|
<i>code sequence</i> <code>})</code></blockquote>
|
562
581
|
|
563
582
|
<p>[<a href='#contents'>Contents</a>] [<a href='#values_methods'>Values And Methods</a>]</p>
|
@@ -657,6 +676,12 @@ programming environments.</p>
|
|
657
676
|
|
658
677
|
</table>
|
659
678
|
|
679
|
+
<p><a href='#codeblock_syntax'>Code blocks</a> may be bound as methods for
|
680
|
+
all namespaces as follows:</p>
|
681
|
+
|
682
|
+
<blockquote><code>@Namespace.methods('</code><i>method_name</i><code>, {</code>
|
683
|
+
<i>code sequence</i> <code>})</code></blockquote>
|
684
|
+
|
660
685
|
<p>[<a href='#contents'>Contents</a>] [<a href='#values_methods'>Values And Methods</a>]</p>
|
661
686
|
|
662
687
|
<h3 id='nil_value'>Nil</h3>
|
@@ -678,7 +703,7 @@ value. It can be accessed via the namespace method call
|
|
678
703
|
<p><a href='#codeblock_syntax'>Code blocks</a> may be bound as methods
|
679
704
|
for the nil value as follows:</p>
|
680
705
|
|
681
|
-
<blockquote><code>$.nil
|
706
|
+
<blockquote><code>$.nil.methods('</code><i>method_name</i><code>, {</code>
|
682
707
|
<i>code sequence</i> <code>})</code></blockquote>
|
683
708
|
|
684
709
|
<p>[<a href='#contents'>Contents</a>] [<a href='#values_methods'>Values And Methods</a>]</p>
|
@@ -762,8 +787,7 @@ for the nil value as follows:</p>
|
|
762
787
|
<p><a href='#codeblock_syntax'>Code blocks</a> may be bound as methods for
|
763
788
|
all numeric values as follows:</p>
|
764
789
|
|
765
|
-
<blockquote><code>@Number
|
766
|
-
@Number['</code><i>method_name</i><code>]=({</code>
|
790
|
+
<blockquote><code>@Number.methods('</code><i>method_name</i><code>, {</code>
|
767
791
|
<i>code sequence</i> <code>})</code></blockquote>
|
768
792
|
|
769
793
|
<p>[<a href='#contents'>Contents</a>] [<a href='#values_methods'>Values And Methods</a>]</p>
|
@@ -812,6 +836,12 @@ all numeric values as follows:</p>
|
|
812
836
|
<tr><td><code>len</code>, <code>length</code></td>
|
813
837
|
<td>Returns the length of the string</td></tr>
|
814
838
|
|
839
|
+
<tr><td><code>match(</code><i>regexp</i><code>,
|
840
|
+
</code><i>offset</i><code>)</code></td><
|
841
|
+
<td>Match a string against a <a href='#regexp_values'>regular
|
842
|
+
expression</a>. Returns MatchData if the pattern matches, or nil
|
843
|
+
if it doesn't.</td></tr>
|
844
|
+
|
815
845
|
<tr><td><code>pcte</code></td>
|
816
846
|
<td>Returns the "percent encoding" of the string
|
817
847
|
(for URI components; since 0.1.4)</td></tr>
|
@@ -893,19 +923,17 @@ all numeric values as follows:</p>
|
|
893
923
|
<p><a href='#codeblock_syntax'>Code blocks</a> may be bound as methods for
|
894
924
|
all string values as follows:</p>
|
895
925
|
|
896
|
-
<blockquote><code>@String
|
897
|
-
@String['</code><i>method_name</i><code>]=({</code>
|
926
|
+
<blockquote><code>@String.methods('</code><i>method_name</i><code>, {</code>
|
898
927
|
<i>code sequence</i> <code>})</code></blockquote>
|
899
928
|
|
900
929
|
<p>[<a href='#contents'>Contents</a>] [<a href='#values_methods'>Values And Methods</a>]</p>
|
901
930
|
|
902
|
-
<h3 id='regexp_values'>Regular Expressions</h3>
|
931
|
+
<h3 id='regexp_values'>Regular Expressions And Matches</h3>
|
903
932
|
|
904
933
|
<p>"<a href='http://en.wikipedia.org/wiki/Regular_expression'>Regular
|
905
934
|
expressions</a>" ("regex" or "regexp" for short) in LtdTemplate are
|
906
|
-
|
907
|
-
|
908
|
-
expression.</p>
|
935
|
+
generated from <a href='#string_values'>string values</a> using the
|
936
|
+
<code>regexp</code> method.</p>
|
909
937
|
|
910
938
|
<p>Regular expressions must be enabled by the calling program. See
|
911
939
|
the gem documentation for details.</p>
|
@@ -919,9 +947,18 @@ the gem documentation for details.</p>
|
|
919
947
|
<tr><td><code>ci, ignorecase</code></td>
|
920
948
|
<td>Make pattern case-insensitive</td></tr>
|
921
949
|
|
950
|
+
<tr><td><code>class</code></td>
|
951
|
+
<td>Returns the string "Regexp"</td></tr>
|
952
|
+
|
922
953
|
<tr><td><code>ext, extended</code></td>
|
923
954
|
<td>Allow white space and comments in the pattern</td></tr>
|
924
955
|
|
956
|
+
<tr><td><code>match(</code><i>string</i><code>,
|
957
|
+
</code><i>offset</i><code>)</code></td>
|
958
|
+
<td>Match a <a href='#string_values'>string</a> against a regular
|
959
|
+
expression. Returns MatchData if the pattern matches, or nil if it
|
960
|
+
doesn't.</td></tr>
|
961
|
+
|
925
962
|
<tr><td><code>multi, multiline</code></td>
|
926
963
|
<td>Makes <code>.</code> match newlines too</td></tr>
|
927
964
|
|
@@ -932,6 +969,48 @@ the gem documentation for details.</p>
|
|
932
969
|
<blockquote><code>"Example".replace("[aeiou]".regexp.ci,'_)
|
933
970
|
/* => _x_mpl_ */</code></blockquote>
|
934
971
|
|
972
|
+
<p><a href='#codeblock_syntax'>Code blocks</a> may be bound as methods for
|
973
|
+
all regular expression values as follows:</p>
|
974
|
+
|
975
|
+
<blockquote><code>@Regexp.methods('</code><i>method_name</i><code>, {</code>
|
976
|
+
<i>code sequence</i> <code>})</code></blockquote>
|
977
|
+
|
978
|
+
<p>Regular expression match-result methods:</p>
|
979
|
+
|
980
|
+
<table>
|
981
|
+
|
982
|
+
<tr><th>Method</th><th>Description</th></tr>
|
983
|
+
|
984
|
+
<tr><td><code>[</code><i>index</i><code>]</code></td>
|
985
|
+
<td>Returns the entire (index 0) or specific sub-pattern match.</td></tr>
|
986
|
+
|
987
|
+
<tr><td><code>[</code><i>name</i><code>]</code></td>
|
988
|
+
<td>Returns the named sub-pattern match.</td></tr>
|
989
|
+
|
990
|
+
<tr><td><code>begin(</code><i>n</i><code>)</code></td>
|
991
|
+
<td>Returns the offset of the start of the <i>nth</i> match array
|
992
|
+
in the string (numeric index or named sub-match).</td></tr>
|
993
|
+
|
994
|
+
<tr><td><code>class</code></td>
|
995
|
+
<td>Returns the string "Match".</td></tr>
|
996
|
+
|
997
|
+
<tr><td><code>end(</code><i>n</i><code>)</code></td>
|
998
|
+
<td>Returns the offset of the character immediately following the
|
999
|
+
<i>nth</i> match in the string (numeric index or named
|
1000
|
+
sub-match).</td></tr>
|
1001
|
+
|
1002
|
+
<tr><td><code>length</code>, <code>size</code></td>
|
1003
|
+
<td>Returns the size of the match results array.</td></tr>
|
1004
|
+
|
1005
|
+
<tr><td><code>offset(</code><i>n</i><code>)</code></td>
|
1006
|
+
<td>Returns the <code>begin</code> and <code>end</code> values in a
|
1007
|
+
two-element array.</td></tr>
|
1008
|
+
|
1009
|
+
<tr><td><code>type</code></td>
|
1010
|
+
<td>Returns the string "match".</td></tr>
|
1011
|
+
|
1012
|
+
</table>
|
1013
|
+
|
935
1014
|
<p>[<a href='#contents'>Contents</a>] [<a href='#values_methods'>Values And Methods</a>]</p>
|
936
1015
|
|
937
1016
|
<h2 id='examples'>(Some More) Examples</h2>
|
@@ -939,7 +1018,7 @@ the gem documentation for details.</p>
|
|
939
1018
|
<p>This example binds a list method to arrays to include commas and "and"
|
940
1019
|
between elements:</p>
|
941
1020
|
|
942
|
-
<blockquote><code>@Array
|
1021
|
+
<blockquote><code>@Array.methods('list, {<br>
|
943
1022
|
$.target.join(" and ", ", ", ", ", ", and ") })<br>
|
944
1023
|
$.*('Ruby).list /* => Ruby */<br>
|
945
1024
|
$.*('Perl, 'Ruby).list /* => Perl and Ruby */<br>
|
@@ -950,7 +1029,7 @@ $.*('Perl, 'PHP, 'Python, 'Ruby).list /* => Perl, PHP, Python, and Ruby */
|
|
950
1029
|
the English "nth" for the number (e.g. 1st for 1, 2nd for 2, 3rd for 3,
|
951
1030
|
etc.) and then generates them from -11 to 24:</p>
|
952
1031
|
|
953
|
-
<blockquote><code>@Number
|
1032
|
+
<blockquote><code>@Number.methods('en_nth, {<br>
|
954
1033
|
$.var(.. 'n, $.target) $.var(.. 'n10, n.abs%(10), 'n100, n.abs%(100))<br>
|
955
1034
|
n $.if({ n100>=(11)&(n100<=(20)) }, 'th, { n10==(1) }, 'st,<br>
|
956
1035
|
{ n10==(2) }, 'nd, { n10==(3) }, 'rd, 'th) })<br>
|
data/lib/ltdtemplate.rb
CHANGED
@@ -2,13 +2,37 @@
|
|
2
2
|
#
|
3
3
|
# A template system with limitable resource usage.
|
4
4
|
#
|
5
|
-
# @author Brian Katzung
|
6
|
-
# @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
|
5
|
+
# @author Brian Katzung (briank@kappacs.com), Kappa Computer Solutions, LLC
|
6
|
+
# @copyright 2013-2014 Brian Katzung and Kappa Computer Solutions, LLC
|
7
7
|
# @license MIT License
|
8
8
|
|
9
|
+
require 'rubyverse'
|
10
|
+
require 'sarah'
|
11
|
+
|
9
12
|
class LtdTemplate
|
10
13
|
|
11
|
-
|
14
|
+
include Rubyverse
|
15
|
+
|
16
|
+
# Classes that "extend LtdTemplate::Consumer" are instantiated
|
17
|
+
# with the template as the first parameter by LtdTemplate#factory.
|
18
|
+
module Consumer; end
|
19
|
+
|
20
|
+
# Instances of classes that "include LtdTemplate::Method_Handler"
|
21
|
+
# handle their own template methods in our Rubyverse and don't need
|
22
|
+
# a proxy object.
|
23
|
+
module Method_Handler; end
|
24
|
+
|
25
|
+
# Use "params.extend LtdTemplate::Univalue" to mark parameter lists
|
26
|
+
# that may be interpreted as scalar (e.g. a single value and no
|
27
|
+
# ".." operator).
|
28
|
+
module Univalue; end
|
29
|
+
|
30
|
+
# This exception is raised when a resource limit is exceeded.
|
31
|
+
class ResourceLimitExceeded < RuntimeError; end
|
32
|
+
|
33
|
+
##################################################
|
34
|
+
|
35
|
+
VERSION = '1.0.0'
|
12
36
|
|
13
37
|
TOKEN_MAP = {
|
14
38
|
?. => :dot, # method separator
|
@@ -22,251 +46,168 @@ class LtdTemplate
|
|
22
46
|
?} => :rbrace # end code block
|
23
47
|
}
|
24
48
|
|
25
|
-
# @!attribute [r] exceeded
|
26
|
-
# @return [Symbol, nil]
|
27
|
-
# The resource whose limit was being exceeded when an
|
28
|
-
# LtdTemplate::ResourceLimitExceeded exception was raised.
|
29
|
-
attr_reader :exceeded
|
30
|
-
|
31
|
-
# @!attribute [r] factory_singletons
|
32
|
-
# @return [Hash]
|
33
|
-
# A hash of factory singletons (e.g. nil, true, and false values)
|
34
|
-
# for this template.
|
35
|
-
attr_reader :factory_singletons
|
36
|
-
|
37
|
-
# @!attribute [r] limits
|
38
|
-
# @return [Hash]
|
39
|
-
# A hash of resource limits to enforce during parsing and rendering.
|
40
|
-
attr_reader :limits
|
41
|
-
|
42
|
-
# @!attribute [r] namespace
|
43
|
-
# @return [LtdTemplate::Value::Namespace, nil]
|
44
|
-
# The current namespace (at the bottom of the rendering namespace stack).
|
45
|
-
attr_reader :namespace
|
46
|
-
|
47
|
-
# @!attribute [r] options
|
48
|
-
# @return [Hash]
|
49
|
-
# Instance initialization options
|
50
|
-
attr_reader :options
|
51
|
-
|
52
|
-
# @!attribute [r] usage
|
53
|
-
# @return [Hash]
|
54
|
-
# A hash of resource usage. It is updated after calls to #parse and
|
55
|
-
# #render.
|
56
|
-
attr_reader :usage
|
57
|
-
|
58
|
-
# @!attribute [r] used
|
59
|
-
# @return [Hash]
|
60
|
-
# A hash of used resources for this template
|
61
|
-
attr_reader :used
|
62
|
-
|
63
49
|
# @@classes contains the default factory classes. These can be overridden
|
64
50
|
# globally using the #set_classes class method or per-template using the
|
65
51
|
# #set_classes instance method.
|
52
|
+
#
|
53
|
+
# NOTE: Factory types are also used for resource tracking, and therefore
|
54
|
+
# must be unique among resource usage symbols.
|
66
55
|
@@classes = {
|
67
56
|
#
|
68
|
-
# These represent storable values.
|
69
|
-
# literals in code blocks.
|
57
|
+
# These represent storable values.
|
70
58
|
#
|
71
|
-
:array => '
|
72
|
-
:
|
73
|
-
:
|
59
|
+
:array => 'Sarah',
|
60
|
+
:array_splat => 'LtdTemplate::Value::Array_Splat',
|
61
|
+
:code_block => 'LtdTemplate::Value::Code_Block',
|
74
62
|
:namespace => 'LtdTemplate::Value::Namespace',
|
75
|
-
|
76
|
-
|
77
|
-
|
63
|
+
|
64
|
+
#
|
65
|
+
# These proxies provide template methods for native values.
|
66
|
+
#
|
67
|
+
:array_proxy => 'LtdTemplate::Proxy::Array',
|
68
|
+
:boolean_proxy => 'LtdTemplate::Proxy::Boolean',
|
69
|
+
:match_proxy => 'LtdTemplate::Proxy::Match',
|
70
|
+
:nil_proxy => 'LtdTemplate::Proxy::Nil',
|
71
|
+
:number_proxy => 'LtdTemplate::Proxy::Number',
|
72
|
+
:regexp_proxy => 'LtdTemplate::Proxy::Regexp',
|
73
|
+
:string_proxy => 'LtdTemplate::Proxy::String',
|
78
74
|
|
79
75
|
#
|
80
76
|
# These only occur as part of code blocks.
|
81
77
|
#
|
82
78
|
:call => 'LtdTemplate::Code::Call',
|
83
|
-
:
|
79
|
+
:code_seq => 'LtdTemplate::Code::Sequence',
|
84
80
|
:parameters => 'LtdTemplate::Code::Parameters',
|
85
81
|
:subscript => 'LtdTemplate::Code::Subscript',
|
86
82
|
:variable => 'LtdTemplate::Code::Variable',
|
87
83
|
}
|
88
84
|
|
85
|
+
# @@proxies contains the default mapping of native Ruby classes to
|
86
|
+
# factory symbols for the corresponding proxy classes.
|
87
|
+
# These can be overridden globally using the #set_proxies class method
|
88
|
+
# or per-template using the #set_proxies instance method.
|
89
|
+
# Unproxied native types will be invisible.
|
90
|
+
@@proxies = {
|
91
|
+
'Array' => :array_proxy,
|
92
|
+
'FalseClass' => :boolean_proxy,
|
93
|
+
'Hash' => :array_proxy,
|
94
|
+
'MatchData' => :match_proxy,
|
95
|
+
'NilClass' => :nil_proxy,
|
96
|
+
'Numeric' => :number_proxy,
|
97
|
+
'Regexp' => :regexp_proxy,
|
98
|
+
'Sarah' => :array_proxy,
|
99
|
+
'String' => :string_proxy,
|
100
|
+
'TrueClass' => :boolean_proxy,
|
101
|
+
}
|
102
|
+
|
103
|
+
# @!attribute [r] limits
|
104
|
+
# @return [Hash]
|
105
|
+
# A hash of resource limits to enforce during parsing and rendering.
|
106
|
+
attr_reader :limits
|
107
|
+
|
108
|
+
# @!attribute [r] usage
|
109
|
+
# @return [Hash]
|
110
|
+
# A hash of resource usage. It is updated after calls to #parse and
|
111
|
+
# #render.
|
112
|
+
attr_reader :usage
|
113
|
+
|
114
|
+
# @!attribute [r] exceeded
|
115
|
+
# @return [Symbol, nil]
|
116
|
+
# The resource whose limit was being exceeded when an
|
117
|
+
# LtdTemplate::ResourceLimitExceeded exception was raised.
|
118
|
+
attr_reader :exceeded
|
119
|
+
|
120
|
+
# @!attribute [r] options
|
121
|
+
# @return [Hash]
|
122
|
+
# Instance initialization options.
|
123
|
+
attr_reader :options
|
124
|
+
|
125
|
+
# @!attribute [r] namespace
|
126
|
+
# @return [LtdTemplate::Value::Namespace, nil]
|
127
|
+
# The current namespace (at the bottom of the rendering namespace stack).
|
128
|
+
attr_reader :namespace
|
129
|
+
|
130
|
+
# @!attribute [r] used
|
131
|
+
# @return [Hash]
|
132
|
+
# The resources already $.use'd for this template
|
133
|
+
attr_reader :used
|
134
|
+
|
89
135
|
# Change default factory classes globally
|
90
136
|
#
|
91
|
-
# @param
|
137
|
+
# @param mapping [Hash] A hash of factory symbols and corresponding
|
92
138
|
# classes to be instantiated.
|
93
139
|
# @return [Hash] Returns the current class mapping.
|
94
|
-
def self.set_classes (
|
95
|
-
@@classes.merge!
|
140
|
+
def self.set_classes (mapping)
|
141
|
+
@@classes.merge! mapping
|
142
|
+
end
|
143
|
+
|
144
|
+
# Change default proxy types globally
|
145
|
+
#
|
146
|
+
# @param mapping [Hash] A hash of native object classes and corresponding
|
147
|
+
# factory types for proxy objects to be instantiated.
|
148
|
+
# @return [Hash] Returns the current proxy mapping.
|
149
|
+
def self.set_proxies (mapping)
|
150
|
+
@@proxies.merge! mapping
|
96
151
|
end
|
97
152
|
|
153
|
+
##################################################
|
154
|
+
|
98
155
|
# Constructor
|
99
156
|
#
|
100
157
|
# @param options [Hash] Options hash
|
101
158
|
# @option options [Proc] :loader Callback for $.use method
|
102
159
|
# @option options [Proc] :missing_method Callback for missing methods
|
160
|
+
# @option options [Boolean] :regexp Set to true to enable regular
|
161
|
+
# expression processing
|
103
162
|
def initialize (options = {})
|
104
|
-
@classes, @
|
163
|
+
@classes, @proxies = {}, {}
|
105
164
|
@code, @namespace = nil, nil
|
106
|
-
@limits, @usage = {}, {}
|
165
|
+
@limits, @usage, @used = {}, {}, {}
|
107
166
|
@options = options
|
108
|
-
@used = {}
|
109
167
|
end
|
110
168
|
|
111
|
-
|
112
|
-
#
|
113
|
-
# @param template [String] A template string. Templates look
|
114
|
-
# like <tt>'literal<<template code>>literal...'</tt>.
|
115
|
-
# @return [LtdTemplate]
|
116
|
-
def parse (template)
|
117
|
-
@usage = {}
|
118
|
-
tokens = []
|
119
|
-
literal = true
|
120
|
-
trim_next = false
|
121
|
-
|
122
|
-
# Template "text<<code>>text<<code>>..." =>
|
123
|
-
# (text, code, text, code, ...)
|
124
|
-
template.split(/<<(?!<)((?:[^<>]|<[^<]|>[^>])*)>>/s).each do |part|
|
125
|
-
if part.length > 0
|
126
|
-
if literal
|
127
|
-
part.sub!(/^\s+/s, '') if trim_next
|
128
|
-
tokens.push [ :ext_string, part ]
|
129
|
-
else
|
130
|
-
if part[0] == '.'
|
131
|
-
tokens[-1][1].sub!(/\s+$/s, '') if
|
132
|
-
tokens[0] and tokens[-1][0] == :ext_string
|
133
|
-
part = part[1..-1] if part.length > 1
|
134
|
-
end
|
135
|
-
part = part[0..-2] if trim_next = (part[-1] == '.')
|
136
|
-
tokens += get_tokens part
|
137
|
-
end
|
138
|
-
else
|
139
|
-
trim_next = false
|
140
|
-
end
|
141
|
-
literal = !literal
|
142
|
-
end
|
143
|
-
|
144
|
-
@code = parse_template tokens
|
145
|
-
self
|
146
|
-
end
|
169
|
+
##################################################
|
147
170
|
|
148
|
-
#
|
171
|
+
# Throw an exception if a resource limit has been exceeded.
|
149
172
|
#
|
150
|
-
# @param
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
173
|
+
# @param resource [Symbol] The resource limit to be checked.
|
174
|
+
def check_limit (resource)
|
175
|
+
if @limits[resource] && @usage[resource] &&
|
176
|
+
@usage[resource] > @limits[resource]
|
177
|
+
@exceeded = resource
|
178
|
+
raise LtdTemplate::ResourceLimitExceeded.new(
|
179
|
+
"Exceeded #{resource} #{@limits[resource]}")
|
180
|
+
end
|
156
181
|
end
|
157
182
|
|
158
|
-
# Generate new code
|
183
|
+
# Generate new code, value, or proxy objects.
|
159
184
|
#
|
160
185
|
# @param type [Symbol] The symbol for the type of object to generate,
|
161
|
-
# e.g. :
|
186
|
+
# e.g. :number_proxy, :string_proxy, :code_block, etc.
|
162
187
|
# @param args [Array] Type-specific initialization parameters.
|
163
188
|
# @return Returns the new code/value object.
|
164
189
|
def factory (type, *args)
|
165
190
|
use :factory
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
191
|
+
spec = @classes[type] || @@classes[type]
|
192
|
+
case spec
|
193
|
+
when nil
|
194
|
+
raise ArgumentError, "Unknown template factory type #{type}"
|
195
|
+
when String
|
196
|
+
# Convert the class name to a class on first use and cache it
|
197
|
+
require spec.downcase.gsub('::', File::SEPARATOR)
|
198
|
+
@classes[type] = klass = eval(spec)
|
199
|
+
else klass = spec
|
173
200
|
end
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
# Render the template.
|
180
|
-
#
|
181
|
-
# The options hash may include :parameters, which may be an array or
|
182
|
-
# hash. These values will form the parameter array "_" in the root
|
183
|
-
# namespace.
|
184
|
-
#
|
185
|
-
# @param options [Hash] Rendering options.
|
186
|
-
# @return [String] The result of rendering the template.
|
187
|
-
def render (options = {})
|
188
|
-
@exceeded = nil # No limit exceeded yet
|
189
|
-
@namespace = nil # Reset the namespace stack between runs
|
190
|
-
@usage = {} # Reset resource usage
|
191
|
-
@used = {} # No libraries used yet
|
192
|
-
|
193
|
-
#
|
194
|
-
# Accept template parameters from an array or hash.
|
195
|
-
#
|
196
|
-
parameters = factory :array
|
197
|
-
parameters.set_from_array options[:parameters] if
|
198
|
-
options[:parameters].is_a? Array
|
199
|
-
parameters.set_from_hash options[:parameters] if
|
200
|
-
options[:parameters].is_a? Hash
|
201
|
-
|
202
|
-
#
|
203
|
-
# Create the root namespace and evaluate the template code.
|
204
|
-
#
|
205
|
-
push_namespace 'render', parameters
|
206
|
-
@code ? @code.get_value.to_text : ''
|
207
|
-
end
|
208
|
-
|
209
|
-
# Push a new namespace onto the namespace stack.
|
210
|
-
#
|
211
|
-
# @param method [String] The (native) method string. This will be
|
212
|
-
# available as <tt>$.method</tt> within the template.
|
213
|
-
# @param parameters [Array<LtdTemplate::Code>] These are code blocks
|
214
|
-
# to set the namespace parameters (available as the "_" array in the
|
215
|
-
# template).
|
216
|
-
# @param opts [Hash] Options hash.
|
217
|
-
# @option opts [LtdTemplate::Value] :target The target of the method
|
218
|
-
# call. This will be available as <tt>$.target</tt> within the template.
|
219
|
-
def push_namespace (method, parameters = nil, opts = {})
|
220
|
-
use :namespaces
|
221
|
-
use :namespace_depth
|
222
|
-
@namespace = factory :namespace, method, parameters, @namespace
|
223
|
-
@namespace.target = opts[:target] if opts[:target]
|
224
|
-
end
|
225
|
-
|
226
|
-
# Pop the current namespace from the stack.
|
227
|
-
def pop_namespace
|
228
|
-
if @namespace.parent
|
229
|
-
@namespace = @namespace.parent
|
230
|
-
use :namespace_depth, -1
|
201
|
+
use type
|
202
|
+
args.unshift self if klass.is_a? LtdTemplate::Consumer
|
203
|
+
if klass.respond_to? :new then klass.new *args
|
204
|
+
else klass
|
231
205
|
end
|
232
206
|
end
|
233
207
|
|
234
|
-
#
|
208
|
+
# Convert a string into an array of parser tokens.
|
235
209
|
#
|
236
|
-
# @param
|
237
|
-
# @param amount [Integer] The additional amount of the resource being
|
238
|
-
# used (or released, if negative).
|
239
|
-
def use (resource, amount = 1)
|
240
|
-
@usage[resource] ||= 0
|
241
|
-
@usage[resource] += amount
|
242
|
-
check_limit resource
|
243
|
-
end
|
244
|
-
|
245
|
-
# Track peak usage of a resource.
|
246
|
-
#
|
247
|
-
# @param resource [Symbol] The resource being used.
|
248
|
-
# @param amount [Integer] The total amount of the resource currently
|
249
|
-
# being used.
|
250
|
-
def using (resource, amount)
|
251
|
-
@usage[resource] ||= 0
|
252
|
-
@usage[resource] = amount if amount > @usage[resource]
|
253
|
-
check_limit resource
|
254
|
-
end
|
255
|
-
|
256
|
-
# Throw an exception if a resource limit has been exceeded.
|
257
|
-
#
|
258
|
-
# @param resource [Symbol] The resource limit to be checked.
|
259
|
-
def check_limit (resource)
|
260
|
-
if @limits[resource] and @usage[resource] and
|
261
|
-
@usage[resource] > @limits[resource]
|
262
|
-
@exceeded = resource
|
263
|
-
raise LtdTemplate::ResourceLimitExceeded
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
# Convert a string into an array of parsing tokens.
|
268
|
-
#
|
269
|
-
# @param str [String] The string to split into parsing tokens.
|
210
|
+
# @param str [String] The string to split into parser tokens.
|
270
211
|
# @return [Array<Array>]
|
271
212
|
def get_tokens (str)
|
272
213
|
tokens = []
|
@@ -297,11 +238,12 @@ class LtdTemplate
|
|
297
238
|
tokens.push [ :string, parse_strlit($1) ]
|
298
239
|
elsif token =~ /^-?\d+(?:\.\d+)?/
|
299
240
|
# Numeric literal
|
300
|
-
tokens.push [ :number, token
|
241
|
+
tokens.push [ :number, token =~ /\./ ?
|
242
|
+
token.to_f : token.to_i ]
|
301
243
|
elsif TOKEN_MAP[token]
|
302
244
|
# Delimiter
|
303
245
|
tokens.push [ TOKEN_MAP[token] ]
|
304
|
-
elsif token =~ /^[@^][a-z0-9_]|^[a-z_]
|
246
|
+
elsif token =~ /^[@^][a-z0-9_]|^[a-z_]|^[@^\$]$/i
|
305
247
|
# Variable or alphanumeric method name
|
306
248
|
tokens.push [ :name, token ]
|
307
249
|
else
|
@@ -313,11 +255,45 @@ class LtdTemplate
|
|
313
255
|
tokens
|
314
256
|
end
|
315
257
|
|
316
|
-
#
|
258
|
+
# Don't "spill our guts" upon inspection.
|
259
|
+
def inspect; "#<#{self.class.name}##{self.object_id}>"; end
|
260
|
+
|
261
|
+
# Parse a template from a string.
|
317
262
|
#
|
318
|
-
# @param
|
319
|
-
#
|
320
|
-
|
263
|
+
# @param template [String] A template string. Templates look
|
264
|
+
# like <tt>'literal<<template code>>literal...'</tt>.
|
265
|
+
# @return [LtdTemplate]
|
266
|
+
def parse (template)
|
267
|
+
@usage = {}
|
268
|
+
tokens = []
|
269
|
+
literal = true
|
270
|
+
trim_next = false
|
271
|
+
|
272
|
+
# Template "text<<code>>text<<code>>..." =>
|
273
|
+
# (text, code, text, code, ...)
|
274
|
+
template.split(/<<(?!<)((?:[^<>]|<[^<]|>[^>])*)>>/s).each do |part|
|
275
|
+
if part.length > 0
|
276
|
+
if literal
|
277
|
+
part.sub!(/^\s+/s, '') if trim_next
|
278
|
+
tokens.push [ :ext_string, part ]
|
279
|
+
else
|
280
|
+
if part[0] == '.'
|
281
|
+
tokens[-1][1].sub!(/\s+$/s, '') if
|
282
|
+
tokens[0] and tokens[-1][0] == :ext_string
|
283
|
+
part = part[1..-1] if part.length > 1
|
284
|
+
end
|
285
|
+
part = part[0..-2] if trim_next = (part[-1] == '.')
|
286
|
+
tokens += get_tokens part
|
287
|
+
end
|
288
|
+
else
|
289
|
+
trim_next = false
|
290
|
+
end
|
291
|
+
literal = !literal
|
292
|
+
end
|
293
|
+
|
294
|
+
@code = parse_template tokens
|
295
|
+
self
|
296
|
+
end
|
321
297
|
|
322
298
|
# Parse a code block, stopping at any stop token.
|
323
299
|
#
|
@@ -332,10 +308,8 @@ class LtdTemplate
|
|
332
308
|
|
333
309
|
token = tokens.shift# Consume the current token
|
334
310
|
case token[0]
|
335
|
-
when :string, :ext_string # string
|
336
|
-
code.push
|
337
|
-
when :number # numeric literal
|
338
|
-
code.push factory(:number, token[1])
|
311
|
+
when :string, :ext_string, :number # string and numeric literals
|
312
|
+
code.push token[1]
|
339
313
|
when :name # variable
|
340
314
|
code.push factory(:variable, token[1])
|
341
315
|
when :lbrack # variable element subscripts
|
@@ -361,41 +335,13 @@ class LtdTemplate
|
|
361
335
|
params = parse_parameters tokens
|
362
336
|
code.push factory(:call, code.pop, 'call', params) if code[0]
|
363
337
|
when :lbrace # explicit code block
|
364
|
-
code.push factory(:
|
338
|
+
code.push factory(:code_block,
|
365
339
|
parse_block(tokens, [ :rbrace ]))
|
366
340
|
tokens.shift if tokens[0] # Consume }
|
367
341
|
end
|
368
342
|
end
|
369
343
|
|
370
|
-
(code.size == 1) ? code[0] : factory(:
|
371
|
-
end
|
372
|
-
|
373
|
-
# Parse subscripts after the opening left bracket
|
374
|
-
#
|
375
|
-
# @param tokens [Array<Array>]] The token stream.
|
376
|
-
# @return [Array<LtdTemplate::Code>]
|
377
|
-
def parse_subscripts (tokens)
|
378
|
-
subs = parse_list tokens, [ :rbrack ], [ :lbrack ]
|
379
|
-
tokens.shift # Consume ]
|
380
|
-
subs
|
381
|
-
end
|
382
|
-
|
383
|
-
# Parse a positional and/or named parameter list
|
384
|
-
#
|
385
|
-
# @param tokens [Array<Array>] The token stream.
|
386
|
-
# @return [LtdTemplate::Code::Parameters]
|
387
|
-
def parse_parameters (tokens)
|
388
|
-
positional = parse_list tokens, [ :dotdot, :rparen ]
|
389
|
-
|
390
|
-
if tokens[0] and tokens[0][0] == :dotdot
|
391
|
-
tokens.shift # Consume ..
|
392
|
-
named = parse_list tokens, [ :rparen ]
|
393
|
-
else
|
394
|
-
named = nil
|
395
|
-
end
|
396
|
-
|
397
|
-
tokens.shift # Consume )
|
398
|
-
factory :parameters, positional, named
|
344
|
+
(code.size == 1) ? code[0] : factory(:code_seq, code)
|
399
345
|
end
|
400
346
|
|
401
347
|
# Common code for parsing various lists.
|
@@ -425,6 +371,24 @@ class LtdTemplate
|
|
425
371
|
list
|
426
372
|
end
|
427
373
|
|
374
|
+
# Parse a positional and/or named parameter list
|
375
|
+
#
|
376
|
+
# @param tokens [Array<Array>] The token stream.
|
377
|
+
# @return [LtdTemplate::Code::Parameters]
|
378
|
+
def parse_parameters (tokens)
|
379
|
+
positional = parse_list tokens, [ :dotdot, :rparen ]
|
380
|
+
|
381
|
+
if tokens[0] and tokens[0][0] == :dotdot
|
382
|
+
tokens.shift # Consume ..
|
383
|
+
named = parse_list tokens, [ :rparen ]
|
384
|
+
else
|
385
|
+
named = nil
|
386
|
+
end
|
387
|
+
|
388
|
+
tokens.shift # Consume )
|
389
|
+
factory :parameters, positional, named
|
390
|
+
end
|
391
|
+
|
428
392
|
# Parse escape sequences in string literals.
|
429
393
|
#
|
430
394
|
# These are the same as in Ruby double-quoted strings:
|
@@ -456,7 +420,153 @@ class LtdTemplate
|
|
456
420
|
end.join ''
|
457
421
|
end
|
458
422
|
|
423
|
+
# Parse subscripts after the opening left bracket
|
424
|
+
#
|
425
|
+
# @param tokens [Array<Array>]] The token stream.
|
426
|
+
# @return [Array<LtdTemplate::Code>]
|
427
|
+
def parse_subscripts (tokens)
|
428
|
+
subs = parse_list tokens, [ :rbrack ], [ :lbrack ]
|
429
|
+
tokens.shift # Consume ]
|
430
|
+
subs
|
431
|
+
end
|
432
|
+
|
433
|
+
# This is the top-level token parser.
|
434
|
+
#
|
435
|
+
# @param tokens [Array<Array>] The tokens to be parsed.
|
436
|
+
def parse_template (tokens); parse_block tokens; end
|
437
|
+
|
438
|
+
# Pop the current namespace from the stack.
|
439
|
+
def pop_namespace
|
440
|
+
if @namespace.parent
|
441
|
+
@namespace = @namespace.parent
|
442
|
+
use :namespace_depth, -1
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# Return the factory type symbol for a value's method handler class.
|
447
|
+
#
|
448
|
+
# @param type [Class] The value's class.
|
449
|
+
# @return The factory type symbol for the value's method-handler class.
|
450
|
+
def proxy_type (type)
|
451
|
+
if type
|
452
|
+
type_name = type.name
|
453
|
+
if !@proxies.has_key? type_name
|
454
|
+
# No local proxy class for this type; check the global
|
455
|
+
# settings and the proxy type for the superclass.
|
456
|
+
@proxies[type_name] = @@proxies[type_name] ||
|
457
|
+
proxy_type(type.superclass)
|
458
|
+
end
|
459
|
+
@proxies[type_name]
|
460
|
+
else
|
461
|
+
nil
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
# Push a new namespace onto the namespace stack.
|
466
|
+
#
|
467
|
+
# @param method [String] The (native) method string. This will be
|
468
|
+
# available as <tt>$.method</tt> within the template.
|
469
|
+
# @param parameters [Sarah] The code block parameters (available as
|
470
|
+
# the "_" array in the template).
|
471
|
+
# @param opts [Hash] Options hash.
|
472
|
+
# @option opts [Object] :target The target of the method
|
473
|
+
# call. This will be available as <tt>$.target</tt> within the template.
|
474
|
+
def push_namespace (method, parameters = nil, opts = {})
|
475
|
+
use :namespaces
|
476
|
+
use :namespace_depth
|
477
|
+
@namespace = factory :namespace, method, parameters, @namespace
|
478
|
+
@namespace.target = opts[:target] if opts[:target]
|
479
|
+
end
|
480
|
+
|
481
|
+
# Render the template.
|
482
|
+
#
|
483
|
+
# The options hash may include :parameters, which may be an array or
|
484
|
+
# hash. These values will form the parameter array "_" in the root
|
485
|
+
# namespace.
|
486
|
+
#
|
487
|
+
# @param options [Hash] Rendering options.
|
488
|
+
# @option options [Array|Sarah] :parameters Parameters (copied to
|
489
|
+
# array "_" in the template)
|
490
|
+
# @option options [Boolean] :cleanup Set to false to disable
|
491
|
+
# post-rendering cleanup.
|
492
|
+
# @return [String] The result of rendering the template.
|
493
|
+
def render (options = {})
|
494
|
+
#
|
495
|
+
# Reset for each rendering
|
496
|
+
#
|
497
|
+
@exceeded, @usage, @used = nil, {}, {}
|
498
|
+
@namespace = nil
|
499
|
+
|
500
|
+
#
|
501
|
+
# Create the root namespace and evaluate the template code.
|
502
|
+
#
|
503
|
+
if @code
|
504
|
+
params = Sarah.new
|
505
|
+
params.set *options[:parameters] if options[:parameters]
|
506
|
+
push_namespace 'render', params
|
507
|
+
rubyversed(@code).evaluate.in_rubyverse(self).tpl_text
|
508
|
+
else ''
|
509
|
+
end
|
510
|
+
ensure
|
511
|
+
self.rubyverse_map.clear unless options[:cleanup] == false
|
512
|
+
end
|
513
|
+
|
514
|
+
# Return an object to handle template methods for the supplied object.
|
515
|
+
#
|
516
|
+
# Unrecognized object types will be treated as nil.
|
517
|
+
def rubyverse_new (object)
|
518
|
+
if object.is_a? LtdTemplate::Method_Handler
|
519
|
+
object # The object handles its own template methods.
|
520
|
+
elsif type = proxy_type(object.class)
|
521
|
+
factory type, object
|
522
|
+
else rubyversed nil
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
# Override the default factory class mapping for this template instance.
|
527
|
+
#
|
528
|
+
# @param mapping [Hash] A hash of factory symbols and corresponding
|
529
|
+
# classes to be instantiated.
|
530
|
+
# @return [LtdTemplate]
|
531
|
+
def set_classes (mapping)
|
532
|
+
@classes.merge! mapping
|
533
|
+
self
|
534
|
+
end
|
535
|
+
|
536
|
+
# Override the default proxy types for this template instance.
|
537
|
+
#
|
538
|
+
# @param mapping [Hash] A hash of class names and corresponding
|
539
|
+
# factory types for proxy objects to be instantiated.
|
540
|
+
# @return [LtdTemplate]
|
541
|
+
def set_proxies (mapping)
|
542
|
+
@proxies.merge! mapping
|
543
|
+
self
|
544
|
+
end
|
545
|
+
|
546
|
+
# Track incremental usage of a resource.
|
547
|
+
#
|
548
|
+
# @param resource [Symbol] The resource being used.
|
549
|
+
# @param amount [Integer] The additional amount of the resource being
|
550
|
+
# used (or released, if negative).
|
551
|
+
def use (resource, amount = 1)
|
552
|
+
@usage[resource] ||= 0
|
553
|
+
@usage[resource] += amount
|
554
|
+
check_limit resource
|
555
|
+
end
|
556
|
+
|
557
|
+
# Track peak usage of a resource.
|
558
|
+
#
|
559
|
+
# @param resource [Symbol] The resource being used.
|
560
|
+
# @param amount [Integer] The total amount of the resource currently
|
561
|
+
# being used.
|
562
|
+
def using (resource, amount)
|
563
|
+
@usage[resource] ||= 0
|
564
|
+
if amount > @usage[resource]
|
565
|
+
@usage[resource] = amount
|
566
|
+
check_limit resource
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
459
570
|
end
|
460
571
|
|
461
|
-
#
|
462
|
-
class LtdTemplate::ResourceLimitExceeded < RuntimeError; end
|
572
|
+
# END
|