overview: | Lambdas are a special-cased data type for use in interpolations and sections. When used as the data value for an Interpolation tag, the lambda MUST be treatable as an arity 0 function, and invoked as such. The returned value MUST be rendered against the default delimiters, then interpolated in place of the lambda. When used as the data value for a Section tag, the lambda MUST be treatable as an arity 1 function, and invoked as such (passing a String containing the unprocessed section contents). The returned value MUST be rendered against the current delimiters, then interpolated in place of the section. tests: - name: Interpolation desc: A lambda's return value should be interpolated. data: lambda: !code ruby: 'proc { "world" }' perl: 'sub { "world" }' js: 'function() { return "world" }' php: 'return "world";' python: 'lambda: "world"' clojure: '(fn [] "world")' lisp: '(lambda () "world")' template: "Hello, {{lambda}}!" expected: "Hello, world!" - name: Interpolation - Expansion desc: A lambda's return value should be parsed. data: planet: "world" lambda: !code ruby: 'proc { "{{planet}}" }' perl: 'sub { "{{planet}}" }' js: 'function() { return "{{planet}}" }' php: 'return "{{planet}}";' python: 'lambda: "{{planet}}"' clojure: '(fn [] "{{planet}}")' lisp: '(lambda () "{{planet}}")' template: "Hello, {{lambda}}!" expected: "Hello, world!" - name: Interpolation - Alternate Delimiters desc: A lambda's return value should parse with the default delimiters. data: planet: "world" lambda: !code ruby: 'proc { "|planet| => {{planet}}" }' perl: 'sub { "|planet| => {{planet}}" }' js: 'function() { return "|planet| => {{planet}}" }' php: 'return "|planet| => {{planet}}";' python: 'lambda: "|planet| => {{planet}}"' clojure: '(fn [] "|planet| => {{planet}}")' lisp: '(lambda () "|planet| => {{planet}}")' template: "{{= | | =}}\nHello, (|&lambda|)!" expected: "Hello, (|planet| => world)!" - name: Interpolation - Multiple Calls desc: Interpolated lambdas should not be cached. data: lambda: !code ruby: 'proc { $calls ||= 0; $calls += 1 }' perl: 'sub { no strict; $calls += 1 }' js: 'function() { return (g=(function(){return this})()).calls=(g.calls||0)+1 }' php: 'global $calls; return ++$calls;' python: 'lambda: globals().update(calls=globals().get("calls",0)+1) or calls' clojure: '(def g (atom 0)) (fn [] (swap! g inc))' lisp: '(let ((g 0)) (lambda () (incf g)))' template: '{{lambda}} == {{{lambda}}} == {{lambda}}' expected: '1 == 2 == 3' - name: Escaping desc: Lambda results should be appropriately escaped. data: lambda: !code ruby: 'proc { ">" }' perl: 'sub { ">" }' js: 'function() { return ">" }' php: 'return ">";' python: 'lambda: ">"' clojure: '(fn [] ">")' lisp: '(lambda () ">")' template: "<{{lambda}}{{{lambda}}}" expected: "<>>" - name: Section desc: Lambdas used for sections should receive the raw section string. data: x: 'Error!' lambda: !code ruby: 'proc { |text| text == "{{x}}" ? "yes" : "no" }' perl: 'sub { $_[0] eq "{{x}}" ? "yes" : "no" }' js: 'function(txt) { return (txt == "{{x}}" ? "yes" : "no") }' php: 'return ($text == "{{x}}") ? "yes" : "no";' python: 'lambda text: text == "{{x}}" and "yes" or "no"' clojure: '(fn [text] (if (= text "{{x}}") "yes" "no"))' lisp: '(lambda (text) (if (string= text "{{x}}") "yes" "no"))' template: "<{{#lambda}}{{x}}{{/lambda}}>" expected: "" - name: Section - Expansion desc: Lambdas used for sections should have their results parsed. data: planet: "Earth" lambda: !code ruby: 'proc { |text| "#{text}{{planet}}#{text}" }' perl: 'sub { $_[0] . "{{planet}}" . $_[0] }' js: 'function(txt) { return txt + "{{planet}}" + txt }' php: 'return $text . "{{planet}}" . $text;' python: 'lambda text: "%s{{planet}}%s" % (text, text)' clojure: '(fn [text] (str text "{{planet}}" text))' lisp: '(lambda (text) (format nil "~a{{planet}}~a" text text))' template: "<{{#lambda}}-{{/lambda}}>" expected: "<-Earth->" - name: Section - Alternate Delimiters desc: Lambdas used for sections should parse with the current delimiters. data: planet: "Earth" lambda: !code ruby: 'proc { |text| "#{text}{{planet}} => |planet|#{text}" }' perl: 'sub { $_[0] . "{{planet}} => |planet|" . $_[0] }' js: 'function(txt) { return txt + "{{planet}} => |planet|" + txt }' php: 'return $text . "{{planet}} => |planet|" . $text;' python: 'lambda text: "%s{{planet}} => |planet|%s" % (text, text)' clojure: '(fn [text] (str text "{{planet}} => |planet|" text))' lisp: '(lambda (text) (format nil "~a{{planet}} => |planet|~a" text text))' template: "{{= | | =}}<|#lambda|-|/lambda|>" expected: "<-{{planet}} => Earth->" - name: Section - Multiple Calls desc: Lambdas used for sections should not be cached. data: lambda: !code ruby: 'proc { |text| "__#{text}__" }' perl: 'sub { "__" . $_[0] . "__" }' js: 'function(txt) { return "__" + txt + "__" }' php: 'return "__" . $text . "__";' python: 'lambda text: "__%s__" % (text)' clojure: '(fn [text] (str "__" text "__"))' lisp: '(lambda (text) (format nil "__~a__" text))' template: '{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}' expected: '__FILE__ != __LINE__' - name: Inverted Section desc: Lambdas used for inverted sections should be considered truthy. data: static: 'static' lambda: !code ruby: 'proc { |text| false }' perl: 'sub { 0 }' js: 'function(txt) { return false }' php: 'return false;' python: 'lambda text: 0' clojure: '(fn [text] false)' lisp: '(lambda (text) (declare (ignore text)) nil)' template: "<{{^lambda}}{{static}}{{/lambda}}>" expected: "<>"