ltdtemplate 0.2.4 → 1.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/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
|