defmodule ExCrawlzy.Client.Handlers.Fields do
@moduledoc """
Internal module to handle json fields, mainly helps to create modules with defined keys on compile
add to the module the `use` and use the macro `add_field/3`
```elixir
defmodule Some.Module do
use ExCrawlzy.Client.Handlers.Fields
add_field(:name, "a.mr-1 span.repo", :text)
...
```
and adds the function `fields/0` to the module
```elixir
> Some.Module.fields()
%{name: {"a.mr-1 span.repo", :text}}
```
"""
defmacro __using__(_) do
quote do
import ExCrawlzy.Client.Handlers.Fields
@moduledoc """
Get all the fields added with the macro `add_field/3`
"""
def fields() do
mod_functions = __MODULE__.__info__(:functions)
get_available_selectors()
|> Enum.reduce(%{}, fn k, acc ->
{selector, func} = get_selector_data(k)
func = if Keyword.has_key?(mod_functions, func), do: {__MODULE__, func}, else: func
Map.put(acc, k, {selector, func})
end)
end
Module.register_attribute(__MODULE__, :selectors, accumulate: true)
@before_compile ExCrawlzy.Client.Handlers.Fields
end
end
defmacro __before_compile__(env) do
selectors = Module.get_attribute(env.module, :selectors)
available_selectors = Enum.map(selectors, &Map.get(&1, :field_name))
selectors =
Enum.map(selectors, fn %{field_name: field_name, selector: selector, func: func} ->
quote do
defp get_selector_data(unquote(field_name)), do: {unquote(selector), unquote(func)}
end
end)
quote do
unquote(selectors)
defp get_available_selectors(), do: unquote(available_selectors)
end
end
@doc """
Add the params in the inner data, all are saved on the module and you can get the whole data using the function `fields/0`
"""
defmacro add_field(field_name, selector, func) do
quote do
@selectors %{field_name: unquote(field_name), selector: unquote(selector), func: unquote(func)}
end
end
end