Lesson 3

Dealing with Ecto Repos for our Database

Ecto is the mechanism we're going to use to connect to our postgres database, which we created in Lesson 1.

Ecto takes an approach that's different from ActiveRecord to how you'll deal with the database, but it does support database migrations.

To start with we'll need to configure postgrex and ecto as both applications and dependencies.

When you want to include a new project as a dependency in your application, you add the its to the deps section in mix.exs. Add the following two lines into the file:

defmodule Splurty.Mixfile do
  use Mix.Project

  def project do
    [app: :splurty,
     version: "0.0.1",
     elixir: "~> 1.0",
     elixirc_paths: ["lib", "web"],
     compilers: [:phoenix] ++ Mix.compilers,
     deps: deps]
  end

  # Configuration for the OTP application
  #
  # Type `mix help compile.app` for more information
  def application do
    [mod: {Splurty, []},
     applications: [:phoenix, :cowboy, :logger]]
  end

  # Specifies your project dependencies
  #
  # Type `mix help deps` for examples and options
  defp deps do
    [
     {:phoenix, "~> 0.8.0"},
{:cowboy, "~> 1.0"}, {:postgrex, ">= 0.0.0"}, {:ecto, "~> 0.2.0"}
] end end

Save the file.

Then run the command to fetch and install the dependencies:

mix do deps.get, compile

It should complete without giving you error messages. That command will download, install and compile all the dependencies in our application.

Elixir also has a concept of applications. An application is run in a separate process, which we will communicate with.

We will need to add both the postgrex and ecto as applications in our project.

defmodule Splurty.Mixfile do
  use Mix.Project

  def project do
    [app: :splurty,
     version: "0.0.1",
     elixir: "~> 1.0",
     elixirc_paths: ["lib", "web"],
     compilers: [:phoenix] ++ Mix.compilers,
     deps: deps]
  end

  # Configuration for the OTP application
  #
  # Type `mix help compile.app` for more information
  def application do
    [mod: {Splurty, []},
applications: [:phoenix, :cowboy, :logger, :postgrex, :ecto]]
end # Specifies your project dependencies # # Type `mix help deps` for examples and options defp deps do [ {:phoenix, "~> 0.8.0"}, {:cowboy, "~> 1.0"}, {:postgrex, ">= 0.0.0"}, {:ecto, "~> 0.2.0"} ] end end

Zooming Out: How Ecto Is Organized

For an in-depth analysis of how ecto works, check this talk by Jose Valim, or the official documentation here.

The key to understanding Ecto is understanding the three components it is broken into:

Repo

Short for Repository, a repo is the connection to a database. Ecto makes it easy to connect to multiple different databases in a single application.

Model

Models make it easy to save items to our database and provide callbacks in the lifecycle of the object.

Queries

Unlike ActiveRecord, Ecto exposes queries in a direct manner. The querying system is influenced by dot-net's LINQ. Since Elixir's syntax supports macros, the library actually doesn't need any "Language Integration", in the same vein as LINQ, to achieve the same "Language-Integrated-ness".

One critical difference between LINQ and Ecto is that Ecto exclusively deals with databases. In LINQ the abstraction is moved to any type of collection (such as arrays, hashes, etc). Ecto makes the claim that the relational algebra it supports should be available to databases, which it maps to intuitively, but should not provide the functionality for regular collections, since the data types are not optimized for those types of queries.

Building the Repo

Add a file in the lib/splurty folder called repo.ex. This will define the repository we'll be using in the context of this web application. Make the file look like this:

defmodule Repo do
  use Ecto.Repo, adapter: Ecto.Adapters.Postgres

  def conf do
    parse_url Application.get_env(:phoenix, :database)[:url]
  end

  def priv do
    app_dir(:splurty, "priv/repo")
  end
end

Save the file

This sets up the repo.

The priv method defines what folder should private ecto stuff go (for example ecto database migration files).

The conf method describes how we should connect to the database. It's expecting a database connection string, for example something like: ecto://postgres-user:postgres-password@localhost/database_name.

In this method we're saying we should pull the database connection string information out of the application's configuration (Mix.Config). We're looking ahead, and by putting this in the configuration it will be possible to deploy to heroku, or have production's database have a different username/password (or even be on a different physical machine).

Setup the configuration for our database by adding the following line to the bottom of config/dev.exs.

use Mix.Config

config :splurty, Splurty.Endpoint,
  http: [port: System.get_env("PORT") || 4000],
  debug_errors: true,
  cache_static_lookup: false

# Enables code reloading for development
config :phoenix, :code_reloader, true

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"

config :phoenix, :database, url: "ecto://ecto:password@localhost/splurty_development"

Save the file.

This sets up most of Ecto's repo. The core repo code will run in external erlang process however, so we'll to configure the repo's process to run.

When dealing with multiple processes, both Erlang and Elixir use the concept of Supervisors, and this ensures our application is Fault Tolerant. In short, we will think about strategies to keep our processes running smoothly, and how to deal with exceptional cases in advance of problems arising.

The core thought in the Erlang community is that Exceptional Cases Should Crash the Process, and then supervisors should restart a new process that isn't messed up.

The two most common types of supervisors are one_for_one, which indicates when a process crashes a new process should be spun up to replace it. The stratgey one_for_all will restart all children whenever any process dies.

For more information about Elixir's supervision tree check the Elixir-Lang Supervisor and Application documentation.

