lib/slime.ex

defmodule Slime do
  @moduledoc """
  Slim-like HTML templates.
  """

  alias Slime.Renderer

  defmodule TemplateSyntaxError do
    @moduledoc """
    Syntax exception which may appear during parsing and compilation processes
    """
    defexception [:line, :line_number, :column, message: "Syntax error", source: "INPUT"]

    def message(exception) do
      column = if exception.column == 0, do: 0, else: exception.column - 1

      """
      #{exception.message}
      #{exception.source}, Line #{exception.line_number}, Column #{exception.column}
      #{exception.line}
      #{String.duplicate(" ", column)}^
      """
    end
  end

  defdelegate render(slime), to: Renderer
  defdelegate render(slime, bindings), to: Renderer

  @doc """
  Generates a function definition from the file contents.
  The kind (`:def` or `:defp`) must be given, the
  function name, its arguments and the compilation options.
  This function is useful in case you have templates but
  you want to precompile inside a module for speed.

  ## Examples

      # sample.slim
      = a + b

      # sample.ex
      defmodule Sample do
        require Slime
        Slime.function_from_file :def, :sample, "sample.slime", [:a, :b]
      end

      # iex
      Sample.sample(1, 2) #=> "3"
  """
  defmacro function_from_file(kind, name, file, args \\ [], opts \\ []) do
    quote bind_quoted: binding() do
      require EEx
      eex = file |> File.read!() |> Renderer.precompile()
      EEx.function_from_string(kind, name, eex, args, opts)
    end
  end

  @doc """
  Generates a function definition from the string.
  The kind (`:def` or `:defp`) must be given, the
  function name, its arguments and the compilation options.

  ## Examples

      iex> defmodule Sample do
      ...>   require Slime
      ...>   Slime.function_from_string :def, :sample, "= a + b", [:a, :b]
      ...> end
      iex> Sample.sample(1, 2)
      "3"
  """
  defmacro function_from_string(kind, name, source, args \\ [], opts \\ []) do
    quote bind_quoted: binding() do
      require EEx
      eex = source |> Renderer.precompile()
      EEx.function_from_string(kind, name, eex, args, opts)
    end
  end
end