Sheharyar Naseer

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.