Josh Thompson     about     archive     tags

First pass with Elixir/Phoenix

I’m digging into Elixir and Phoenix. I’m working through this tutorial to cloning Slack.

The tutorial author says

At the time of writing, I have ~1 week experience with Phoenix. Similar to Rubber Ducky Debugging, I am writing this blog post to force myself to think differently about the problems I am solving and therefore gain a better understanding of the language and framework.

Couldn’t agree more myself. This post serves as my Rubber Ducky Debugging process for getting started with Elixir/Phoenix.

Similar to the author, I come from a Ruby/Rails background, but undoubtedly less experienced than the author. So, this article will capture tidbits and pieces useful to me as I work through the tutorial.

I’m intentional in how I learn, as well as in how I ask questions, so this is just me, doing what I do, to learn things.

Onward!

Installing Elixir and Phoenix

Installation is super easy.

The recommended tutorials + some clicking around was all I needed. Brew handled most of it, and elixir-lang’s installation guide pairs well with the same for phoenix

I just cd‘ed into workspace/elixir (where my otherwise homeless bits of code lives) and test-drove Phoenix’s equivalent of rails new. I’m taking a quick detour through this guide at the moment.

OK, it was more than a quick detour. The rest of this post is me familiarizing myself with the above guide. Read Elixir/Phoenix part deux for more

$ mix phx.new hellow gives us a bunch of output, and eventually a new project.

(I tend to intentionally misspell variables and such when following tutorials - helps me detangle the language/framework’s nomenclature from my own, and tunes me in a bit closer to ways to make errors. So, this project is named “hellow”, and any further code snippets will reflect that. Be wary, this strategy has led me into more than a little debugging as I walk through tutorials. But I come out stronger on the other end…)

The directory structure looks familiar:

.
├── README.md
├── _build
├── assets
├── config
├── deps
├── lib
├── mix.exs
├── mix.lock
├── priv
└── test

deps is foreign to me, though. Inside of that directory is a bunch of other directories, all looking greek to me.

Poking around a bit, I’m seeing files with .ex and .exs extensions. StackOverflow says .ex is for “compiled” Elixir code, while .exs is “scripting” Elixir code. Elixir is my first brush with a functional programming language, which is also my first exposure to compiled languages, so I’m putting a flag in this as Significant.

Ah, and .eex is “embedded Elixir”, or in my Rails-centric background “elixir’s version of embedded ruby/.erb”. Good, because no one wants to write raw HTML.

If Elixir doesn’t have some version of pry, I’m going to be sad.

Per the instructions, $ mix ecto.create, a few missing dependencies (say Y), and

=> The database for Hellow.Repo has been created

Wahoo.

$ mix phx.server seems to be rails server. So handy having a mental framework to hang new info on.

A quick check of localhost:4000 and we’re in business:

terminal goodies + localhost

On to part 2 of the intro guide:

The writeup mentions that most of our work will live in the /lib directory

Lots of interesting stuff that seems quite familiar after working with a MVC model, but we’ll dig into it all later.

Routes

Ah, routes. Makes me nostalgic for config/routes.rb

Elixir’s equivalent lives in hellow/lib/hellow_web/router.ex, which we know is a compiled file (I think?).

By default, it looks pretty manageable:

defmodule HellowWeb.Router do
  use HellowWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", HellowWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end

end

As soon as seeing the reference to PageController, I had to see if there was an associated test.

Ah, software. It even reads just like RSpec. :)

# test/hellow_web/controllers/page_controller_test.exs

defmodule HellowWeb.PageControllerTest do
  use HellowWeb.ConnCase

  test "GET /", %{conn: conn} do
    conn = get conn, "/"
    assert html_response(conn, 200) =~ "Welcome to Phoenix!"
  end
end

Testing

We interrupt current programming to figure out how to run tests in Elixir

Looks like Mix is an Elixir build tool. We’ll be using it a lot.

ExUnit seems to be Elixir’s test-unit framework. AKA RSpec/Minitest. (source)

And tests

Looks like $ mix test does it.

Holy cow… There’s a ton of deprecation warnings and the test took ages. (Er, it passed by the way. )

Looks like this is a new and fixed issue, so the recommended fix is to use a more recent version of poison.

Jumping into mix.exs (aka Rail’s Gemfile), adding the newest version of poison:

