1overview: |
2  Lambdas are a special-cased data type for use in interpolations and
3  sections.
4
5  When used as the data value for an Interpolation tag, the lambda MUST be
6  treatable as an arity 0 function, and invoked as such.  The returned value
7  MUST be rendered against the default delimiters, then interpolated in place
8  of the lambda.
9
10  When used as the data value for a Section tag, the lambda MUST be treatable
11  as an arity 1 function, and invoked as such (passing a String containing the
12  unprocessed section contents).  The returned value MUST be rendered against
13  the current delimiters, then interpolated in place of the section.
14tests:
15  - name: Interpolation
16    desc: A lambda's return value should be interpolated.
17    data:
18      lambda: !code
19        ruby:   'proc { "world" }'
20        perl:   'sub { "world" }'
21        js:     'function() { return "world" }'
22        php:    'return "world";'
23        python: 'lambda: "world"'
24    template: "Hello, {{lambda}}!"
25    expected: "Hello, world!"
26
27  - name: Interpolation - Expansion
28    desc: A lambda's return value should be parsed.
29    data:
30      planet: "world"
31      lambda: !code
32        ruby:   'proc { "{{planet}}" }'
33        perl:   'sub { "{{planet}}" }'
34        js:     'function() { return "{{planet}}" }'
35        php:    'return "{{planet}}";'
36        python: 'lambda: "{{planet}}"'
37    template: "Hello, {{lambda}}!"
38    expected: "Hello, world!"
39
40  - name: Interpolation - Alternate Delimiters
41    desc: A lambda's return value should parse with the default delimiters.
42    data:
43      planet: "world"
44      lambda: !code
45        ruby:   'proc { "|planet| => {{planet}}" }'
46        perl:   'sub { "|planet| => {{planet}}" }'
47        js:     'function() { return "|planet| => {{planet}}" }'
48        php:    'return "|planet| => {{planet}}";'
49        python: 'lambda: "|planet| => {{planet}}"'
50    template: "{{= | | =}}\nHello, (|&lambda|)!"
51    expected: "Hello, (|planet| => world)!"
52
53  - name: Interpolation - Multiple Calls
54    desc: Interpolated lambdas should not be cached.
55    data:
56      lambda: !code
57        ruby:   'proc { $calls ||= 0; $calls += 1 }'
58        perl:   'sub { no strict; $calls += 1 }'
59        js:     'function() { return (g=(function(){return this})()).calls=(g.calls||0)+1 }'
60        php:    'global $calls; return ++$calls;'
61        python: 'lambda: globals().update(calls=globals().get("calls",0)+1) or calls'
62    template: '{{lambda}} == {{{lambda}}} == {{lambda}}'
63    expected: '1 == 2 == 3'
64
65  - name: Escaping
66    desc: Lambda results should be appropriately escaped.
67    data:
68      lambda: !code
69        ruby:   'proc { ">" }'
70        perl:   'sub { ">" }'
71        js:     'function() { return ">" }'
72        php:    'return ">";'
73        python: 'lambda: ">"'
74    template: "<{{lambda}}{{{lambda}}}"
75    expected: "<&gt;>"
76
77  - name: Section
78    desc: Lambdas used for sections should receive the raw section string.
79    data:
80      x: 'Error!'
81      lambda: !code
82        ruby:   'proc { |text| text == "{{x}}" ? "yes" : "no" }'
83        perl:   'sub { $_[0] eq "{{x}}" ? "yes" : "no" }'
84        js:     'function(txt) { return (txt == "{{x}}" ? "yes" : "no") }'
85        php:    'return ($text == "{{x}}") ? "yes" : "no";'
86        python: 'lambda text: text == "{{x}}" and "yes" or "no"'
87    template: "<{{#lambda}}{{x}}{{/lambda}}>"
88    expected: "<yes>"
89
90  - name: Section - Expansion
91    desc: Lambdas used for sections should have their results parsed.
92    data:
93      planet: "Earth"
94      lambda: !code
95        ruby:   'proc { |text| "#{text}{{planet}}#{text}" }'
96        perl:   'sub { $_[0] . "{{planet}}" . $_[0] }'
97        js:     'function(txt) { return txt + "{{planet}}" + txt }'
98        php:    'return $text . "{{planet}}" . $text;'
99        python: 'lambda text: "%s{{planet}}%s" % (text, text)'
100    template: "<{{#lambda}}-{{/lambda}}>"
101    expected: "<-Earth->"
102
103  - name: Section - Alternate Delimiters
104    desc: Lambdas used for sections should parse with the current delimiters.
105    data:
106      planet: "Earth"
107      lambda: !code
108        ruby:   'proc { |text| "#{text}{{planet}} => |planet|#{text}" }'
109        perl:   'sub { $_[0] . "{{planet}} => |planet|" . $_[0] }'
110        js:     'function(txt) { return txt + "{{planet}} => |planet|" + txt }'
111        php:    'return $text . "{{planet}} => |planet|" . $text;'
112        python: 'lambda text: "%s{{planet}} => |planet|%s" % (text, text)'
113    template: "{{= | | =}}<|#lambda|-|/lambda|>"
114    expected: "<-{{planet}} => Earth->"
115
116  - name: Section - Multiple Calls
117    desc: Lambdas used for sections should not be cached.
118    data:
119      lambda: !code
120        ruby:   'proc { |text| "__#{text}__" }'
121        perl:   'sub { "__" . $_[0] . "__" }'
122        js:     'function(txt) { return "__" + txt + "__" }'
123        php:    'return "__" . $text . "__";'
124        python: 'lambda text: "__%s__" % (text)'
125    template: '{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}'
126    expected: '__FILE__ != __LINE__'
127
128  - name: Inverted Section
129    desc: Lambdas used for inverted sections should be considered truthy.
130    data:
131      static: 'static'
132      lambda: !code
133        ruby:   'proc { |text| false }'
134        perl:   'sub { 0 }'
135        js:     'function(txt) { return false }'
136        php:    'return false;'
137        python: 'lambda text: 0'
138    template: "<{{^lambda}}{{static}}{{/lambda}}>"
139    expected: "<>"
140