Skip to content

Commit

Permalink
Use a variable to access a hash
Browse files Browse the repository at this point in the history
  • Loading branch information
jmks authored and edgurgel committed Nov 19, 2024
1 parent 14b5c2c commit b1962ba
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 9 deletions.
35 changes: 27 additions & 8 deletions lib/solid/context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,9 @@ defmodule Solid.Context do
"""
@spec get_in(t(), [term()], [scope]) :: {:ok, term} | {:error, {:not_found, [term()]}}
def get_in(context, key, scopes) do
scopes
|> Enum.reverse()
|> Enum.map(&get_from_scope(context, &1, key))
|> Enum.reduce({:error, {:not_found, key}}, fn
{:ok, nil}, acc = {:ok, _} -> acc
value = {:ok, _}, _acc -> value
_value, acc -> acc
end)
resolved_key = resolve_references(context, key, scopes)

lookup_key(context, resolved_key, scopes)
end

@doc """
Expand Down Expand Up @@ -81,6 +76,30 @@ defmodule Solid.Context do
end
end

defp resolve_references(context, key, scopes) do
Enum.map(key, fn
{:reference, reference} ->
case lookup_key(context, [reference], scopes) do
{:ok, resolved} -> resolved
{:error, _} -> reference
end

part ->
part
end)
end

defp lookup_key(context, key, scopes) do
scopes
|> Enum.reverse()
|> Enum.map(&get_from_scope(context, &1, key))
|> Enum.reduce({:error, {:not_found, key}}, fn
{:ok, nil}, acc = {:ok, _} -> acc
value = {:ok, _}, _acc -> value
_value, acc -> acc
end)
end

defp cycle_to_map(cycle) do
cycle
|> Enum.with_index()
Expand Down
7 changes: 6 additions & 1 deletion lib/solid/parser/variable.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ defmodule Solid.Parser.Variable do

def bracket_access do
ignore(string("["))
|> choice([Literal.int(), Literal.single_quoted_string(), Literal.double_quoted_string()])
|> choice([
Literal.int(),
Literal.single_quoted_string(),
Literal.double_quoted_string(),
unwrap_and_tag(identifier(), :reference)
])
|> ignore(string("]"))
end

Expand Down
18 changes: 18 additions & 0 deletions test/context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ defmodule Solid.ContextTest do
assert Context.get_in(context, ["x"], [:vars, :counter_vars]) == {:ok, 2}
end

test "counter_vars & vars scopes with nested access by a reference" do
context = %Context{
counter_vars: %{"x" => %{"y" => 1, "z" => "2"}},
vars: %{"variable" => "y"}
}

assert Context.get_in(context, ["x", {:reference, "variable"}], [:vars, :counter_vars]) == {:ok, 1}
end

test "missing reference" do
context = %Context{
counter_vars: %{"x" => %{"y" => %{"z" => "2"}}},
vars: %{"variable" => "q"}
}

assert Context.get_in(context, ["x", "y", {:reference, "missing"}], [:vars, :counter_vars]) == {:error, {:not_found, ["x", "y", "missing"]}}
end

test "list access" do
context = %Context{vars: %{"x" => ["a", "b", "c"]}}
assert Context.get_in(context, ["x", 1], [:vars]) == {:ok, "b"}
Expand Down
61 changes: 61 additions & 0 deletions test/integration/objects_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,67 @@ defmodule Solid.Integration.ObjectsTest do
assert render("Number {{ key[1] }}", %{"key" => [1, 2, 3]}) == "Number 2"
end

test "field access by literal string" do
template = """
{%- assign greeting = greetings['casual'].text -%}
<p>{{ greeting }} world</p>
"""

data = %{
"greetings" => %{
"formal" => %{"text" => "Hello"},
"casual" => %{"text" => "Hey!"},
"friendly" => %{"text" => "Yo!"}
}
}

assert render(template, data) == """
<p>Hey! world</p>
"""
end

test "field access by variable" do
template = """
{%- assign greet_as = 'friendly' -%}
{%- assign greeting = greetings[greet_as].text -%}
<p>{{ greeting }} world</p>
"""

data = %{
"greetings" => %{
"formal" => %{"text" => "Hello"},
"casual" => %{"text" => "Hey!"},
"friendly" => %{"text" => "Yo!"}
}
}

assert render(template, data) == """
<p>Yo! world</p>
"""
end

test "field access through many variables" do
template = """
{%- assign greet_as = 'casual' -%}
{%- assign really_greet_as = greet_as -%}
{%- assign really_really_greet_as = really_greet_as -%}
{%- assign greeting = greetings[really_really_greet_as].text -%}
<p>{{ greeting }} world</p>
"""

data = %{
"greetings" => %{
"formal" => %{"text" => "Hello"},
"casual" => %{"text" => "Hey!"},
"friendly" => %{"text" => "Yo!"}
}
}

assert render(template, data) == """
<p>Hey! world</p>
"""
end

test "complex key rendering" do
hash = %{"key1" => %{"key2" => %{"key3" => 123}}}
assert render("Number {{ key1.key2.key3 }} !", hash) == "Number 123 !"
Expand Down

0 comments on commit b1962ba

Please sign in to comment.