defmodule Ecto.Rut do @moduledoc """ Provides simple, sane and terse shortcuts for Ecto models. Ecto.Rut is a wrapper around `Ecto.Repo` methods that usually require you to pass the module as the subject and sometimes even require you do extra work before hand, (as in the case of `Repo.insert/3`) to perform operations on your database. Ecto.Rut tries to reduce code repetition by following the "Convention over Configuration" ideology. For example, once set up, it allows you to do this on a model called `Post`: ``` # Create a Post Post.insert(title: "Introduction to Elixir", categories: ["Elixir", "Programming"]) # Get all Posts Post.all # Get a Post with its id Post.get(3) # Get a Post with another attribute Post.get_by(published_date: "2016-02-24") # Delete a Post Post.delete(lame_post) ``` ## Installation Once added to your mix dependencies, all you need to do is call `use Ecto.Rut` in your Ecto models: ``` defmodule YourApp.Post do use Ecto.Schema use Ecto.Rut # Schema, Changeset and other stuff... end ``` ### Phoenix Projects If you're using Ecto with a Phoenix project, instead of calling `use Ecto.Rut` in all of your models, you can just call it once in the `model/0` method of your `web/web.ex` file: ``` # web/web.ex def model do quote do use Ecto.Schema use Ecto.Rut # Other stuff... end end ``` ## Configuration You can also pass options to Ecto.Rut when calling `use` on it. These values are inferred automatically by Ecto.Rut, but you can set them yourself in those special cases where it can't. The two options are: - `:model` You set this when your Ecto Model is different from the module where you are calling `use` - `:repo` You set this when your app's Ecto.Repo module is set differently. ``` defmodule YourApp.OtherNamespace.Post do use Ecto.Schema use Ecto.Rut, model: YourApp.Post, repo: YourApp.CustomEcto.Repo # Other Stuff end ``` """ @doc false defmacro __using__(opts \\ []) do quote bind_quoted: [opts: opts] do @model opts[:model] || __MODULE__ @app opts[:app] || @model |> Module.split |> Enum.drop(-1) |> Module.concat @repo opts[:repo] || @app |> Module.concat("Repo") def all(opts \\ []) do call(:all, [@model, opts]) end def get(id, opts \\ []) do call(:get, [@model, id, opts]) end def get!(id, opts \\ []) do call(:get!, [@model, id, opts]) end def get_by(clauses, opts \\ []) do call(:get_by, [@model, clauses, opts]) end def get_by!(clauses, opts \\ []) do call(:get_by!, [@model, clauses, opts]) end def delete(struct, opts \\ []) do call(:delete, [struct, opts]) end def delete!(struct, opts \\ []) do call(:delete!, [struct, opts]) end def insert(struct, opts \\ []) def insert(struct, opts) when is_map(struct) do call(:insert, [struct, opts]) end def insert(keywords, opts) do @model |> Kernel.struct |> @model.changeset(to_map(keywords)) |> insert(opts) end def insert!(struct, opts \\ []) def insert!(struct, opts) when is_map(struct) do call(:insert!, [struct, opts]) end def insert!(keywords, opts) do @model |> Kernel.struct |> @model.changeset(to_map(keywords)) |> insert!(opts) end # Private Methods defp call(method, args \\ []) do apply(@repo, method, args) end defp to_map(keyword) do Enum.into(keyword, %{}) end end end end