mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-12 23:40:45 -07:00
Merge branch 'experimental' of git://github.com/jamesmacaulay/liquid into jamesmacaulay/experimental
Conflicts: lib/liquid.rb lib/liquid/variable.rb
This commit is contained in:
+6
-1
@@ -33,7 +33,12 @@ module Liquid
|
|||||||
VariableStart = /\{\{/
|
VariableStart = /\{\{/
|
||||||
VariableEnd = /\}\}/
|
VariableEnd = /\}\}/
|
||||||
VariableIncompleteEnd = /\}\}?/
|
VariableIncompleteEnd = /\}\}?/
|
||||||
QuotedFragment = /"[^"]+"|'[^']+'|[^\s,|]+/
|
QuotedFragment = /"[^"]+"|'[^']+'|[^\s,\|]+/
|
||||||
|
StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s,\|,\:,\,]+/
|
||||||
|
FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
|
||||||
|
OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
|
||||||
|
SpacelessFilter = /#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
|
||||||
|
Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
|
||||||
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
||||||
AnyStartingTag = /\{\{|\{\%/
|
AnyStartingTag = /\{\{|\{\%/
|
||||||
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
|
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
|
||||||
|
|||||||
@@ -133,6 +133,9 @@ module Liquid
|
|||||||
:blank?
|
:blank?
|
||||||
when 'empty'
|
when 'empty'
|
||||||
:empty?
|
:empty?
|
||||||
|
# filtered variables
|
||||||
|
when SpacelessFilter
|
||||||
|
filtered_variable(key)
|
||||||
# Single quoted strings
|
# Single quoted strings
|
||||||
when /^'(.*)'$/
|
when /^'(.*)'$/
|
||||||
$1.to_s
|
$1.to_s
|
||||||
@@ -221,5 +224,9 @@ module Liquid
|
|||||||
|
|
||||||
object
|
object
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filtered_variable(markup)
|
||||||
|
Variable.new(markup).render(self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -63,9 +63,27 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Sort elements of the array
|
# Sort elements of the array
|
||||||
def sort(input)
|
# provide optional property with which to sort an array of hashes or drops
|
||||||
[input].flatten.sort
|
def sort(input, property = nil)
|
||||||
|
ary = [input].flatten
|
||||||
|
if property.nil?
|
||||||
|
ary.sort
|
||||||
|
elsif ary.first.respond_to?('[]') and !ary.first[property].nil?
|
||||||
|
ary.sort {|a,b| a[property] <=> b[property] }
|
||||||
|
elsif ary.first.respond_to?(property)
|
||||||
|
ary.sort {|a,b| a.send(property) <=> b.send(property) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# map/collect on a given property
|
||||||
|
def map(input, property)
|
||||||
|
ary = [input].flatten
|
||||||
|
if ary.first.respond_to?('[]') and !ary.first[property].nil?
|
||||||
|
ary.map {|e| e[property] }
|
||||||
|
elsif ary.first.respond_to?(property)
|
||||||
|
ary.map {|e| e.send(property) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Replace occurrences of a string with another
|
# Replace occurrences of a string with another
|
||||||
def replace(input, string, replacement = '')
|
def replace(input, string, replacement = '')
|
||||||
@@ -155,6 +173,26 @@ module Liquid
|
|||||||
array.last if array.respond_to?(:last)
|
array.last if array.respond_to?(:last)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# addition
|
||||||
|
def plus(input, operand)
|
||||||
|
input + operand if input.respond_to?('+')
|
||||||
|
end
|
||||||
|
|
||||||
|
# subtraction
|
||||||
|
def minus(input, operand)
|
||||||
|
input - operand if input.respond_to?('-')
|
||||||
|
end
|
||||||
|
|
||||||
|
# multiplication
|
||||||
|
def times(input, operand)
|
||||||
|
input * operand if input.respond_to?('*')
|
||||||
|
end
|
||||||
|
|
||||||
|
# division
|
||||||
|
def divided_by(input, operand)
|
||||||
|
input / operand if input.respond_to?('/')
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_filter(StandardFilters)
|
Template.register_filter(StandardFilters)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ module Liquid
|
|||||||
# {{ monkey }}
|
# {{ monkey }}
|
||||||
#
|
#
|
||||||
class Assign < Tag
|
class Assign < Tag
|
||||||
Syntax = /(#{VariableSignature}+)\s*=\s*(#{QuotedFragment}+)/
|
Syntax = /(#{VariableSignature}+)\s*=\s*(#{Expression}+)/
|
||||||
|
|
||||||
def initialize(tag_name, markup, tokens)
|
def initialize(tag_name, markup, tokens)
|
||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Case < Block
|
class Case < Block
|
||||||
Syntax = /(#{QuotedFragment})/
|
Syntax = /(#{Expression})/
|
||||||
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/
|
WhenSyntax = /(#{Expression})(?:(?:\s+or\s+|\s*\,\s*)(#{Expression}.*))?/
|
||||||
|
|
||||||
def initialize(tag_name, markup, tokens)
|
def initialize(tag_name, markup, tokens)
|
||||||
@blocks = []
|
@blocks = []
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ module Liquid
|
|||||||
# <div class="green"> Item five</div>
|
# <div class="green"> Item five</div>
|
||||||
#
|
#
|
||||||
class Cycle < Tag
|
class Cycle < Tag
|
||||||
SimpleSyntax = /#{QuotedFragment}/
|
SimpleSyntax = /#{Expression}/
|
||||||
NamedSyntax = /(#{QuotedFragment})\s*\:\s*(.*)/
|
NamedSyntax = /(#{Expression})\s*\:\s*(.*)/
|
||||||
|
|
||||||
def initialize(tag_name, markup, tokens)
|
def initialize(tag_name, markup, tokens)
|
||||||
case markup
|
case markup
|
||||||
@@ -49,7 +49,7 @@ module Liquid
|
|||||||
|
|
||||||
def variables_from_string(markup)
|
def variables_from_string(markup)
|
||||||
markup.split(',').collect do |var|
|
markup.split(',').collect do |var|
|
||||||
var =~ /\s*(#{QuotedFragment})\s*/
|
var =~ /\s*(#{Expression})\s*/
|
||||||
$1 ? $1 : nil
|
$1 ? $1 : nil
|
||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ module Liquid
|
|||||||
# forloop.last:: Returns true if the item is the last item.
|
# forloop.last:: Returns true if the item is the last item.
|
||||||
#
|
#
|
||||||
class For < Block
|
class For < Block
|
||||||
Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)\s*(reversed)?/
|
Syntax = /(\w+)\s+in\s+(#{Expression}+)\s*(reversed)?/
|
||||||
|
|
||||||
def initialize(tag_name, markup, tokens)
|
def initialize(tag_name, markup, tokens)
|
||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ module Liquid
|
|||||||
#
|
#
|
||||||
class If < Block
|
class If < Block
|
||||||
SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
||||||
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/
|
Syntax = /(#{Expression})\s*([=!<>a-z_]+)?\s*(#{Expression})?/
|
||||||
|
|
||||||
def initialize(tag_name, markup, tokens)
|
def initialize(tag_name, markup, tokens)
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ module Liquid
|
|||||||
@name = match[1]
|
@name = match[1]
|
||||||
if markup.match(/#{FilterSeparator}\s*(.*)/)
|
if markup.match(/#{FilterSeparator}\s*(.*)/)
|
||||||
filters = Regexp.last_match(1).split(/#{FilterSeparator}/)
|
filters = Regexp.last_match(1).split(/#{FilterSeparator}/)
|
||||||
|
filters.each do |f|
|
||||||
filters.each do |f|
|
|
||||||
if matches = f.match(/\s*(\w+)/)
|
if matches = f.match(/\s*(\w+)/)
|
||||||
filtername = matches[1]
|
filtername = matches[1]
|
||||||
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/).flatten
|
filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*(#{QuotedFragment})/).flatten
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
require File.dirname(__FILE__) + '/helper'
|
||||||
|
|
||||||
|
class IfElseTest < Test::Unit::TestCase
|
||||||
|
include Liquid
|
||||||
|
|
||||||
|
def test_with_filtered_expressions
|
||||||
|
assert_template_result('foo','{% assign foo = values|sort|last %}{{ foo }}', 'values' => %w{foo bar baz})
|
||||||
|
assert_template_result('foo','{% assign sorted = values|sort %}{{ sorted | last }}', 'values' => %w{foo bar baz})
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -112,6 +112,25 @@ class IfElseTest < Test::Unit::TestCase
|
|||||||
assert_template_result('elsif','{% if false %}if{% elsif true %}elsif{% endif %}')
|
assert_template_result('elsif','{% if false %}if{% elsif true %}elsif{% endif %}')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_with_filtered_expressions
|
||||||
|
assert_template_result('yes','{% if "BLAH"|downcase == "blah" %}yes{% endif %}')
|
||||||
|
assert_template_result('yes','{% if "FOO BAR"|truncatewords:1,"--" == "FOO--" %}yes{% endif %}')
|
||||||
|
assert_template_result('yes','{% if "FOO BAR"|truncatewords:1,"--"|downcase == "foo--" %}yes{% endif %}')
|
||||||
|
assert_template_result('yes','{% if "foo--" == "FOO BAR"|truncatewords:1,"--"|downcase %}yes{% endif %}')
|
||||||
|
# array transformation, to make sure we aren't converting arrays to strings somewhere along the way:
|
||||||
|
assert_template_result('yes','{% if values|sort == sorted %}yes{% endif %}', 'values' => %w{foo bar baz}, 'sorted' => %w{bar baz foo})
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_allow_no_spaces_in_filtered_expressions
|
||||||
|
assert_template_result('','{% if "foo--" == "FOO BAR" |truncatewords:1,"--"|downcase %}yes{% endif %}')
|
||||||
|
assert_template_result('','{% if "foo--" == "FOO BAR"| truncatewords:1,"--"|downcase %}yes{% endif %}')
|
||||||
|
assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords :1,"--"|downcase %}yes{% endif %}')
|
||||||
|
assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords: 1,"--"|downcase %}yes{% endif %}')
|
||||||
|
assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords:1 ,"--"|downcase %}yes{% endif %}')
|
||||||
|
assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords:1, "--"|downcase %}yes{% endif %}')
|
||||||
|
assert_template_result('','{% if "foo--" == "FOO BAR"|truncatewords:1,"--" |downcase %}yes{% endif %}')
|
||||||
|
end
|
||||||
|
|
||||||
def test_syntax_error_no_variable
|
def test_syntax_error_no_variable
|
||||||
assert_raise(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}')}
|
assert_raise(SyntaxError){ assert_template_result('', '{% if jerry == 1 %}')}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -67,6 +67,13 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_sort
|
def test_sort
|
||||||
assert_equal [1,2,3,4], @filters.sort([4,3,2,1])
|
assert_equal [1,2,3,4], @filters.sort([4,3,2,1])
|
||||||
|
assert_equal [{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], @filters.sort([{"a" => 4}, {"a" => 3}, {"a" => 1}, {"a" => 2}], "a")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_map
|
||||||
|
assert_equal [1,2,3,4], @filters.map([{"a" => 1}, {"a" => 2}, {"a" => 3}, {"a" => 4}], 'a')
|
||||||
|
assert_template_result 'abc', "{{ ary | map:'foo' | map:'bar' }}",
|
||||||
|
'ary' => [{'foo' => {'bar' => 'a'}}, {'foo' => {'bar' => 'b'}}, {'foo' => {'bar' => 'c'}}]
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_date
|
def test_date
|
||||||
@@ -118,9 +125,25 @@ class StandardFiltersTest < Test::Unit::TestCase
|
|||||||
assert_template_result "a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc"
|
assert_template_result "a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_plus
|
||||||
|
assert_template_result "2", "{{ 1 | plus:1 }}"
|
||||||
|
assert_template_result "11", "{{ '1' | plus:'1' }}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_minus
|
||||||
|
assert_template_result "4", "{{ input | minus:operand }}", 'input' => 5, 'operand' => 1
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_times
|
||||||
|
assert_template_result "12", "{{ 3 | times:4 }}"
|
||||||
|
assert_template_result "foofoofoofoo", "{{ 'foo' | times:4 }}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_divided_by
|
||||||
|
assert_template_result "4", "{{ 12 | divided_by:3 }}"
|
||||||
|
assert_template_result "4", "{{ 14 | divided_by:3 }}"
|
||||||
|
assert_template_result "5", "{{ 15 | divided_by:3 }}"
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -99,6 +99,10 @@ HERE
|
|||||||
assert_template_result('+--', '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}', assigns)
|
assert_template_result('+--', '{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}', assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_for_with_filtered_expressions
|
||||||
|
assert_template_result('abc','{% for letter in letters|sort %}{{ letter }}{% endfor %}', 'letters' => %w{c b a})
|
||||||
|
end
|
||||||
|
|
||||||
def test_limiting
|
def test_limiting
|
||||||
assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
|
assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
|
||||||
assert_template_result('12','{%for i in array limit:2 %}{{ i }}{%endfor%}',assigns)
|
assert_template_result('12','{%for i in array limit:2 %}{{ i }}{%endfor%}',assigns)
|
||||||
|
|||||||
Reference in New Issue
Block a user