diff --git a/CHANGELOG.md b/CHANGELOG.md index 06c8163..c742cd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Master +### Breaking + +Components are now defined using modules. You can convert your existing components by configuring your component prefix and wrapping your current component files in the `Temple.Component` behaviour implementation. + ### Bugs - Did not correctly parse expressions with do blocks where the expression had two or more arguments before the block diff --git a/README.md b/README.md index 070774e..ff08310 100644 --- a/README.md +++ b/README.md @@ -58,28 +58,32 @@ end ### Components -To define a component, you can create a file in your configured temple -components directory, which defaults to `lib/components`. You would -probably want to change that to be `lib/my_app_web/components` if you -are building a phoenix app. +To define a component, you can define a module that that starts with your defined components prefix. The last name in the module should be a came-cases version of the component name. + +This module should implement the `Temple.Component` behaviour. ```elixir # config/config.exs -config :temple, :components_path, "./lib/my_app_web/components" +config :temple, :components_prefix, MyAppWeb.Components ``` -This file should be of the `.exs` extension. - You can then use this component in any other temple template. -For example, if I were to define a `flex` component, I would create a -file called `lib/my_app_web/components/flex.exs`, with the following -contents. +For example, if I were to define a `flex` component, I would create the following module. ```elixir -div class: "flex #{@temple[:class]}", id: @id do - @children +defmodule MyAppWeb.Components.Flex do + @behavior Temple.Component + + @impl Temple.Component + def render do + quote do + div class: "flex #{@temple[:class]}", id: @id do + @children + end + end + end end ``` diff --git a/config/config.exs b/config/config.exs index 8233fe9..e3dfb65 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,3 +1,4 @@ use Mix.Config +config :temple, :component_prefix, Temple.Components import_config "#{Mix.env()}.exs" diff --git a/integration_test/temple_demo/config/config.exs b/integration_test/temple_demo/config/config.exs index c4db027..2bbad10 100644 --- a/integration_test/temple_demo/config/config.exs +++ b/integration_test/temple_demo/config/config.exs @@ -12,8 +12,7 @@ config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase config :temple_demo, ecto_repos: [TempleDemo.Repo] -config :phoenix, :template_engines, - exs: Temple.Engine +config :phoenix, :template_engines, exs: Temple.Engine # Configures the endpoint config :temple_demo, TempleDemoWeb.Endpoint, @@ -31,11 +30,13 @@ config :logger, :console, # Use Jason for JSON parsing in Phoenix config :phoenix, :json_library, Jason -config :temple, :aliases, - label: :_label, - link: :_link, - textarea: :_textarea - +config :temple, + aliases: [ + label: :_label, + link: :_link, + textarea: :_textarea + ], + component_prefix: TempleDemoWeb.Component # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. diff --git a/lib/temple/component.ex b/lib/temple/component.ex new file mode 100644 index 0000000..d852d71 --- /dev/null +++ b/lib/temple/component.ex @@ -0,0 +1,23 @@ +defmodule Temple.Component do + @moduledoc """ + Behaviour for defining temple components. + """ + + @doc """ + The render callback must return AST to be inserted into the markup. + + Since you need to return AST, it is typical to wrap the contents of the function in a `quote` block like: + + ``` + @impl Temple.Component + def render() do + quote do + div do + @children + end + end + end + ``` + """ + @callback render() :: Macro.t() +end diff --git a/lib/temple/parsers/components.ex b/lib/temple/parsers/components.ex index f262f2a..cf6137d 100644 --- a/lib/temple/parsers/components.ex +++ b/lib/temple/parsers/components.ex @@ -1,15 +1,20 @@ defmodule Temple.Parser.Components do @behaviour Temple.Parser - @components_path Application.get_env(:temple, :components_path, "./lib/components") + @component_prefix Application.fetch_env!(:temple, :component_prefix) alias Temple.Parser def applicable?({name, meta, _}) when is_atom(name) do - !meta[:temple_component_applied] && File.exists?(Path.join([@components_path, "#{name}.exs"])) + !meta[:temple_component_applied] && + match?({:module, _}, name |> component_module() |> Code.ensure_compiled()) end def applicable?(_), do: false + defp component_module(name) do + Module.concat([@component_prefix, Macro.camelize(to_string(name))]) + end + def run({name, _meta, args}, _buffer) do {assigns, children} = case args do @@ -26,9 +31,9 @@ defmodule Temple.Parser.Components do {nil, nil} end - ast = - File.read!(Path.join([@components_path, "#{name}.exs"])) - |> Code.string_to_quoted!() + component_module = Module.concat([@component_prefix, Macro.camelize(to_string(name))]) + + ast = apply(component_module, :render, []) {name, meta, args} = ast diff --git a/test/support/components/component.ex b/test/support/components/component.ex new file mode 100644 index 0000000..5db2637 --- /dev/null +++ b/test/support/components/component.ex @@ -0,0 +1,12 @@ +defmodule Temple.Components.Component do + @behaviour Temple.Component + + @impl Temple.Component + def render do + quote do + div class: @assign do + @children + end + end + end +end diff --git a/test/support/components/component.exs b/test/support/components/component.exs deleted file mode 100644 index 1b05689..0000000 --- a/test/support/components/component.exs +++ /dev/null @@ -1,3 +0,0 @@ -div class: @assign do - @children -end diff --git a/test/support/components/component2.ex b/test/support/components/component2.ex new file mode 100644 index 0000000..6b81d58 --- /dev/null +++ b/test/support/components/component2.ex @@ -0,0 +1,12 @@ +defmodule Temple.Components.Component2 do + @behaviour Temple.Component + + @impl Temple.Component + def render do + quote do + div class: @class do + @children + end + end + end +end diff --git a/test/support/components/component2.exs b/test/support/components/component2.exs deleted file mode 100644 index bca867a..0000000 --- a/test/support/components/component2.exs +++ /dev/null @@ -1,3 +0,0 @@ -div class: @class do - @children -end diff --git a/test/support/components/has_temple.ex b/test/support/components/has_temple.ex new file mode 100644 index 0000000..93dced4 --- /dev/null +++ b/test/support/components/has_temple.ex @@ -0,0 +1,12 @@ +defmodule Temple.Components.HasTemple do + @behaviour Temple.Component + + @impl Temple.Component + def render do + quote do + div class: @temple[:class] do + @children + end + end + end +end diff --git a/test/support/components/has_temple.exs b/test/support/components/has_temple.exs deleted file mode 100644 index 41abb12..0000000 --- a/test/support/components/has_temple.exs +++ /dev/null @@ -1,3 +0,0 @@ -div class: @temple[:class] do - @children -end diff --git a/test/support/components/inner.ex b/test/support/components/inner.ex new file mode 100644 index 0000000..9ca399e --- /dev/null +++ b/test/support/components/inner.ex @@ -0,0 +1,12 @@ +defmodule Temple.Components.Inner do + @behaviour Temple.Component + + @impl Temple.Component + def render do + quote do + div id: "inner", outer_id: @outer_id do + @children + end + end + end +end diff --git a/test/support/components/inner.exs b/test/support/components/inner.exs deleted file mode 100644 index 83dfb5d..0000000 --- a/test/support/components/inner.exs +++ /dev/null @@ -1,3 +0,0 @@ -div id: "inner", outer_id: @outer_id do - @children -end diff --git a/test/support/components/outer.ex b/test/support/components/outer.ex new file mode 100644 index 0000000..f7b530c --- /dev/null +++ b/test/support/components/outer.ex @@ -0,0 +1,12 @@ +defmodule Temple.Components.Outer do + @behaviour Temple.Component + + @impl Temple.Component + def render do + quote do + inner outer_id: "from-outer" do + @children + end + end + end +end diff --git a/test/support/components/outer.exs b/test/support/components/outer.exs deleted file mode 100644 index eee0b80..0000000 --- a/test/support/components/outer.exs +++ /dev/null @@ -1,3 +0,0 @@ -inner outer_id: "from-outer" do - @children -end diff --git a/test/support/components/section.ex b/test/support/components/section.ex new file mode 100644 index 0000000..06f8bc2 --- /dev/null +++ b/test/support/components/section.ex @@ -0,0 +1,12 @@ +defmodule Temple.Components.Section do + @behaviour Temple.Component + + @impl Temple.Component + def render do + quote do + section class: "foo!" do + @children + end + end + end +end diff --git a/test/support/components/section.exs b/test/support/components/section.exs deleted file mode 100644 index bbd92d0..0000000 --- a/test/support/components/section.exs +++ /dev/null @@ -1,3 +0,0 @@ -section class: "foo!" do - @children -end