Current section
Files
Jump to
Current section
Files
lib/defrag.ex
defmodule Defrag do
@moduledoc """
Provides a convenient macro for creating Ecto fragments with interpolation syntax.
This module offers the `defrag/1` macro that transforms string interpolation syntax
into Ecto fragment queries, making them more readable and maintainable.
"""
@pattern ~r/\#\{([^\}]+)\}/
@doc """
Converts string interpolation syntax into an Ecto fragment.
This macro allows you to write Ecto fragments using familiar string interpolation
syntax (`\#{...}`). It automatically converts the interpolation into proper
Ecto fragment parameters.
## Examples
import Defrag
# Instead of writing:
fragment("date_part('year', ?)", some_date)
# You can write:
defrag("date_part('year', \#{some_date})")
# Multiple interpolations are supported:
defrag("BETWEEN \#{start_date} AND \#{end_date}")
# Becomes: fragment("BETWEEN ? AND ?", start_date, end_date)
## Usage in Ecto Queries
import Ecto.Query
import Defrag
def users_born_in_year(query, year) do
from u in query,
where: defrag("date_part('year', \#{u.birth_date}) = \#{^year}")
end
## Notes
- The macro converts each interpolation (`\#{...}`) into a parameter placeholder (`?`)
- Variables used in interpolation must be in scope where the macro is called
- The resulting fragment is safe against SQL injection as it uses Ecto's parameterization
"""
defmacro defrag(ast) do
string = Macro.to_string(ast)
fragment_string = Regex.replace(@pattern, string, "?")
vars = Enum.map(Regex.scan(@pattern, string), fn [_, var] -> var end)
Code.string_to_quoted!("fragment(#{Enum.join([fragment_string | vars], ", ")})")
end
end