diff --git a/Gemfile b/Gemfile index 31561a9e0..b9b79f4f4 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,6 @@ group :test do gem 'rubocop-performance', require: false platform :mri, :truffleruby do - gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'master' + gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'main' end end diff --git a/lib/liquid/context.rb b/lib/liquid/context.rb index 5b1a01ee1..80720a450 100644 --- a/lib/liquid/context.rb +++ b/lib/liquid/context.rb @@ -197,10 +197,14 @@ def find_variable(key, raise_on_not_found: true) try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found) end - variable = variable.to_liquid + # update variable's context before invoking #to_liquid variable.context = self if variable.respond_to?(:context=) - variable + liquid_variable = variable.to_liquid + + liquid_variable.context = self if variable != liquid_variable && liquid_variable.respond_to?(:context=) + + liquid_variable end def lookup_and_evaluate(obj, key, raise_on_not_found: true) diff --git a/test/integration/context_test.rb b/test/integration/context_test.rb index a25362a67..03b33cf58 100644 --- a/test/integration/context_test.rb +++ b/test/integration/context_test.rb @@ -36,6 +36,24 @@ def to_liquid end end +class ProductsDrop < Liquid::Drop + def initialize(products) + @products = products + end + + def size + @products.size + end + + def to_liquid + if @context["forloop"] + @products.first(@context["forloop"].length) + else + @products + end + end +end + class CategoryDrop < Liquid::Drop attr_accessor :category, :context @@ -635,6 +653,25 @@ def test_context_always_uses_static_registers assert_equal(:my_value, c.registers[:my_register]) end + def test_variable_to_liquid_returns_contextual_drop + context = { + "products" => ProductsDrop.new(["A", "B", "C", "D", "E"]), + } + + template = Liquid::Template.parse(<<~LIQUID) + {%- for i in (1..3) -%} + for_loop_products_count: {{ products | size }} + {% endfor %} + + unscoped_products_count: {{ products | size }} + LIQUID + + result = template.render(context) + + assert_includes(result, "for_loop_products_count: 3") + assert_includes(result, "unscoped_products_count: 5") + end + private def assert_no_object_allocations