Last active
January 18, 2022 07:26
-
-
Save joshnuss/6de436f82ea1c3c34fbeae238924bedf to your computer and use it in GitHub Desktop.
ActiveRecord pattern with Ecto
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule ActiveRecord do | |
@doc false | |
defmacro __using__(_opts) do | |
quote do | |
use Ecto.Schema | |
import Ecto.{ Changeset, Query } | |
import ActiveRecord | |
Module.register_attribute(__MODULE__, :validations, accumulate: true, persist: true) | |
def changeset(record \\ __struct__(), attrs) do | |
record | |
|> cast(attrs, __schema__(:fields)) | |
|> apply_validations() | |
end | |
def validations do | |
attributes = __MODULE__.__info__(:attributes) | |
rules = Keyword.get_values(attributes, :validations) | |
Enum.flat_map(rules, fn [{keys, value}] -> | |
for k <- List.wrap(keys), do: {k, value} | |
end) | |
end | |
defp apply_validations(changeset) do | |
validations() |> Enum.reduce(changeset, fn {field, opts}, changeset -> | |
presence = Keyword.get(opts, :presence) | |
if presence do | |
validate_required(changeset, field, opts) | |
else | |
# TODO: add more validations options | |
changeset | |
end | |
end) | |
end | |
end | |
end | |
defmacro scope(name, filters) do | |
quote do | |
def unquote(name)(query \\ __MODULE__) do | |
query |> unquote(filters) | |
end | |
end | |
end | |
defmacro validates(attributes, opts) do | |
quote do | |
@validations unquote({attributes, opts}) | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Org.Employee do | |
use ActiveRecord | |
schema "employees" do | |
field :role, :string | |
field :first_name, :string | |
field :last_name, :string | |
field :start_date, :utc_datetime | |
timestamps() | |
end | |
validates [:role, :first_name, :last_name, :start_date], presence: true | |
scope :managers, where(role: "manager") | |
scope :onboarding, where([e], e.start_date > ^DateTime.utc_now()) | |
scope :first, limit(1) | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
alias Org.Employee | |
%{role: "Manager", first_name: "John", last_name: "Smith"} | |
|> Employee.changeset() # changeset logic is generated for you | |
|> Repo.insert! | |
# get all managers | |
Employee.managers |> Repo.all | |
# get all future employees | |
Employee.onboarding |> Repo.all | |
# pipe multiple scopes | |
Employee.managers |> Employee.onboarding |> Repo.all |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment