mirror of
https://github.com/kemko/liquid.git
synced 2026-01-01 15:55:40 +03:00
Stack scope by variable and not by level
This commit is contained in:
@@ -88,7 +88,7 @@ module Liquid
|
||||
|
||||
# Merge a hash of variables in the current local scope
|
||||
def merge(new_scopes)
|
||||
@scope.merge!(new_scopes)
|
||||
new_scopes.each { |k, v| self[k] = v }
|
||||
end
|
||||
|
||||
# Pushes a new local scope on the stack, pops it at the end of the block
|
||||
@@ -100,25 +100,19 @@ module Liquid
|
||||
#
|
||||
# context['var] #=> nil
|
||||
def stack(*variable_names)
|
||||
previous_values = {}
|
||||
variable_names.each do |variable_name|
|
||||
previous_values[variable_name] = @scope[variable_name]
|
||||
end
|
||||
|
||||
@stack_level += 1
|
||||
raise StackLevelError, "Nesting too deep".freeze if @stack_level > Block::MAX_DEPTH
|
||||
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
@scope.merge!(previous_values)
|
||||
@stack_level -= 1
|
||||
end
|
||||
end
|
||||
|
||||
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
|
||||
def []=(key, value)
|
||||
@scope[key] = value
|
||||
(@scope[key] ||= [nil]) << value
|
||||
end
|
||||
|
||||
# Look up variable, either resolve directly after considering the name. We can directly handle
|
||||
@@ -133,6 +127,19 @@ module Liquid
|
||||
evaluate(Expression.parse(expression))
|
||||
end
|
||||
|
||||
def unset(key)
|
||||
if @scope[key].size <= 1
|
||||
@scope.delete(key)
|
||||
else
|
||||
@scope[key].pop
|
||||
end
|
||||
end
|
||||
|
||||
def set_root(key, val)
|
||||
@scope[key] ||= []
|
||||
@scope[key][0] = val
|
||||
end
|
||||
|
||||
def key?(key)
|
||||
self[key] != nil
|
||||
end
|
||||
@@ -143,8 +150,10 @@ module Liquid
|
||||
|
||||
# Fetches an object starting at the local scope and then moving up the hierachy
|
||||
def find_variable(key, raise_on_not_found: true)
|
||||
trigger = false
|
||||
value = @scope[key]
|
||||
scope = @scope if value != nil
|
||||
scope = @scope unless value.nil?
|
||||
trigger = true unless value.nil?
|
||||
|
||||
if scope.nil?
|
||||
index = @environments.find_index do |e|
|
||||
@@ -157,7 +166,7 @@ module Liquid
|
||||
scope = @environments[index || -1]
|
||||
end
|
||||
|
||||
variable ||= lookup_and_evaluate(scope, key, raise_on_not_found: raise_on_not_found)
|
||||
variable ||= lookup_and_evaluate(scope, key, trigger, raise_on_not_found: raise_on_not_found)
|
||||
|
||||
variable = variable.to_liquid
|
||||
variable.context = self if variable.respond_to?(:context=)
|
||||
@@ -165,12 +174,16 @@ module Liquid
|
||||
variable
|
||||
end
|
||||
|
||||
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
||||
def lookup_and_evaluate(obj, key, trigger = false, raise_on_not_found: true)
|
||||
if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
|
||||
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
||||
end
|
||||
|
||||
value = obj[key]
|
||||
value = if trigger == true
|
||||
obj[key][-1]
|
||||
else
|
||||
obj[key]
|
||||
end
|
||||
|
||||
if value.is_a?(Proc) && obj.respond_to?(:[]=)
|
||||
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
||||
@@ -192,7 +205,7 @@ module Liquid
|
||||
@scope.each_key do |k|
|
||||
@environments.each do |env|
|
||||
if env.key?(k)
|
||||
@scope[k] = lookup_and_evaluate(env, k)
|
||||
@scope[k] = [lookup_and_evaluate(env, k)]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ module Liquid
|
||||
|
||||
def render(context)
|
||||
val = @from.render(context)
|
||||
context[@to] = val
|
||||
context.set_root(@to, val)
|
||||
context.resource_limits.assign_score += assign_score_of(val)
|
||||
''.freeze
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ module Liquid
|
||||
|
||||
def render(context)
|
||||
output = super
|
||||
context[@to] = output
|
||||
context.set_root(@to, output)
|
||||
context.resource_limits.assign_score += output.bytesize
|
||||
''.freeze
|
||||
end
|
||||
|
||||
@@ -168,6 +168,7 @@ module Liquid
|
||||
context[@variable_name] = item
|
||||
result << @for_block.render(context)
|
||||
loop_vars.send(:increment!)
|
||||
context.unset(@variable_name)
|
||||
|
||||
# Handle any interrupts if they exist.
|
||||
if context.interrupt?
|
||||
@@ -176,6 +177,8 @@ module Liquid
|
||||
next if interrupt.is_a? ContinueInterrupt
|
||||
end
|
||||
end
|
||||
|
||||
context.unset('forloop'.freeze)
|
||||
ensure
|
||||
for_stack.pop
|
||||
end
|
||||
|
||||
@@ -369,7 +369,7 @@ HERE
|
||||
end
|
||||
|
||||
def test_overwriting_internal_variable
|
||||
template = <<-END_TEMPLATE
|
||||
template = <<-HEREDOC
|
||||
{% assign forloop = 'first' %}
|
||||
|
||||
{% for item in items %}
|
||||
@@ -379,7 +379,7 @@ HERE
|
||||
{% endfor %}
|
||||
|
||||
{{ forloop }}
|
||||
END_TEMPLATE
|
||||
HEREDOC
|
||||
|
||||
result = Liquid::Template.parse(template).render('items' => '1')
|
||||
assert_equal 'Liquid::ForloopDrop Liquid::ForloopDrop second', result.split.map(&:strip).join(' ')
|
||||
|
||||
@@ -163,35 +163,6 @@ class ContextUnitTest < Minitest::Test
|
||||
assert_equal 'test', @context['test']
|
||||
end
|
||||
|
||||
def test_add_item_in_inner_scope
|
||||
@context.stack('test') do
|
||||
@context['test'] = 'test'
|
||||
assert_equal 'test', @context['test']
|
||||
end
|
||||
|
||||
assert_nil @context['test']
|
||||
end
|
||||
|
||||
def test_nested_scopes
|
||||
@context['test'] = 1
|
||||
|
||||
@context.stack('test') do
|
||||
assert_equal 1, @context['test']
|
||||
@context['test'] = 2
|
||||
assert_equal 2, @context['test']
|
||||
|
||||
@context.stack('test') do
|
||||
assert_equal 2, @context['test']
|
||||
@context['test'] = 3
|
||||
assert_equal 3, @context['test']
|
||||
end
|
||||
|
||||
assert_equal 2, @context['test']
|
||||
end
|
||||
|
||||
assert_equal 1, @context['test']
|
||||
end
|
||||
|
||||
def test_hierachical_data
|
||||
@context['hash'] = { "name" => 'tobi' }
|
||||
assert_equal 'tobi', @context['hash.name']
|
||||
|
||||
Reference in New Issue
Block a user