In this application, we will build a supervisor that uses the one_for_one strategy, and starts our web application's endpoints as well as the Ecto repo.

Add a file called in the lib/splurty folder called supervisor.ex that looks like this:

defmodule Splurty.Supervisor do
  use Supervisor
  
  def start_link do
    :supervisor.start_link(__MODULE__, [])
  end
  
  def init([]) do
    # Adding repo to be sent into supervise
    tree = [
      worker(Repo, []),
      worker(Splurty.Endpoint, []),
    ]
    supervise(tree, strategy: :one_for_one)
  end
end

This sets up a pretty standard supervisor for our application.

The start_link method you may notice starts the line with :supervisor. The code :supervisor indicates we are calling the method on the Erlang's supervisor module. Elixir allows us to call Erlang code directly quite easily.

The variable tree is a list of all the child processes we want this supervisor to monitor with the strategy.

Now, we need to tell our application to use this supervisor.

Edit the lib/splurty.ex file to point to this supervisor.

defmodule Splurty do
  use Application

  # See http://elixir-lang.org/docs/stable/elixir/Application.html
  # for more information on OTP Applications
def start(_type, _args) do import Supervisor.Spec, warn: false Splurty.Supervisor.start_link end
# Tell Phoenix to update the endpoint configuration # whenever the application is updated. def config_change(changed, _new, removed) do Splurty.Endpoint.config_change(changed, removed) :ok end end

Save the file.

This changes our application to use the Supervisor we just setup.

Create a Database Migration

Just like ActiveRecord, changes to the database schema are managed through migrations.

Generate a new migration by running the mix command:

$ mix ecto.gen.migration Repo create_quotes

This should output text similar to the following when indicating files it generated:

* creating priv/repo/migrations
* creating priv/repo/migrations/20150124205648_create_quotes.exs

Open the priv/repo/XXXXX_create_quotes.ex file.

You'll notice there are two empty methods in the module: a method for migrating up, and a method for rolling back. Each is expected to return a string of SQL code that will run against the database.

At this point, Ecto doesn't have a nice DSL style method to create and modify our database structure, so we'll need to enter SQL commands to alter the structure.

Edit the priv/repo/XXXXX_create_quotes.ex file to create a database table for quotes that has a saying and an author.

defmodule Repo.Migrations.CreateQuotes do
  use Ecto.Migration

def up do "CREATE TABLE quotes(id serial primary key, saying varchar(140), author varchar(140));" end def down do "DROP TABLE quotes;" end
end

Save the file.

Execute the database migration by running the following command:

mix ecto.migrate Repo

It will output text on the console that looks like this:

[debug] CREATE TABLE IF NOT EXISTS schema_migrations (id serial primary key, version bigint) (404670µs)
[debug] SELECT version FROM schema_migrations (13173µs)
* running UP _build/dev/lib/splurty/priv/repo/migrations/20150124205648_create_quotes.exs
[debug] CREATE TABLE IF NOT EXISTS schema_migrations (id serial primary key, version bigint) (12836µs)
[debug] SELECT version FROM schema_migrations WHERE version = 20150124205648 (7710µs)
[debug] CREATE TABLE quotes(id serial primary key, saying varchar(140), author varchar(140)); (5369µs)
[debug] INSERT INTO schema_migrations(version) VALUES (20150124205648) (2352µs)

Awesome! This means that our database is setup!

Setting up the Model

Now that we have our database table setup, we can build a model to interact with the database table. Add a file in web/models directory called quote.ex that contains the following code:

defmodule Splurty.Quote do
  use Ecto.Model

  schema "quotes" do
    field :saying, :string
    field :author, :string
  end
end

Save the file.

Notice how we have to explicitly tell the model explicitly what the database schema is for the model that it's mapping to.

Storing a quote in our database

Let's add a quote in our database by using elixir's interactive console.

Run the following command to get into interactive elixir console, pulling in our mix project's code:

$ iex -S mix

When it boots, build a quote specifying the key/value pairs for the quote and store it in a variable.

> quote = %Splurty.Quote{saying: "Haters gonna hate.  Ain'ters gonna ain't.", author: "Dave Skylark"}

W can extract the message from the quote in the map by access the saying field from the quote variable now, like this:

> quote.saying 

You should see output that looks like this:

"Haters gunna hate.  Ain'ter gunna a'int."

Insert the quote in the repo, or in other-words save it to the database, by running the following command:

> Repo.insert(quote)

When you run the command you should see something like the following output to the console:

[debug] INSERT INTO "quotes" ("author", "saying") VALUES ($1, $2) RETURNING "id" (347555µs)
%Splurty.Quote{author: "Dave Skylark", id: 1,
 saying: "Haters gonna hate.  Ain'ters gonna ain't."}

This indicates the item was saved to the database and now has an id of 1. Awesome!

Load the item from the database by running the following command.

quote2 = Repo.get(Splurty.Quote, 1)

Ecto allows us to update records in the database quite easily too.

In order to update the author to be James Franco instead of Dave Skylark for the quote we have in our database, run the following two commands:

> quote3 = %{quote2 | author: "James Franco"}
> Repo.update(quote3)

Delete the item from our database by running this command:

> Repo.delete(quote3)

Sweet! We know our database connection is working properly! We also have a pretty good idea of how we will be able to implement most of the CRUD functionality with Ecto.

Press CTRL+C twice to exit iex, and let's move on to the next lesson.

Next Lesson