@vanstee
operable.io
Ectoan ORM without objects
Ruby and ActiveRecord
Elixir and Ecto
=
ActiveRecord refresher
# Create an unpersisted record
user = User.new(name: "Caroline Holbrook")
# Modify the user
user.name = "Caroline Van Stee"
# Persist a record
user.save
# Retrieve a record
User.find(1)
# Run a complex query
User.where(admin: true).count
Calling methods on
objects, that have
methods which
return objects…
I love Ruby,
but what if we wanted
to build an ORM
without objects.
Data structures Functions
Models
Queries Attribute Mapping
Validations
Executing Queries
User.where(name: "Patrick")
query = { table: :users,
where: { name: "Patrick" } }
results = execute(connection, query)
users = results.map(&:to_model)
with objects
without objects
Wait, you just turned
1 line into 3.
How could that be
any better?
Queries are more flexible1
Connections can be
swapped out
2
Results are more flexible
and are just simple maps
3
Well, that's
how Ecto
works!
query = from u in User,
where: u.name == "Patrick"
results = Repo.all(query)
Integrated
Query
Language
Repo
Model
Changeset
Query
Repos are just
connections to
the database
user = %User{name: "Patrick"}
WriterRepo.insert!(user)
ReaderRepo.all(User)
Multiple Repos
Models
defmodule App.User do
use Ecto.Model
schema "users" do
field :name
end
end
Fields have types
defmodule JSON do
alias Postgres.TypeInfo
@behaviour Postgrex.Extension
def init(_parameters, opts),
do: Keyword.fetch!(opts, :library)
def matching(_library),
do: [type: "json"]
def format(_library),
do: :binary
def encode(%TypeInfo{type: "json"}, map, _state, library),
do: library.encode!(map)
def decode(%TypeInfo{type: "json"}, json, _state, library),
do: library.decode!(json)
end
Changesets
defmodule User do
use Ecto.Model
def changeset(user, params  :empty) do
user
|> cast(params, ~w(name email), ~w(age))
|> unique_constraint(:email)
end
end
Changesets
params = %{email: "patrick@vanstee.me"}
changeset = User.changeset(%User{}, params)
{:error, changeset} = Repo.insert(changeset)
changeset.errors
#=> [email: "has already been taken"]
Queries
from u in User,
join: p in Post, on: u.id == p.user_id,
where: u.name == "Patrick" or p.views > 500,
select: {u.name, p.title}
Everything else
• Migrations
• Mix tasks (like rake)
• Callbacks
• Validations
• PostgreSQL, MySQL, Mongo
⚡
elixir-lang.org
elixir-lang/ecto

Ecto Introduction by Patrick Vanstee