Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
run: mix deps.get --only test

- name: Install liquid gem
run: gem install liquid -v 5.8.1
run: gem install liquid -v 5.12.0

- name: Run tests
run: mix test --trace --include integration
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 1.3.2 (2026-06-14)

## Bug fixes

* Fix `empty` and `blank` comparison cases

# 1.3.1 (2026-06-10)

## Bug fixes
Expand Down
1 change: 1 addition & 0 deletions lib/solid/argument.ex
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ defmodule Solid.Argument do
end

defp stringify!(%Literal.Empty{}), do: ""
defp stringify!(%Literal.Blank{}), do: ""

defp stringify!(range) when is_struct(range, Range) do
"#{range.first}..#{range.last}"
Expand Down
24 changes: 17 additions & 7 deletions lib/solid/binary_condition.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Solid.BinaryCondition do
alias Solid.{Argument, Filter}
alias Solid.Literal.Empty
alias Solid.Literal.{Blank, Empty}

defstruct [
:loc,
Expand All @@ -23,14 +23,24 @@ defmodule Solid.BinaryCondition do
}

@spec eval({term, Solid.Lexer.operator(), term}) :: {:ok, boolean} | {:error, binary}
def eval({v1, :==, %Empty{}}) when is_map(v1) and not is_struct(v1), do: {:ok, v1 == %{}}
def eval({%Empty{}, :==, v2}) when is_map(v2) and not is_struct(v2), do: {:ok, v2 == %{}}

def eval({v1, :==, %Empty{}}) when is_list(v1), do: {:ok, v1 == []}
def eval({%Empty{}, :==, v2}) when is_list(v2), do: {:ok, v2 == []}
for struct <- [Blank, Empty] do
@struct struct
def eval({v1, :==, %@struct{}}) when is_map(v1) and not is_struct(v1), do: {:ok, v1 == %{}}
def eval({%@struct{}, :==, v2}) when is_map(v2) and not is_struct(v2), do: {:ok, v2 == %{}}

def eval({v1, _, %Empty{}}) when is_map(v1) and not is_struct(v1), do: {:ok, false}
def eval({%Empty{}, _, v2}) when is_map(v2) and not is_struct(v2), do: {:ok, false}
def eval({v1, :==, %@struct{}}) when is_list(v1), do: {:ok, v1 == []}
def eval({%@struct{}, :==, v2}) when is_list(v2), do: {:ok, v2 == []}

def eval({v1, :==, %@struct{}}) when is_binary(v1), do: {:ok, v1 == ""}
def eval({%@struct{}, :==, v2}) when is_binary(v2), do: {:ok, v2 == ""}

def eval({v1, _, %@struct{}}) when is_map(v1) and not is_struct(v1), do: {:ok, false}
def eval({%@struct{}, _, v2}) when is_map(v2) and not is_struct(v2), do: {:ok, false}
end

def eval({nil, :==, %Blank{}}), do: {:ok, true}
def eval({%Blank{}, :==, nil}), do: {:ok, true}

def eval({v1, _, v2})
when (is_map(v1) or is_map(v2)) and not is_struct(v1) and not is_struct(v2),
Expand Down
6 changes: 5 additions & 1 deletion lib/solid/literal.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ defmodule Solid.Literal do
defstruct []
end

defmodule Blank do
defstruct []
end

@enforce_keys [:loc, :value]
defstruct [:loc, :value]

@type value :: boolean | nil | binary | integer | float | %Empty{}
@type value :: boolean | nil | binary | integer | float | %Empty{} | %Blank{}
@type t :: %__MODULE__{loc: Loc.t(), value: value}

defimpl String.Chars do
Expand Down
10 changes: 7 additions & 3 deletions lib/solid/standard_filter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Solid.StandardFilter do
Standard filters
"""

alias Solid.Literal.Empty
alias Solid.Literal.{Blank, Empty}

import Kernel, except: [abs: 1, ceil: 1, round: 1, floor: 1, apply: 2]

Expand Down Expand Up @@ -261,7 +261,7 @@ defmodule Solid.StandardFilter do
456
"""

@empty_values [nil, false, [], "", %{}, %Empty{}]
@empty_values [nil, false, [], "", %{}, %Empty{}, %Blank{}]

