Ecto for Rails developers — The basics

We at Rendered Text are huge fans of Ruby on Rails. As a proof, SemaphoreCI — our biggest product — is still mostly written in Ruby. Lately, however, we started migrating toward Elixir and the OTP platform. Erlang has an excellent platform for developing giant, scalable and fault tolerant systems. Elixir, on the other hand, is a Ruby-like language that helped us to swiftly transition parts of our infrastructure, and harness the power of OTP by using a familiar syntax.

We noticed two big differences right from the start.

Elixir is a mostly pure functional programming language. Ecto — the de facto way to talk to databases in Elixir — has a vastly different approach than ActiveRecord.

The first issue was easy to overcome. We are huge fans of functional programming. Several of us has a working knowledge of Lisp or Haskell, and we even aim to keep our Ruby code immutable and functional. The differences in the database abstraction was a bit harder for us. We were used to Active Record quite a lot. This article is dedicated to this issue, and it is my best attempt to help you transition easier.

Ecto is NOT an ORM

The first big difference you will notice is that Ecto doesn’t convert your database rows into objects. Instead, it uses a database wrapper mechanism, called Repository, and pure Elixir data structures to return your data. Let’s see an example. In Rails, to fetch all the users who are older than 18 would probably look like the following:

users = User . where ( "age > ?" , 18 ) # users are instances of the User class user = users . first p user # => #<User id: 1, age: 20> p user . age # => 20 # we can update the object and save it back into the database user . age = 25 user . save!

In Elixir, you don’t have objects, only pure data. All the communication must be passed through the Repo .

users = User |> where ([ u ], u . age > 18 ) |> Repo . all # users are hashes. `hd` is short for head of the list user = hd ( users ) IO . inspect user # => %User{age: 20, id: 1} IO . puts user . age # => 20 # first we create a changeset changeset = User . changeset ( user , % { age: 25 }) # then we send the changes into the repository changeset |> Repo . update

As you can see, even though Ecto is not an ORM, it does resemble it.

The separation of the wrapper — the Repository — from the query interface was probably the most interesting thing when I was switching to Elixir.

Let’s start our Ecto journey with some basics. The rest of the article will teach you how to invoke CRUD operations on your repository.

Creating new entries

Creating new rows in the database is achieved by constructing a structure and sending it to the repository.

{ :ok , user } = % User { name: " Igor" , age: 25 } |> Repo . insert

Like in Rails, we have two formats for the insert function. The regular version shown in the previous example that returns either :ok or :error , and a bang version Repo.insert! that raises an exception.

user = % User { name: " Igor" , age: 25 } |> Repo . insert!

Reading values from the database

To verify that our insert action succeeded, we will try to fetch it by using its id :

# id of the record created in the previous example id = user . id igor = User |> Repo . get ( id )

The get actions can return either nil if the record is not present in the database, or a structure representing your record.

if igor |> is_nil do IO . puts " User with id: #{ id } not found" else IO . puts " User with id: #{ id } is present" end

Updating rows in the database

Unlike in Rails where we would modify the returned object, in Elixir, we will use changesets . A changeset represents some changes that we want to send to our repository. For example, let’s make our user a bit older than he is:

# first we record the changes changeset = user |> User . changeset ( % { age: 30 , name: " shiroyasha" }) # then we send the changes into the repository changeset |> Repo . update

When you get comfortable with Elixir you will most likely write the above in only one line:

user |> User . changeset ( % { age: 30 , name: " shiroyasha" }) |> Repo . update

The changesets are also pipable, so it is easy to make two changes separately.

user |> User . changeset ( % { age: 30 }) |> User . changeset ( % { name: " shiroyasha" }) |> Repo . update

Deleting records from the database

Finally, let’s delete the user from our database:

{ :ok , _ } = user |> Repo . delete

Final words

I hope that this article helped you to get started with Ecto. Here are some useful resources to continue your learning process:

Happy hacking in Elixir!