Skip to content

Commit

Permalink
Custom matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
edgurgel committed Jul 9, 2024
1 parent d971fd2 commit 6ce8c3a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 6 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ template |> Solid.parse!() |> Solid.render!(context) |> to_string()
# => [email protected]: John Doe
```

If the `Solid.Matcher` protocol is not enough one can provide their own module like this:

```elixir
defmodule MyMatcher do
def match(data, keys), do: {:ok, 42}
end

# ...
Solid.render(template, %{"number" => 4}, matcher_module: MyMatcher)
```

## Contributing

When adding new functionality or fixing bugs consider adding a new test case here inside `test/cases`. These cases are tested against the Ruby gem so we can try to stay as close as possible to the original implementation.
Expand Down
5 changes: 4 additions & 1 deletion lib/solid.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ defmodule Solid do
- `strict_filters`: if `true`, it collects an error when a filter is referenced in the template, but not built-in or provided via `custom_filters`
- `matcher_module`: a module to replace `Solid.Matcher` when resolving variables.
## Example
fs = Solid.LocalFileSystem.new("/path/to/template/dir/")
Expand All @@ -113,7 +115,8 @@ defmodule Solid do
@spec render(%Template{}, map, Keyword.t()) :: {:ok, iolist} | {:error, list(errors), iolist}
@spec render(list, %Context{}, Keyword.t()) :: {iolist, %Context{}}
def render(%Template{parsed_template: parsed_template}, hash, options) do
context = %Context{counter_vars: hash}
matcher_module = Keyword.get(options, :matcher_module, Solid.Matcher)
context = %Context{counter_vars: hash, matcher_module: matcher_module}

{result, context} = render(parsed_template, context, options)

Expand Down
17 changes: 12 additions & 5 deletions lib/solid/context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ defmodule Solid.UndefinedFilterError do
end

defmodule Solid.Context do
defstruct vars: %{}, counter_vars: %{}, iteration_vars: %{}, cycle_state: %{}, errors: []
defstruct vars: %{},
counter_vars: %{},
iteration_vars: %{},
cycle_state: %{},
errors: [],
matcher_module: Solid.Matcher

@type t :: %__MODULE__{
vars: map,
counter_vars: map,
iteration_vars: %{optional(String.t()) => term},
cycle_state: map,
errors: list(Solid.UndefinedVariableError)
errors: list(Solid.UndefinedVariableError),
matcher_module: module
}
@type scope :: :counter_vars | :vars | :iteration_vars

Expand Down Expand Up @@ -82,14 +88,15 @@ defmodule Solid.Context do
end

defp get_from_scope(context, :vars, key) do
Solid.Matcher.match(context.vars, key)
IO.inspect({context, key})
context.matcher_module.match(context.vars, key)
end

defp get_from_scope(context, :counter_vars, key) do
Solid.Matcher.match(context.counter_vars, key)
context.matcher_module.match(context.counter_vars, key)
end

defp get_from_scope(context, :iteration_vars, key) do
Solid.Matcher.match(context.iteration_vars, key)
context.matcher_module.match(context.iteration_vars, key)
end
end
36 changes: 36 additions & 0 deletions test/context_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,42 @@ defmodule Solid.ContextTest do
end
end

defmodule CustomMatcher do
def match(_, _), do: {:ok, 42}
end

describe "get_in/3 with custom matcher module" do
setup do
context = %Context{matcher_module: CustomMatcher}
{:ok, context: context}
end

test "counter_vars scope only", %{context: context} do
context = %{context | counter_vars: %{"x" => 1}}
assert Context.get_in(context, ["x"], [:counter_vars]) == {:ok, 42}
end

test "vars scope only", %{context: context} do
context = %{context | vars: %{"x" => 1}}
assert Context.get_in(context, ["x"], [:vars]) == {:ok, 42}
end

test "var scope with false value", %{context: context} do
context = %{context | vars: %{"x" => false}}
assert Context.get_in(context, ["x"], [:vars]) == {:ok, 42}
end

test "var scope with nil value", %{context: context} do
context = %{context | vars: %{"x" => nil}}
assert Context.get_in(context, ["x"], [:vars]) == {:ok, 42}
end

test "iteration_vars scope only", %{context: context} do
context = %{context | iteration_vars: %{"x" => 1}}
assert Context.get_in(context, ["x"], [:iteration_vars]) == {:ok, 42}
end
end

describe "run_cycle/2" do
test "first run" do
cycle = [values: ["one", "two", "three"]]
Expand Down
15 changes: 15 additions & 0 deletions test/solid_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,20 @@ defmodule SolidTest do
assert IO.iodata_to_binary(result) == "var1: "
assert errors == [%Solid.UndefinedVariableError{variable: ["var1"]}]
end

defmodule CustomMatcher do
def match(_, _), do: {:ok, 42}
end

test "custom matcher" do
template = "var1: {{ var1 }}"

assert {:ok, result} =
template
|> Solid.parse!()
|> Solid.render(%{"var1" => "value1"}, matcher_module: CustomMatcher)

assert IO.iodata_to_binary(result) == "var1: 42"
end
end
end

0 comments on commit 6ce8c3a

Please sign in to comment.