@spec default(any, any, map) :: any
def default(input, value \\ "", opts \\ %{}) do
Expand Down Expand Up @@ -348,6 +348,7 @@ defmodule Solid.StandardFilter do
"""
@spec first(term) :: any
def first(input) when is_list(input), do: List.first(input)
def first(input) when is_binary(input), do: String.first(input)
def first(start.._//_), do: start
# Maps are not ordered making this result not consistent with Ruby's liquid ordered hash
def first(input) when is_map(input) do
Expand Down Expand Up @@ -455,6 +456,7 @@ defmodule Solid.StandardFilter do
"""
@spec last(list) :: any
def last(input) when is_list(input), do: List.last(input)
def last(input) when is_binary(input), do: String.last(input)
def last(_..finish//_), do: finish
def last(_), do: nil

Expand Down Expand Up @@ -628,7 +630,8 @@ defmodule Solid.StandardFilter do

true ->
cond do
is_struct(value, Empty) or is_binary(value) or is_boolean(value) or is_nil(value) ->
is_struct(value, Empty) or is_struct(value, Blank) or is_binary(value) or
is_boolean(value) or is_nil(value) ->
0

is_map(value) ->
Expand Down Expand Up @@ -715,6 +718,7 @@ defmodule Solid.StandardFilter do
end

defp to_str(%Empty{}), do: ""
defp to_str(%Blank{}), do: ""

# defp to_str(%datetime_module{} = datetime)
# when datetime_module in [DateTime, NaiveDateTime, Date, Time] do
Expand Down
2 changes: 1 addition & 1 deletion lib/solid/variable.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ defmodule Solid.Variable do
"true" -> true
"false" -> false
"empty" -> %Literal.Empty{}
"blank" -> ""
"blank" -> %Literal.Blank{}
end
end

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule Solid.MixProject do
use Mix.Project

@source_url "https://github.com/edgurgel/solid"
@version "1.3.1"
@version "1.3.2"

def project do
[
Expand Down
23 changes: 23 additions & 0 deletions test/solid/binary_condition_test.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule Solid.BinaryConditionTest do
use ExUnit.Case, async: true
alias Solid.Literal.{Blank, Empty}

import Solid.BinaryCondition

Expand Down Expand Up @@ -29,6 +30,28 @@ defmodule Solid.BinaryConditionTest do
assert eval({1, :<=, 0}) == {:ok, false}
end

test "literals" do
assert eval({%{}, :==, %Empty{}}) == {:ok, true}
assert eval({%Empty{}, :==, %{}}) == {:ok, true}
assert eval({%{}, :==, %Blank{}}) == {:ok, true}
assert eval({%Blank{}, :==, %{}}) == {:ok, true}

assert eval({[], :==, %Empty{}}) == {:ok, true}
assert eval({%Empty{}, :==, []}) == {:ok, true}
assert eval({[], :==, %Blank{}}) == {:ok, true}
assert eval({%Blank{}, :==, []}) == {:ok, true}

assert eval({"", :==, %Empty{}}) == {:ok, true}
assert eval({%Empty{}, :==, ""}) == {:ok, true}
assert eval({"", :==, %Blank{}}) == {:ok, true}
assert eval({%Blank{}, :==, ""}) == {:ok, true}

assert eval({nil, :==, %Empty{}}) == {:ok, false}
assert eval({%Empty{}, :==, nil}) == {:ok, false}
assert eval({nil, :==, %Blank{}}) == {:ok, true}
assert eval({%Blank{}, :==, nil}) == {:ok, true}
end

test "contains" do
assert eval({"jose", :contains, "o"}) == {:ok, true}
assert eval({"jose", :contains, "jose"}) == {:ok, true}
Expand Down
8 changes: 8 additions & 0 deletions test/solid/integration/scenarios/if/input.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ endif
{% if empty == array %}empty{% else %}not empty{% endif %}
{% if array == blank %}blank{% else %}not blank{% endif %}
{% if blank == array %}blank{% else %}not blank{% endif %}
{% if "" == blank %}blank{% else %}not blank{% endif %}
{% if blank == "" %}blank{% else %}not blank{% endif %}
{% if "" == empty %}empty{% else %}not empty{% endif %}
{% if empty == "" %}empty{% else %}not empty{% endif %}
{% if nil == blank %}blank{% else %}not blank{% endif %}
{% if blank == nil %}blank{% else %}not blank{% endif %}
{% if nil == empty %}empty{% else %}not empty{% endif %}
{% if empty == nil %}empty{% else %}not empty{% endif %}

Empty bodies
{% if true %} {% elsif false %} {% else %} {% endif %}
Expand Down
2 changes: 1 addition & 1 deletion test/solid/variable_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ defmodule Solid.VariableTest do
test "blank" do
assert parse("blank") == {
:ok,
%Solid.Literal{loc: %Loc{line: 1, column: 3}, value: ""},
%Solid.Literal{loc: %Loc{line: 1, column: 3}, value: %Solid.Literal.Blank{}},
[end: %{line: 1, column: 8}]
}
end
Expand Down
Loading