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.
Files changed (40) hide show
  1. data/CHANGELOG +10 -1
  2. data/Gemfile +2 -1
  3. data/RESOURCES +18 -32
  4. data/TEMPLATE_MANUAL.html +126 -47
  5. data/lib/ltdtemplate.rb +352 -242
  6. data/lib/ltdtemplate/code.rb +14 -87
  7. data/lib/ltdtemplate/code/call.rb +20 -16
  8. data/lib/ltdtemplate/code/parameters.rb +28 -31
  9. data/lib/ltdtemplate/code/sequence.rb +39 -0
  10. data/lib/ltdtemplate/code/subscript.rb +57 -50
  11. data/lib/ltdtemplate/code/variable.rb +22 -39
  12. data/lib/ltdtemplate/proxy.rb +26 -0
  13. data/lib/ltdtemplate/proxy/array.rb +258 -0
  14. data/lib/ltdtemplate/proxy/boolean.rb +74 -0
  15. data/lib/ltdtemplate/proxy/match.rb +40 -0
  16. data/lib/ltdtemplate/proxy/nil.rb +27 -0
  17. data/lib/ltdtemplate/proxy/number.rb +77 -0
  18. data/lib/ltdtemplate/proxy/regexp.rb +74 -0
  19. data/lib/ltdtemplate/proxy/string.rb +196 -0
  20. data/lib/ltdtemplate/value.rb +94 -0
  21. data/lib/ltdtemplate/value/array_splat.rb +34 -0
  22. data/lib/ltdtemplate/value/code_block.rb +21 -17
  23. data/lib/ltdtemplate/value/namespace.rb +77 -79
  24. data/ltdtemplate.gemspec +2 -2
  25. data/test/04number.rb +0 -7
  26. data/test/05string.rb +0 -7
  27. data/test/06array.rb +0 -9
  28. data/test/07each.rb +3 -3
  29. data/test/08interpolate.rb +1 -1
  30. data/test/10missing_meth.rb +1 -1
  31. data/test/11classes.rb +9 -9
  32. metadata +15 -13
  33. data/lib/ltdtemplate/code/code_block.rb +0 -30
  34. data/lib/ltdtemplate/value/array.rb +0 -210
  35. data/lib/ltdtemplate/value/boolean.rb +0 -82
  36. data/lib/ltdtemplate/value/nil.rb +0 -30
  37. data/lib/ltdtemplate/value/number.rb +0 -96
  38. data/lib/ltdtemplate/value/string.rb +0 -215
  39. data/lib/test.rb +0 -10
  40. 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.{each,each_rnd,each_seq} methods (executing the code
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
@@ -1,4 +1,5 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem "sarah", "~> 0.0.2"
3
+ gem "rubyverse", "~> 0.0.1"
4
+ gem "sarah", "~> 2.0.0"
4
5
  gem "htmlentities", "~> 4.3.1"
data/RESOURCES CHANGED
@@ -1,32 +1,18 @@
1
- Resource Usage/Limit Summary
2
-
3
- arrays
4
- The total number of arrays created
5
-
6
- array_size
7
- The size of the largest array
8
-
9
- call_depth
10
- The current/maximum call depth
11
-
12
- calls
13
- The total number of calls made
14
-
15
- iterations
16
- The total number of loop iterations
17
-
18
- regexp (limit)
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.2.2; July 29, 2013</span></h1>
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 And Code-Block Bindings</a></li>
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 notation
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 method calls: unconditional
273
- (<code>=</code>) and conditional (<code>?=</code>). A conditional
274
- assignment does nothing if the variable is already bound to (holds a
275
- reference to) a value (including the nil value). Assignments do not
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 is one,
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, non-string value and does
337
- not constitute the entire template code, it is retained as-is. Otherwise,
338
- the string value of each element in the sequence, if any, is combined to
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, non-string values in their code sequences */<br>
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 And Code-Block Bindings</h3>
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>LtdTemplate also uses subscript syntax to bind
381
- <a href='#codeblock_syntax'>code blocks</a> to non-array values. These can
382
- subsequently be called like <a href='#call_syntax'>methods</a>. They
383
- never override standard methods.</p>
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>x.greeting /* => hello */</code></blockquote>
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['</code><i>method_name</i><code>]=({</code>
575
+ <blockquote><code>$.true.methods('</code><i>method_name</i><code>, {</code>
557
576
  <i>code sequence</i> <code>})<br>
558
- $.false['</code><i>method_name</i><code>]=({</code>
577
+ $.false.methods('</code><i>method_name</i><code>, {</code>
559
578
  <i>code sequence</i> <code>})<br>
560
- @Boolean?=(') @Boolean['</code><i>method_name</i><code>]=({</code>
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['</code><i>method_name</i><code>]=({</code>
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
- specially enhanced, "magic strings". They behave like regular strings
907
- unless used in a method call that is able to use them as a regular
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?=(') @Array['list]=({<br>
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?=(') @Number['en_nth]=({<br>
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&gt;=(11)&(n100&lt;=(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 <briank@kappacs.com>, Kappa Computer Solutions, LLC
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
- VERSION = '0.2.4'
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. Some may also occur as
69
- # literals in code blocks.
57
+ # These represent storable values.
70
58
  #
71
- :array => 'LtdTemplate::Value::Array',
72
- :boolean => 'LtdTemplate::Value::Boolean',
73
- :explicit_block => 'LtdTemplate::Value::Code_Block',
59
+ :array => 'Sarah',
60
+ :array_splat => 'LtdTemplate::Value::Array_Splat',
61
+ :code_block => 'LtdTemplate::Value::Code_Block',
74
62
  :namespace => 'LtdTemplate::Value::Namespace',
75
- :nil => 'LtdTemplate::Value::Nil',
76
- :number => 'LtdTemplate::Value::Number',
77
- :string => 'LtdTemplate::Value::String',
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
- :implied_block => 'LtdTemplate::Code::Code_Block',
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 classes [Hash] A hash of factory symbols and corresponding
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 (classes)
95
- @@classes.merge! classes
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, @factory_singletons = {}, {}
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
- # Parse a template from a string.
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
- # Override default factory classes for this template instance.
171
+ # Throw an exception if a resource limit has been exceeded.
149
172
  #
150
- # @param classes [Hash] A hash of factory symbols and corresponding
151
- # classes to be instantiated.
152
- # @return [LtdTemplate]
153
- def set_classes (classes)
154
- @classes.merge! classes
155
- self
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/value objects.
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. :number, :string, :implicit_block, etc.
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
- type = @classes[type] || @@classes[type]
167
- if type.is_a? String
168
- file = type.downcase.gsub '::', File::SEPARATOR
169
- require file
170
- eval(type).instance(self, *args)
171
- else
172
- type.instance(self, *args)
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
- end
175
-
176
- # Shortcut for frequently used template factory :nil
177
- def nil; @factory_singletons[:nil] || factory(:nil); end
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
- # Track incremental usage of a resource.
208
+ # Convert a string into an array of parser tokens.
235
209
  #
236
- # @param resource [Symbol] The resource being used.
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_]|^\$$/i
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
- # This is the top-level token parser.
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 tokens [Array<Array>] The tokens to be parsed.
319
- # @return [LtdTemplate::Code] The implementation code.
320
- def parse_template (tokens); parse_block tokens; end
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 literal
336
- code.push factory(:string, token[1])
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(:explicit_block,
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(:implied_block, code)
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
- # This exception is raised when a resource limit is exceeded.
462
- class LtdTemplate::ResourceLimitExceeded < RuntimeError; end
572
+ # END