Shopify / liquid

Liquid markup language. Safe, customer facing template language for flexible web apps.
https://shopify.github.io/liquid/
MIT License
11.11k stars 1.39k forks source link

Cannot re-assign variable inside snippet #911

Open tophattom opened 7 years ago

tophattom commented 7 years ago

In template:

{% include 'my-snippet', test_variable: 'foo' %}

my-snippet.liquid

{%- assign test_variable = 'bar' -%}
{{ test_variable }}

Results in foo instead of bar.

However, everything works as expected when including the snippet like this:

{%- assign test_variable = 'foo' -%}
{% include 'my-snippet' %}
danshep commented 7 years ago

The assign tag writes to the last scope in the context. Everything else in liquid writes (and reads) from the scopes from first through last.

    def render(context)
      val = @from.render(context)
      context.scopes.last[@to] = val
      context.resource_limits.assign_score += assign_score_of(val)
      ''.freeze
    end

It feels like a bug, but it's been the behaviour of liquid since forever.

The behaviour likely exists to allow the assign method to work within a nested blocks.

{% for page in page %}
  {% if page.awesome %}
    {% assign awesome_page = page %}
    {% break %}
  {% endif %}
{% endfor %}

{% if awesome_page %}
  The page {{page.title}} is awesome.
{% endif %}

It's super-wonky, but changing the behavior would likely break existing templates.

The best solution would probably be to change the assign method to search through the scopes to find where the variable was assigned and modify it in that scope. It's weird that only variables set in the root scope can be reassigned.