defp deps do
    [
      {:poison, "~> 3.1"},
      {:phoenix, "~> 1.3.0"},
      .
      ]
end

and updating dependencies:

$ mix deps.get

and lets run the tests again:

$ mix test

Ah, lovely. No more dependency problems.

Two other notable events, though - I think the first time I ran the tests, Mix had a ton of compiling to do.

This is the output from the first test run:

# all of the many deprecation warnings removed
==> connection
Compiling 1 file (.ex)
Generated connection app
==> gettext
Compiling 1 file (.erl)
Compiling 20 files (.ex)
Generated gettext app
===> Compiling ranch
===> Compiling poolboy
==> decimal
Compiling 1 file (.ex)
Generated decimal app
==> poison
Generated poison app
==> db_connection
Compiling 23 files (.ex)
Generated db_connection app
==> phoenix_pubsub
Compiling 12 files (.ex)
Generated phoenix_pubsub app
===> Compiling cowlib
===> Compiling cowboy
==> mime
Compiling 1 file (.ex)
Generated mime app
==> plug
Compiling 1 file (.erl)
Compiling 44 files (.ex)
Generated plug app
==> phoenix_html
Compiling 8 files (.ex)
Generated phoenix_html app
==> phoenix
Compiling 74 files (.ex)
Generated phoenix app
==> postgrex
Compiling 62 files (.ex)
Generated postgrex app
==> ecto
Compiling 70 files (.ex)
Generated ecto app
==> phoenix_ecto
Compiling 6 files (.ex)
Generated phoenix_ecto app
==> hellow
Compiling 16 files (.ex)
Generated hellow app
....

Finished in 0.1 seconds
4 tests, 0 failures

Randomized with seed 769269

The second time I ran it, not only were there no deprecation warnings, there was basically no set-up:

mix test
Compiling 16 files (.ex)
Generated hellow app
....

Finished in 0.06 seconds
4 tests, 0 failures

Randomized with seed 608206

OK, again, I feel like all the setup steps are Significant, but don’t yet know why.

Onward!

Back to Routes/Routing

Lets add:

scope "/", HellowWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    get "/hello", HellowController, :index # Adding this bad boy
  end

When visiting localhost:4000/hello, we get a very Rails-esque error code, saying “I cannot find the controller you’re asking for”. Which is great, because HellowController doesn’t exist.

A new Controller

Jumping in to make a new controller file moves us along in error-driven development (EDD):

# lib/hellow_web/controllers/hellow_controller.ex
defmodule HellowWeb.HellowController do
  use HellowWeb, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end
end

And we get:

UndefinedFunctionError at GET /hello
function HellowWeb.HellowView.render/2 is undefined (module HellowWeb.HellowView is not available)

Perfect. We are (I suspect) calling a non-existent file to render stuff to the browser. Phoenix complains.

A new View

I put together a quick view, templated off an existing view, and for the first time my MVC background failed me. I assumed views would be for presentation, but it seems like Views in Phoenix are more analagous to Models in Rails.

There’s a /templates directory that contains presentation-related data, so I’ll just run with “view == models” for now.

Phoenix views have several important jobs. They render templates. They also act as a presentation layer for raw data from the controller, preparing it for use in a template. Functions which perform this transformation should go in a view. (hexdocks)

Templates

Ah, interesting. As soon as I created lib/hellow_web/templates/hellow/index.html.eex, the errors in Mix went away, and I’m now loading an empty page.

(Even though the file is empty)

So, Phenix has live code-reloading (when I made the file, I saw the logs from mix phx.server show compiling and rendering data. The red went away.)

Also, the view is obviously embedded inside of an application layout, because the header/footer/things-that-make-HTML-work-on-the-internet kicked in and rendered my three lines of HTML.

A quick peek at lib/hellow_web/templates/layout/app.html.eex, and it’s almost indistinguishable from app/views/layouts/application.html.erb in Rails.

A second new page

The universally sound advice for education and training applies here quite well:

Do it again, faster

So, I did a quick trip through adding another page, to do a standard “handle a dynamic URL and present the value from the URL in the browser”. This is like the “hello world” of dynamic routing.


That’s the end of the tiny “getting started” tutorial. I’m ending this post here, and will continue with the slack clone in part 2


Get occasional emails

If I've written any new posts, you'll get an email with summaries on Friday. If I've not, you'll not hear from me.