mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-29 15:15:19 -07:00
further differentiate between environments and only evaluate Procs once
This commit is contained in:
+31
-18
@@ -13,16 +13,15 @@ module Liquid
|
|||||||
#
|
#
|
||||||
# context['bob'] #=> nil class Context
|
# context['bob'] #=> nil class Context
|
||||||
class Context
|
class Context
|
||||||
attr_reader :scopes
|
attr_reader :scopes, :errors, :registers, :environments
|
||||||
attr_reader :errors, :registers, :environment
|
|
||||||
|
|
||||||
def initialize(environment = {}, instance_assigns = {}, registers = {}, rethrow_errors = false)
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false)
|
||||||
@environment = environment
|
@environments = [environments].flatten
|
||||||
@scopes = [(instance_assigns || {})]
|
@scopes = [(outer_scope || {})]
|
||||||
@registers = registers
|
@registers = registers
|
||||||
@errors = []
|
@errors = []
|
||||||
@rethrow_errors = rethrow_errors
|
@rethrow_errors = rethrow_errors
|
||||||
squash_instance_assigns_with_environment
|
squash_instance_assigns_with_environments
|
||||||
end
|
end
|
||||||
|
|
||||||
def strainer
|
def strainer
|
||||||
@@ -162,13 +161,17 @@ module Liquid
|
|||||||
# fetches an object starting at the local scope and then moving up
|
# fetches an object starting at the local scope and then moving up
|
||||||
# the hierachy
|
# the hierachy
|
||||||
def find_variable(key)
|
def find_variable(key)
|
||||||
scope = @scopes.find { |s| s.has_key?(key) } || environment
|
scope = @scopes.find { |s| s.has_key?(key) }
|
||||||
|
if scope.nil?
|
||||||
if scope[key].is_a?(Proc)
|
@environments.each do |e|
|
||||||
variable = scope[key] = scope[key].call(self)
|
if variable = lookup_and_evaluate(e, key)
|
||||||
else
|
scope = e
|
||||||
variable = scope[key]
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
scope ||= @environments.last || @scopes.last
|
||||||
|
variable ||= lookup_and_evaluate(scope, key)
|
||||||
|
|
||||||
variable = variable.to_liquid
|
variable = variable.to_liquid
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
variable.context = self if variable.respond_to?(:context=)
|
||||||
@@ -204,8 +207,7 @@ module Liquid
|
|||||||
(object.respond_to?(:fetch) and part.is_a?(Integer)))
|
(object.respond_to?(:fetch) and part.is_a?(Integer)))
|
||||||
|
|
||||||
# if its a proc we will replace the entry with the proc
|
# if its a proc we will replace the entry with the proc
|
||||||
res = object[part]
|
res = lookup_and_evaluate(object, part)
|
||||||
res = object[part] = res.call(self) if res.is_a?(Proc) and object.respond_to?(:[]=)
|
|
||||||
object = res.to_liquid
|
object = res.to_liquid
|
||||||
|
|
||||||
# Some special cases. If the part wasn't in square brackets and
|
# Some special cases. If the part wasn't in square brackets and
|
||||||
@@ -229,10 +231,21 @@ module Liquid
|
|||||||
object
|
object
|
||||||
end
|
end
|
||||||
|
|
||||||
def squash_instance_assigns_with_environment
|
def lookup_and_evaluate(obj, key)
|
||||||
scopes[0].each_key do |k|
|
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=)
|
||||||
if environment.has_key?(k)
|
obj[key] = value.call(self)
|
||||||
scopes[0][k] = environment[k]
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def squash_instance_assigns_with_environments
|
||||||
|
@scopes.last.each_key do |k|
|
||||||
|
@environments.each do |env|
|
||||||
|
if env.has_key?(k)
|
||||||
|
scopes.last[k] = lookup_and_evaluate(env, k)
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -93,12 +93,9 @@ module Liquid
|
|||||||
when Liquid::Context
|
when Liquid::Context
|
||||||
args.shift
|
args.shift
|
||||||
when Hash
|
when Hash
|
||||||
environment = args.shift
|
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors)
|
||||||
environment.merge!(assigns) {|k,v1,v2| v1}
|
|
||||||
Context.new(environment, instance_assigns, registers, @rethrow_errors)
|
|
||||||
when nil
|
when nil
|
||||||
environment = assigns.dup
|
Context.new(assigns, instance_assigns, registers, @rethrow_errors)
|
||||||
Context.new(environment, instance_assigns, registers, @rethrow_errors)
|
|
||||||
else
|
else
|
||||||
raise ArgumentError, "Expect Hash or Liquid::Context as parameter"
|
raise ArgumentError, "Expect Hash or Liquid::Context as parameter"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@template = Liquid::Template.new
|
@context = Liquid::Context.new
|
||||||
@context = Liquid::Context.new(@template.assigns, @template.registers)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_variables
|
def test_variables
|
||||||
@@ -151,11 +150,11 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context = Context.new(@template)
|
context = Context.new
|
||||||
context.add_filters(filter)
|
context.add_filters(filter)
|
||||||
assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
|
assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
|
||||||
|
|
||||||
context = Context.new(@template)
|
context = Context.new
|
||||||
assert_raises(FilterNotFound) {
|
assert_raises(FilterNotFound) {
|
||||||
context.invoke(:hi, 'hi?')
|
context.invoke(:hi, 'hi?')
|
||||||
}
|
}
|
||||||
@@ -191,7 +190,7 @@ class ContextTest < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context = Context.new(@template)
|
context = Context.new
|
||||||
methods_before = context.strainer.methods.map { |method| method.to_s }
|
methods_before = context.strainer.methods.map { |method| method.to_s }
|
||||||
context.add_filters(filter)
|
context.add_filters(filter)
|
||||||
methods_after = context.strainer.methods.map { |method| method.to_s }
|
methods_after = context.strainer.methods.map { |method| method.to_s }
|
||||||
|
|||||||
@@ -54,4 +54,22 @@ class TemplateTest < Test::Unit::TestCase
|
|||||||
assert_equal 'from persistent assigns', t.parse("{{ foo }}").render
|
assert_equal 'from persistent assigns', t.parse("{{ foo }}").render
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders
|
||||||
|
t = Template.new
|
||||||
|
t.assigns['number'] = lambda { @global ||= 0; @global += 1 }
|
||||||
|
assert_equal '1', t.parse("{{number}}").render
|
||||||
|
assert_equal '1', t.parse("{{number}}").render
|
||||||
|
assert_equal '1', t.render
|
||||||
|
@global = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
||||||
|
t = Template.new
|
||||||
|
assigns = {'number' => lambda { @global ||= 0; @global += 1 }}
|
||||||
|
assert_equal '1', t.parse("{{number}}").render(assigns)
|
||||||
|
assert_equal '1', t.parse("{{number}}").render(assigns)
|
||||||
|
assert_equal '1', t.render(assigns)
|
||||||
|
@global = nil
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
Reference in New Issue
Block a user