diff --git a/lib/liquid/context.rb b/lib/liquid/context.rb
index 2d739d2..4c9b9b4 100644
--- a/lib/liquid/context.rb
+++ b/lib/liquid/context.rb
@@ -14,13 +14,15 @@ module Liquid
# context['bob'] #=> nil class Context
class Context
attr_reader :scopes
- attr_reader :errors, :registers
+ attr_reader :errors, :registers, :environment
- def initialize(assigns = {}, registers = {}, rethrow_errors = false)
- @scopes = [(assigns || {})]
- @registers = registers
- @errors = []
+ def initialize(environment = {}, instance_assigns = {}, registers = {}, rethrow_errors = false)
+ @environment = environment
+ @scopes = [(instance_assigns || {})]
+ @registers = registers
+ @errors = []
@rethrow_errors = rethrow_errors
+ squash_instance_assigns_with_environment
end
def strainer
@@ -61,9 +63,9 @@ module Liquid
end
# push new local scope on the stack. use Context#stack instead
- def push
+ def push(new_scope={})
raise StackLevelError, "Nesting too deep" if @scopes.length > 100
- @scopes.unshift({})
+ @scopes.unshift(new_scope)
end
# merge a hash of variables in the current local scope
@@ -86,9 +88,9 @@ module Liquid
# end
# context['var] #=> nil
#
- def stack(&block)
+ def stack(new_scope={},&block)
result = nil
- push
+ push(new_scope)
begin
result = yield
ensure
@@ -96,6 +98,10 @@ module Liquid
end
result
end
+
+ def clear_instance_assigns
+ @scopes[0] = {}
+ end
# Only allow String, Numeric, Hash, Array, Proc, Boolean or Liquid::Drop
def []=(key, value)
@@ -156,9 +162,14 @@ module Liquid
# fetches an object starting at the local scope and then moving up
# the hierachy
def find_variable(key)
- scope = @scopes[0..-2].find { |s| s.has_key?(key) } || @scopes.last
- variable = scope[key]
- variable = scope[key] = variable.call(self) if variable.is_a?(Proc)
+ scope = @scopes.find { |s| s.has_key?(key) } || environment
+
+ if scope[key].is_a?(Proc)
+ variable = scope[key] = scope[key].call(self)
+ else
+ variable = scope[key]
+ end
+
variable = variable.to_liquid
variable.context = self if variable.respond_to?(:context=)
return variable
@@ -217,5 +228,14 @@ module Liquid
object
end
+
+ def squash_instance_assigns_with_environment
+ scopes[0].each_key do |k|
+ if environment.has_key?(k)
+ scopes[0][k] = environment[k]
+ end
+ end
+ end
+
end
end
diff --git a/lib/liquid/template.rb b/lib/liquid/template.rb
index fae8ae3..a9ab882 100644
--- a/lib/liquid/template.rb
+++ b/lib/liquid/template.rb
@@ -67,6 +67,10 @@ module Liquid
@assigns ||= {}
end
+ def instance_assigns
+ @instance_assigns ||= {}
+ end
+
def errors
@errors ||= []
end
@@ -84,16 +88,17 @@ module Liquid
#
def render(*args)
return '' if @root.nil?
-
+
context = case args.first
when Liquid::Context
args.shift
when Hash
- a = args.shift
- assigns.each { |k,v| a[k] = v unless a.has_key?(k) }
- Context.new(a, registers, @rethrow_errors)
+ environment = args.shift
+ environment.merge!(assigns) {|k,v1,v2| v1}
+ Context.new(environment, instance_assigns, registers, @rethrow_errors)
when nil
- Context.new(assigns.dup, registers, @rethrow_errors)
+ environment = assigns.dup
+ Context.new(environment, instance_assigns, registers, @rethrow_errors)
else
raise ArgumentError, "Expect Hash or Liquid::Context as parameter"
end
diff --git a/test/template_test.rb b/test/template_test.rb
index d96d274..6f49a07 100644
--- a/test/template_test.rb
+++ b/test/template_test.rb
@@ -23,4 +23,35 @@ class TemplateTest < Test::Unit::TestCase
assert_equal [' ', '{% comment %}', ' ', '{% endcomment %}', ' '], Template.new.send(:tokenize, " {% comment %} {% endcomment %} ")
end
+ def test_instance_assigns_persist_on_same_template_object_between_parses
+ t = Template.new
+ assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
+ assert_equal 'from instance assigns', t.parse("{{ foo }}").render
+ end
+
+ def test_instance_assigns_persist_on_same_template_parsing_between_renders
+ t = Template.new.parse("{{ foo }}{% assign foo = 'foo' %}{{ foo }}")
+ assert_equal 'foo', t.render
+ assert_equal 'foofoo', t.render
+ end
+
+ def test_custom_assigns_do_not_persist_on_same_template
+ t = Template.new
+ assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns')
+ assert_equal '', t.parse("{{ foo }}").render
+ end
+
+ def test_custom_assigns_squash_instance_assigns
+ t = Template.new
+ assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
+ assert_equal 'from custom assigns', t.parse("{{ foo }}").render('foo' => 'from custom assigns')
+ end
+
+ def test_persistent_assigns_squash_instance_assigns
+ t = Template.new
+ assert_equal 'from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render
+ t.assigns['foo'] = 'from persistent assigns'
+ assert_equal 'from persistent assigns', t.parse("{{ foo }}").render
+ end
+
end
\ No newline at end of file