EctoRut - Simple Ecto Shortcuts to make your life easier
A few months back, I published an Elixir package called Ecto.Rut
.
Ecto.Rut lets you call Ecto.Repo
methods directly on the Model itself instead
of passing your Model as an argument to each Repo
method call.
While the project’s Readme and Documentation are self-sufficient in getting you started, I wanted to write about my motivation for creating the package in the first place.
Quick Introduction
Ecto.Rut is simply a wrapper around Ecto.Repo
, so it can let you do awesome
things like this:
Post.all
# instead of YourApp.Repo.all(Post)
Post.get(2)
# instead of YourApp.Repo.get(Post, 2)
Post.delete(5)
# instead of YourApp.Repo.delete(Post, 5)
Post.insert(title: "Awesome Post", slug: "awesome-post", category_id: 3)
# instead of:
# changeset = Post.changeset(%Post{}, %{title: "Awesome Post", slug: "awesome-post", category_id: 3})
# YourApp.Repo.insert(changeset)
# Well, you get the idea
Motivation
As I improved my Elixir skills, I needed to be better familiar with Macros
and
Metaprogramming in general (see my talk on Introduction to Elixir),
and it all started with random experiments in Elixir Macros. At the same time,
working on my Phoenix applications, I grew tired of calling Repo
methods for
even the simplest database queries on my models and missed the Rails-y way of
calling Model.find
, Model.update
, etc. I saw that (some) other developers
on the Elixir Forum felt the same way too.
I took this as an opportunity to publish my first Hex Package, while refining
my Elixir skills at the same time. The goal of the project here is to not to
fully replace Ecto.Repo
calls in your app, but to reduce code repetition
and simplify and speed up development.
For complex queries, it’s highly recommended that you use the original Ecto.Repo
calls and not this package.
Implementation
Ecto.Rut is implemented as a simple behaviour
using Macros that can be activated
by calling the use
construct. The basic structure of the code looks something
like this:
defmodule Ecto.Rut do
defmacro __using__(opts \\ []) do
quote bind_quoted: [opts: opts] do
def all, do: call(:all, [@model])
def get(id), do: call(:get, [@model, id])
def delete(struct), do: call(:delete, [struct])
defp call(method, args \\ []) do
apply(@repo, method, args)
end
end
end
end
The gist above is just an example, not the full code. But it does convey how powerful Macros in Elixir truly are. Abstraction over abstraction leads to beautifully simple code without comprimising any of the performance and giving rise to creative solutions at the same time.
I don’t have much else to say about the topic, but the beauty and flexibility of the Elixir language keeps surprising me every now and then.