Igor Šarčević wrote this on April 7, 2016

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.

  1. Elixir is a mostly pure functional programming language.

  2. 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!

comments powered by Disqus

About Igor Šarčević

Igor is a programming enthusiast with a passion for mathematics and intelligent systems. He gets lots of joy while solving difficult problems with a handful of dedicated people. He likes to be helpful to others and tries to improve himself on a daily basis. When he is not coding you can probably find him running, reading some Japanese poetry or improving his ninja skills.

Suggested Reads

Rails Testing Handbook

A new ebook on building test-driven Rails apps with RSpec and Cucumber.

At Rendered Text, we have a long history with Ruby on Rails. Checking the blog archive reminds me that we published first posts about working with Rails way back in 2009.

———

Rendered Text is a software company. For questions regarding Semaphore, please visit semaphoreci.com. Otherwise, feel free to get in touch any time by sending us an email.