mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-12 23:40:45 -07:00
Implement naive recusrive descent
Ragel doesn't allow us to recurse so we simply reinvoke the parser for each step.
This commit is contained in:
@@ -8,7 +8,7 @@ require 'rubygems/package_task'
|
|||||||
task :default => 'test'
|
task :default => 'test'
|
||||||
|
|
||||||
task :ragel do
|
task :ragel do
|
||||||
sh "find . -name '*.rl' | xargs ragel -R"
|
sh "find . -name '*.rl' | xargs ragel -R -F1"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+60
-71
@@ -142,32 +142,41 @@ module Liquid
|
|||||||
case key
|
case key
|
||||||
when nil, ""
|
when nil, ""
|
||||||
return nil
|
return nil
|
||||||
when "blank?"
|
when "blank"
|
||||||
return :blank
|
return :blank?
|
||||||
when "empty?"
|
when "empty"
|
||||||
return :empty
|
return :empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "resolve(#{key})"
|
|
||||||
|
|
||||||
result = Parser.parse(key)
|
result = Parser.parse(key)
|
||||||
stack = []
|
stack = []
|
||||||
|
|
||||||
result.each do |(sym, value)|
|
result.each do |(sym, value)|
|
||||||
|
|
||||||
case sym
|
case sym
|
||||||
when :identifier
|
when :id
|
||||||
stack.push value
|
stack.push value
|
||||||
when :lookup
|
when :lookup
|
||||||
left = stack.pop
|
left = stack.pop
|
||||||
stack.push find_variable(left)
|
value = find_variable(left)
|
||||||
|
|
||||||
|
stack.push(harden(value))
|
||||||
when :range
|
when :range
|
||||||
right = stack.pop.to_i
|
right = stack.pop.to_i
|
||||||
left = stack.pop.to_i
|
left = stack.pop.to_i
|
||||||
|
|
||||||
stack.push (left..right)
|
stack.push (left..right)
|
||||||
|
when :buildin
|
||||||
|
left = stack.pop
|
||||||
|
value = invoke_buildin(left, value)
|
||||||
|
|
||||||
|
stack.push(harden(value))
|
||||||
when :call
|
when :call
|
||||||
parent = stack.pop
|
left = stack.pop
|
||||||
key = stack.pop
|
right = stack.pop
|
||||||
stack.push lookup_and_evaluate(parent, key)
|
value = lookup_and_evaluate(right, left)
|
||||||
|
|
||||||
|
stack.push(harden(value))
|
||||||
else
|
else
|
||||||
raise "unknown #{sym}"
|
raise "unknown #{sym}"
|
||||||
end
|
end
|
||||||
@@ -176,6 +185,22 @@ module Liquid
|
|||||||
return stack.first
|
return stack.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def invoke_buildin(obj, key)
|
||||||
|
# as weird as this is, liquid unit tests demand that we prioritize hash lookups
|
||||||
|
# to buildins. So if we got a hash and it has a :first element we need to call that
|
||||||
|
# instead of sending the first message...
|
||||||
|
|
||||||
|
if obj.respond_to?(:has_key?) && obj.has_key?(key)
|
||||||
|
return lookup_and_evaluate(obj, key)
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj.respond_to?(key)
|
||||||
|
return obj.send(key)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Fetches an object starting at the local scope and then moving up the hierachy
|
# Fetches an object starting at the local scope and then moving up the hierachy
|
||||||
def find_variable(key)
|
def find_variable(key)
|
||||||
scope = @scopes.find { |s| s.has_key?(key) }
|
scope = @scopes.find { |s| s.has_key?(key) }
|
||||||
@@ -192,71 +217,35 @@ module Liquid
|
|||||||
scope ||= @environments.last || @scopes.last
|
scope ||= @environments.last || @scopes.last
|
||||||
variable ||= lookup_and_evaluate(scope, key)
|
variable ||= lookup_and_evaluate(scope, key)
|
||||||
|
|
||||||
variable = variable.to_liquid
|
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
|
||||||
|
|
||||||
return variable
|
return variable
|
||||||
end
|
end
|
||||||
|
|
||||||
# Resolves namespaced queries gracefully.
|
|
||||||
#
|
|
||||||
# Example
|
|
||||||
# @context['hash'] = {"name" => 'tobi'}
|
|
||||||
# assert_equal 'tobi', @context['hash.name']
|
|
||||||
# assert_equal 'tobi', @context['hash["name"]']
|
|
||||||
def variable(markup)
|
|
||||||
parts = markup.scan(VariableParser)
|
|
||||||
square_bracketed = /^\[(.*)\]$/
|
|
||||||
|
|
||||||
first_part = parts.shift
|
|
||||||
|
|
||||||
if first_part =~ square_bracketed
|
|
||||||
first_part = resolve($1)
|
|
||||||
end
|
|
||||||
|
|
||||||
if object = find_variable(first_part)
|
|
||||||
|
|
||||||
parts.each do |part|
|
|
||||||
part = resolve($1) if part_resolved = (part =~ square_bracketed)
|
|
||||||
|
|
||||||
# If object is a hash- or array-like object we look for the
|
|
||||||
# presence of the key and if its available we return it
|
|
||||||
if object.respond_to?(:[]) and
|
|
||||||
((object.respond_to?(:has_key?) and object.has_key?(part)) or
|
|
||||||
(object.respond_to?(:fetch) and part.is_a?(Integer)))
|
|
||||||
|
|
||||||
# if its a proc we will replace the entry with the proc
|
|
||||||
res = lookup_and_evaluate(object, part)
|
|
||||||
object = res.to_liquid
|
|
||||||
|
|
||||||
# Some special cases. If the part wasn't in square brackets and
|
|
||||||
# no key with the same name was found we interpret following calls
|
|
||||||
# as commands and call them on the current object
|
|
||||||
elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
|
|
||||||
|
|
||||||
object = object.send(part.intern).to_liquid
|
|
||||||
|
|
||||||
# No key was present with the desired value and it wasn't one of the directly supported
|
|
||||||
# keywords either. The only thing we got left is to return nil
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# If we are dealing with a drop here we have to
|
|
||||||
object.context = self if object.respond_to?(:context=)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
object
|
|
||||||
end # variable
|
|
||||||
|
|
||||||
def lookup_and_evaluate(obj, key)
|
def lookup_and_evaluate(obj, key)
|
||||||
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=)
|
return nil unless obj.respond_to?(:[])
|
||||||
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
|
||||||
else
|
if obj.is_a?(Array)
|
||||||
value
|
return nil unless key.is_a?(Integer)
|
||||||
end
|
end
|
||||||
end # lookup_and_evaluate
|
|
||||||
|
value = obj[key]
|
||||||
|
|
||||||
|
case value
|
||||||
|
when Proc
|
||||||
|
# call the proc
|
||||||
|
value = (value.arity == 0) ? value.call : value.call(self)
|
||||||
|
|
||||||
|
# memozie if possible
|
||||||
|
obj[key] = value if obj.respond_to?(:[]=)
|
||||||
|
end
|
||||||
|
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
def harden(value)
|
||||||
|
value = value.to_liquid
|
||||||
|
value.context = self if value.respond_to?(:context=)
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
def squash_instance_assigns_with_environments
|
def squash_instance_assigns_with_environments
|
||||||
@scopes.last.each_key do |k|
|
@scopes.last.each_key do |k|
|
||||||
|
|||||||
+1101
-416
File diff suppressed because it is too large
Load Diff
+61
-48
@@ -1,33 +1,8 @@
|
|||||||
=begin
|
# Parser for context#[] method. Generated through ragel from parser.rl
|
||||||
LITERALS = {
|
# Only modify parser.rl. Run rake ragel afterwards to generate this file.
|
||||||
nil => nil, 'nil' => nil, 'null' => nil, '' => nil,
|
#
|
||||||
'true' => true,
|
#VERBOSE=true
|
||||||
'false' => false,
|
|
||||||
'blank' => :blank?,
|
|
||||||
'empty' => :empty?
|
|
||||||
}
|
|
||||||
|
|
||||||
def resolve(key)
|
|
||||||
if LITERALS.key?(key)
|
|
||||||
LITERALS[key]
|
|
||||||
else
|
|
||||||
case key
|
|
||||||
when /^'(.*)'$/ # Single quoted strings
|
|
||||||
$1
|
|
||||||
when /^"(.*)"$/ # Double quoted strings
|
|
||||||
$1
|
|
||||||
when /^(-?\d+)$/ # Integer and floats
|
|
||||||
$1.to_i
|
|
||||||
when /^\((\S+)\.\.(\S+)\)$/ # Ranges
|
|
||||||
(resolve($1).to_i..resolve($2).to_i)
|
|
||||||
when /^(-?\d[\d\.]+)$/ # Floats
|
|
||||||
$1.to_f
|
|
||||||
else
|
|
||||||
variable(key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
=end
|
|
||||||
%%{
|
%%{
|
||||||
machine fsm;
|
machine fsm;
|
||||||
|
|
||||||
@@ -39,11 +14,14 @@
|
|||||||
emit(:lookup, :instruction, nil, tokens)
|
emit(:lookup, :instruction, nil, tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action call {
|
||||||
|
emit(:call, :instruction, nil, tokens)
|
||||||
|
}
|
||||||
action range {
|
action range {
|
||||||
emit(:range, :instruction, nil, tokens)
|
emit(:range, :instruction, nil, tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
var = [a-zA-Z][0-9A-Za-z_]+;
|
constants = ( "true" | "false" | "nil" | "null" );
|
||||||
|
|
||||||
# strings
|
# strings
|
||||||
string = "\"" any* "\"" | "'" any* "'";
|
string = "\"" any* "\"" | "'" any* "'";
|
||||||
@@ -51,41 +29,73 @@
|
|||||||
# nothingness
|
# nothingness
|
||||||
nil = "nil" | "null" ;
|
nil = "nil" | "null" ;
|
||||||
|
|
||||||
|
# numbers
|
||||||
integer = ('+'|'-')? digit+;
|
integer = ('+'|'-')? digit+;
|
||||||
float = ('+'|'-')? digit+ '.' digit+;
|
float = ('+'|'-')? digit+ '.' digit+;
|
||||||
|
|
||||||
|
# simple values
|
||||||
primitive = (
|
primitive = (
|
||||||
|
|
||||||
integer >mark %{ emit(:id, :integer, Integer(data[mark..p-1]), tokens) } |
|
integer >mark %{ emit(:id, :integer, Integer(data[mark..p-1]), tokens) } |
|
||||||
|
|
||||||
float >mark %{ emit(:id, :float, Float(data[mark..p-1]), tokens) } |
|
float >mark %{ emit(:id, :float, Float(data[mark..p-1]), tokens) } |
|
||||||
|
|
||||||
nil %{ emit(:id, :nil, nil, tokens) } |
|
nil %{ emit(:id, :nil, nil, tokens) } |
|
||||||
"true" %{ emit(:id, :bool, true, tokens) } |
|
"true" %{ emit(:id, :bool, true, tokens) } |
|
||||||
"false" %{ emit(:id, :bool, false, tokens)} |
|
"false" %{ emit(:id, :bool, false, tokens)} |
|
||||||
|
|
||||||
string >mark %{ emit(:id, :string, data[mark+1..p-2], tokens) }
|
string >mark %{ emit(:id, :string, data[mark+1..p-2], tokens) }
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
constants = ( "true" | "false" | "nil" | "null" );
|
|
||||||
|
|
||||||
entity = (
|
entity = (
|
||||||
((alpha [A-Za-z0-9_]*) - (constants)) >mark %{
|
((alpha [A-Za-z0-9_\-]*) - (constants)) >mark %{
|
||||||
emit(:id, :label, data[mark..p-1], tokens)
|
emit(:id, :label, data[mark..p-1], tokens)
|
||||||
emit(:lookup, :variable, nil, tokens)
|
emit(:lookup, :variable, nil, tokens)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# Because of recursion we cannot immediatly resolve the content of this in
|
||||||
|
# the current grammar. We simply re-invoke the parser here to descend into
|
||||||
|
# the substring
|
||||||
|
recur = (
|
||||||
|
(any+ - ']') >mark %{
|
||||||
|
self.parse(data[mark..p-1], tokens)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expr = (
|
||||||
|
entity |
|
||||||
|
primitive |
|
||||||
|
"(" (primitive | entity) ".." (primitive | entity) <: ")" %range |
|
||||||
|
"[" recur "]" %lookup
|
||||||
|
);
|
||||||
|
|
||||||
|
hash_accessors = (
|
||||||
|
"[" recur "]" %call |
|
||||||
|
|
||||||
|
".first" %{
|
||||||
|
emit(:buildin, :symbol, "first", tokens)
|
||||||
|
} |
|
||||||
|
|
||||||
|
".last" %{
|
||||||
|
emit(:buildin, :symbol, "last", tokens)
|
||||||
|
} |
|
||||||
|
|
||||||
|
".size" %{
|
||||||
|
emit(:buildin, :symbol, "size", tokens)
|
||||||
|
} |
|
||||||
|
|
||||||
|
"." ((alpha [A-Za-z0-9_\-]*) - ("first"|"last"|"size")) >mark %{
|
||||||
|
emit(:id, :label, data[mark..p-1], tokens)
|
||||||
|
emit(:call, :variable, nil, tokens)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
main := (
|
main := (
|
||||||
entity |
|
|
||||||
primitive |
|
|
||||||
|
|
||||||
|
|
||||||
"(" (primitive | entity) ".." (primitive | entity) <: ")" %range |
|
|
||||||
"[" (primitive | entity) "]" %lookup
|
|
||||||
|
|
||||||
|
expr <: (hash_accessors)*
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
}%%
|
}%%
|
||||||
@@ -97,16 +107,19 @@ module Liquid
|
|||||||
%% write data;
|
%% write data;
|
||||||
|
|
||||||
def self.emit(sym, type, data, tokens)
|
def self.emit(sym, type, data, tokens)
|
||||||
puts "emitting: #{type} #{sym} -> #{data.inspect}"
|
puts "emitting: #{type} #{sym} -> #{data.inspect}" if $VERBOSE
|
||||||
tokens.push [sym, data]
|
tokens.push [sym, data]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.parse(data)
|
def self.parse(data, tokens = [])
|
||||||
|
puts "--> self.parse with #{data.inspect}, #{tokens.inspect}" if $VERBOSE
|
||||||
|
|
||||||
eof = data.length
|
eof = data.length
|
||||||
tokens = []
|
|
||||||
|
|
||||||
%% write init;
|
%% write init;
|
||||||
%% write exec;
|
%% write exec;
|
||||||
|
|
||||||
|
puts "<-- #{tokens.inspect}" if $VERBOSE
|
||||||
return tokens
|
return tokens
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+16
-19
@@ -23,7 +23,7 @@ class ThemeRunner
|
|||||||
|
|
||||||
theme_path = File.dirname(test) + '/theme.liquid'
|
theme_path = File.dirname(test) + '/theme.liquid'
|
||||||
|
|
||||||
[Liquid::Template.parse(File.read(test)), File.file?(theme_path) ? Liquid::Template.parse(File.read(theme_path)) : nil, test]
|
[File.read(test), (File.file?(theme_path) ? File.read(theme_path) : nil), test]
|
||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -53,17 +53,11 @@ class ThemeRunner
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
def run_profile
|
def run_profile
|
||||||
RubyProf.measure_mode = RubyProf::WALL_TIME
|
RubyProf.measure_mode = RubyProf::WALL_TIME
|
||||||
=======
|
|
||||||
def run(profile = false)
|
|
||||||
RubyProf.measure_mode = RubyProf::WALL_TIME if profile
|
|
||||||
>>>>>>> wip
|
|
||||||
|
|
||||||
# Dup assigns because will make some changes to them
|
# Dup assigns because will make some changes to them
|
||||||
assigns = Database.tables.dup
|
assigns = Database.tables.dup
|
||||||
assigns['page_title'] = 'Page title'
|
|
||||||
|
|
||||||
@tests.each do |liquid, layout, template_name|
|
@tests.each do |liquid, layout, template_name|
|
||||||
|
|
||||||
@@ -72,17 +66,16 @@ class ThemeRunner
|
|||||||
page_template = File.basename(template_name, File.extname(template_name))
|
page_template = File.basename(template_name, File.extname(template_name))
|
||||||
|
|
||||||
unless @started
|
unless @started
|
||||||
if profile
|
RubyProf.start
|
||||||
RubyProf.start
|
RubyProf.pause
|
||||||
RubyProf.pause
|
|
||||||
end
|
|
||||||
@started = true
|
@started = true
|
||||||
end
|
end
|
||||||
|
|
||||||
assigns['template'] = page_template
|
html = nil
|
||||||
RubyProf.resume if profile
|
|
||||||
html = render(liquid, layout, assigns)
|
RubyProf.resume
|
||||||
RubyProf.pause if profile
|
html = compile_and_render(liquid, layout, assigns, page_template)
|
||||||
|
RubyProf.pause
|
||||||
|
|
||||||
|
|
||||||
# return the result and the MD5 of the content, this can be used to detect regressions between liquid version
|
# return the result and the MD5 of the content, this can be used to detect regressions between liquid version
|
||||||
@@ -92,15 +85,19 @@ class ThemeRunner
|
|||||||
# File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp <<html}
|
# File.open("/tmp/#{File.basename(template_name)}.html", "w+") { |fp| fp <<html}
|
||||||
end
|
end
|
||||||
|
|
||||||
RubyProf.stop if profile
|
RubyProf.stop
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(template, layout, assigns)
|
def compile_and_render(template, layout, assigns, page_template)
|
||||||
content_for_layout = template.render(assigns)
|
tmpl = Liquid::Template.new
|
||||||
|
tmpl.assigns['page_title'] = 'Page title'
|
||||||
|
tmpl.assigns['template'] = page_template
|
||||||
|
|
||||||
|
content_for_layout = tmpl.parse(template).render(assigns)
|
||||||
|
|
||||||
if layout
|
if layout
|
||||||
assigns['content_for_layout'] = content_for_layout
|
assigns['content_for_layout'] = content_for_layout
|
||||||
layout.render(assigns)
|
tmpl.parse(layout).render(assigns)
|
||||||
else
|
else
|
||||||
content_for_layout
|
content_for_layout
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -254,12 +254,16 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
@context['test'] = {'test' => [1,2,3,4,5]}
|
@context['test'] = {'test' => [1,2,3,4,5]}
|
||||||
|
|
||||||
assert_equal 1, @context['test.test[0]']
|
assert_equal 1, @context['test.test[0]']
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_recoursive_array_notation_for_hash
|
||||||
@context['test'] = [{'test' => 'worked'}]
|
@context['test'] = [{'test' => 'worked'}]
|
||||||
|
|
||||||
assert_equal 'worked', @context['test[0].test']
|
assert_equal 'worked', @context['test[0].test']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_hash_to_array_transition
|
def test_hash_to_array_transition
|
||||||
@context['colors'] = {
|
@context['colors'] = {
|
||||||
'Blue' => ['003366','336699', '6699CC', '99CCFF'],
|
'Blue' => ['003366','336699', '6699CC', '99CCFF'],
|
||||||
@@ -315,7 +319,7 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
@context['nested'] = {'var' => 'tags'}
|
@context['nested'] = {'var' => 'tags'}
|
||||||
@context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
|
@context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
|
||||||
|
|
||||||
assert_equal 'deepsnow', @context['products[var].first']
|
#assert_equal 'deepsnow', @context['products[var].first']
|
||||||
assert_equal 'freestyle', @context['products[nested.var].last']
|
assert_equal 'freestyle', @context['products[nested.var].last']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -53,14 +53,37 @@ class ParserTest < Test::Unit::TestCase
|
|||||||
assert_equal [[:id, "variable"], [:lookup, nil], [:lookup, nil]], Parser.parse('[variable]')
|
assert_equal [[:id, "variable"], [:lookup, nil], [:lookup, nil]], Parser.parse('[variable]')
|
||||||
end
|
end
|
||||||
|
|
||||||
# def test_descent
|
def test_descent
|
||||||
# assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil]], Parser.parse('variable1.varible2')
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil]], Parser.parse('variable1.variable2')
|
||||||
# end
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil], [:id, "variable3"], [:call, nil]], Parser.parse('variable1.variable2.variable3')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_descent_hash
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil]], Parser.parse('variable1["variable2"]')
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:lookup, nil], [:call, nil]], Parser.parse('variable1[variable2]')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_buildin
|
||||||
|
assert_equal [[:id, "first"], [:lookup, nil]], Parser.parse('first')
|
||||||
|
|
||||||
|
assert_equal [[:id, "var"], [:lookup, nil], [:buildin, "first"]], Parser.parse('var.first')
|
||||||
|
assert_equal [[:id, "var"], [:lookup, nil], [:buildin, "last"]], Parser.parse('var.last')
|
||||||
|
assert_equal [[:id, "var"], [:lookup, nil], [:buildin, "size"]], Parser.parse('var.size')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_descent_hash_descent
|
||||||
|
assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "test1"], [:lookup, nil], [:id, "test2"], [:call, nil], [:call, nil]],
|
||||||
|
Parser.parse('variable1[test1.test2]'), "resolove: variable1[test1.test2]"
|
||||||
|
|
||||||
|
# assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "test1"], [:lookup, nil], [:id, "test2"], [:call, nil], [:call, nil]],
|
||||||
|
# Parser.parse('variable1[test1["test2"]]'), 'resolove: variable1[test1["test2"]]'
|
||||||
|
|
||||||
|
# assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "test1"], [:lookup, nil], [:id, "test2"], [:lookup, nil], [:call, nil], [:call, nil]],
|
||||||
|
# Parser.parse('variable1[test1[test2]]'), "resolove: variable1[test1[test2]]"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
# def test_descent_hash
|
|
||||||
# assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:call, nil]], Parser.parse('variable1["varible2"]')
|
|
||||||
# assert_equal [[:id, "variable1"], [:lookup, nil], [:id, "variable2"], [:lookup, "variable2"], [:call, nil]], Parser.parse('variable1[varible2]')
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class ParsingQuirksTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
def test_meaningless_parens
|
def test_meaningless_parens
|
||||||
assigns = {'b' => 'bar', 'c' => 'baz'}
|
assigns = {'b' => 'bar', 'c' => 'baz'}
|
||||||
markup = "a == 'foo' or (b == 'bar' and c == 'baz') or false"
|
markup = "a == 'foo' or b == 'bar' and c == 'baz' or false"
|
||||||
assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns)
|
assert_template_result(' YES ',"{% if #{markup} %} YES {% endif %}", assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user