Why variable defined in {% if ... %} invisible outside the macro?

When I tried to fix an issue in markd shards, I encountered a strange problem.

Following is the reduced code for reproduce.

require "uri"

class Foo
  def hello
    str = "你好"

    {% if compare_versions(Crystal::VERSION, "1.2.0") < 0 %}
      encoded_str = URI.encode(str)
    {% else %}
      encoded_str = URI.encode_path(str)
    {% end %}

    p encoded_str # not work, Error: undefined method 'encoded_str' for Foo
    p! encoded_str # "%E4%BD%A0%E5%A5%BD", it works
  end
end

foo = Foo.new
foo.hello

Why above code, p! encoded_str work no error, but when p encoded_str, get undefined method 'encoded_str' for Foo?

Thanks

Local variables have to be declared directly in the current scope, not inside any macros. Try:

require "uri"

class Foo
  def hello
    str = "你好"

    encoded_str = {% if compare_versions(Crystal::VERSION, "1.2.0") < 0 %}
      URI.encode(str)
    {% else %}
      URI.encode_path(str)
    {% end %}

    p encoded_str # not work, Error: undefined method 'encoded_str' for Foo
    p! encoded_str # "%E4%BD%A0%E5%A5%BD", it works
  end
end

foo = Foo.new
foo.hello

But, why p! encoded_str work? it not wrapped by {% … %} anyway.

It’s likely that p! is a macro itself so maybe that changes something. Same as as why just wrapping your original p encoded_str line with {% begin %} / {% end %} works as well.

2 Likes