Current section
Files
Jump to
Current section
Files
lib/crudry_context.ex
defmodule Crudry.Context do
@moduledoc """
Generates CRUD functions to DRY Phoenix Contexts.
Assumes `Ecto.Repo` is being used as the repository.
## Usage
To generate CRUD functions for a given schema, simply do
defmodule MyApp.MyContext do
alias MyApp.Repo
alias MyApp.MySchema
require Crudry.Context
Crudry.Context.generate_functions MySchema
end
And the context will become
defmodule MyApp.MyContext do
alias MyApp.Repo
alias MyApp.MySchema
require Crudry.Context
def get_my_schema(id) do
Repo.get(MySchema, id)
end
def get_my_schema_with_assocs(id, assocs) do
Repo.get(MySchema, id)
|> Repo.preload(assocs)
end
def get_my_schema!(id) do
Repo.get!(MySchema, id)
end
def list_my_schemas() do
Repo.all(MySchema)
end
def list_my_schemas(opts) do
Crudry.Query.list(MySchema, opts)
|> Repo.all()
end
def count_my_schemas(field \\\\ :id) do
Repo.aggregate(MySchema, :count, field)
end
def search_my_schemas(search_term) do
module_fields = MySchema.__schema__(:fields)
Crudry.Query.search(MySchema, search_term, module_fields)
|> Repo.all()
end
def filter_my_schemas(filters) do
Crudry.Query.filter(MySchema, filters)
end
def create_my_schema(attrs) do
%MySchema{}
|> MySchema.changeset(attrs)
|> Repo.insert()
end
def update_my_schema(%MySchema{} = my_schema, attrs) do
my_schema
|> MySchema.changeset(attrs)
|> Repo.update()
end
def update_my_schema(id, attrs) do
id
|> get_my_schema()
|> update_my_schema(attrs)
end
def update_my_schema_with_assocs(%MySchema{} = my_schema, attrs, assocs) do
my_schema
|> Repo.preload(assocs)
|> MySchema.changeset(attrs)
|> Repo.update()
end
def update_my_schema_with_assocs(id, attrs, assocs) do
id
|> get_my_schema()
|> update_my_schema_with_assocs(attrs, assocs)
end
def delete_my_schema(%MySchema{} = my_schema) do
my_schema
|> Repo.delete()
end
def delete_my_schema(id) do
id
|> get_my_schema()
|> delete_my_schema()
end
end
"""
@doc """
Sets default options for the context.
## Options
* `:create` - the name of the changeset function used in the `create` function.
Default to `:changeset`.
* `:update` - the name of the changeset function used in the `update` function.
Default to `:changeset`.
* `:only` - list of functions to be generated. If not empty, functions not
specified in this list are not generated. Default to `[]`.
* `:except` - list of functions to not be generated. If not empty, only functions not specified
in this list will be generated. Default to `[]`.
The accepted values for `:only` and `:except` are: `[:get, :get!, :list, :search, :filter, :count, :create, :update, :delete]`.
## Examples
iex> Crudry.Context.default create: :create_changeset, update: :update_changeset
:ok
iex> Crudry.Context.default only: [:create, :list]
:ok
iex> Crudry.Context.default except: [:get!, :list, :delete]
:ok
"""
defmacro default(opts) do
Module.put_attribute(__CALLER__.module, :create_changeset, opts[:create])
Module.put_attribute(__CALLER__.module, :update_changeset, opts[:update])
Module.put_attribute(__CALLER__.module, :only, opts[:only])
Module.put_attribute(__CALLER__.module, :except, opts[:except])
end
@doc """
Generates CRUD functions for the `schema_module`.
Custom options can be given. To see the available options, refer to the documenation of `Crudry.Context.default/1`.
## Examples
Suppose we want to implement basic CRUD functionality for a User schema,
exposed through an Accounts context:
defmodule MyApp.Accounts do
alias MyApp.Repo
require Crudry.Context
# Assuming Accounts.User implements a `changeset/2` function, used both to create and update a user.
Crudry.Context.generate_functions Accounts.User
end
Now, all this functionality is available:
Accounts.get_user(id)
Accounts.get_user_with_assocs(id, assocs)
Accounts.get_user!(id)
Accounts.list_users()
Accounts.list_users(opts)
Accounts.count_users(field \\\\ :id)
Accounts.search_users(search_term)
Accounts.filter_users(filters)
Accounts.create_user(attrs)
Accounts.update_user(%User{}, attrs)
Accounts.update_user(id, attrs)
Accounts.update_user_with_assocs(%User{}, attrs, assocs)
Accounts.update_user_with_assocs(id, attrs, assocs)
Accounts.delete_user(%User{})
Accounts.delete_user(id)
"""
defmacro generate_functions(schema_module, opts \\ []) do
opts = Keyword.merge(load_default(__CALLER__.module), opts)
name = Helper.get_underscored_name(schema_module)
for func <- [:get, :get!, :list, :search, :filter, :count, :create, :update, :delete] do
if Helper.define_function?(func, opts[:only], opts[:except]) do
ContextFunctionsGenerator.generate_function(func, name, schema_module, opts[:create], opts[:update])
end
end
end
# Load user-defined defaults or fall back to the library's default.
defp load_default(module) do
create_changeset = Module.get_attribute(module, :create_changeset)
update_changeset = Module.get_attribute(module, :update_changeset)
only = Module.get_attribute(module, :only)
except = Module.get_attribute(module, :except)
[
create: create_changeset || :changeset,
update: update_changeset || :changeset,
only: only || [],
except: except || []
]
end
end