ltdtemplate 0.2.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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