Coin-toss.org / Simple Phoenix application - Part 5
Summary
The purpose of this post is to document the creation process of the Coin-toss.org website using the Phoenix Web application framework and the Elixir programming languages. Part 4 steps include:
- Fixing up the templates
- Deploying the application to Heroku
Technology used:
- Elixir 1.2
- Phoenix 1.1.1
- Postgres 9.4
Fixing up the templates
This section is less related to programming and more related to adding the right
HTML and CSS to make the application look passable. They only truly unique
feature in this code is the form_for
function that is used to call a form for
a changeset
that will automatically show errors. With that in mind let us get
started by removing any extraneous HTML and turning our
web\templates\layout\app.html.slim
from what it currently is to this bare bones
version:
doctype html
html lang="en"
head
meta charset="utf-8"
meta content="IE=edge" http-equiv="X-UA-Compatible"
meta content="width=device-width, initial-scale=1" name="viewport"
meta content="" name="description"
meta content="" name="author"
title Coin Toss
link rel="stylesheet" href="#{static_path(@conn, "/css/app.css")}"
body
.container
main role="main"
= render @view_module, @view_template, assigns
script src="#{static_path(@conn, "/js/app.js")}"
This will give us a blank canvas to work from. If we boot up the app with
mix phoenix.server
and visit localhost:4000
we can see that we have blank page
being rendered from TossController.new/1
as that is our entry function as defined
by the router.ex
file. To make the application functional all we need to do now
is add a form_for
tag to web\templates\toss\new.html.slim
with the correct
fields and a button:
= form_for @changeset, toss_path(@conn, :create), fn f ->
= if @changeset.action do
.alert.alert-danger
p Oops, something went wrong! Please check the errors below.
.form-group
= label f, :heads, class: "control-label"
= text_input f, :heads, class: "form-control", required: "required"
= error_tag f, :heads
.form-group
= label f, :tails, class: "control-label"
= text_input f, :tails, class: "form-control", required: "required"
= error_tag f, :tails
.form-group
= submit "Submit", class: "btn btn-primary"
If you remember, there is a case Repo.insert
clause in our create/2
function
which will automatically return a changeset
if there is an error saving the model.
This gets set back out to the new.html.slim
template and the error is rendered inside
the form using the error_tag
function.
Next we can add our outcome to the show.html.slim
template:
h2= if @toss.result == 0, do: "Heads!", else: "Tails!"
p or
h2= if @toss.result == 0, do: @toss.heads, else: @toss.tails
p
= link "Share", to: toss_path(@conn, :show, @toss)
| |
= link "Start again", to: toss_path(@conn, :new)
hr
= if (length(@similar) > 1) do
h4 Similiar tosses
table.table
thead
tr
th Heads
th Tails
th Outcome
th Time
= for toss <- @similar do
tr
td= toss.heads
td= toss.tails
td= if toss.result == 0, do: toss.heads, else: toss.tails
td= toss.inserted_at
In this example we are using a number of techniques: The inline if
statements
will return the correct result text based on the result
attribute. This could
also be moved to a separate function in the toss_view.ex
file. We also use
two link
helper to create URLs to share this particular result and to start a new
toss. Lastly we check if there are more than 1 results in our @similar
list,
and render them individually using the for
function.
We could spend a bit more time on the UI, but this about as functional as it needs to be at this point.
Of course we should check if we broke anything mix test
:
$ mix test
................................
Finished in 0.3 seconds (0.2s on load, 0.1s on tests)
32 tests, 0 failures
Deploying the application to Heroku
Now that the application is finished all we need to do is host it. My preferred hosting site at this point is Heroku. I have already created my application in the Heroku UI and now I have to add various build packs as specified in the guide: http://www.phoenixframework.org/docs/heroku
$ heroku git:remote -a coin-toss
set git remote heroku to https://git.heroku.com/coin-toss.git
$ heroku buildpacks:add https://github.com/HashNuke/heroku-buildpack-elixir.git
Buildpack added. Next release on coin-toss will use https://github.com/HashNuke/heroku-buildpack-elixir.git.
Run git push heroku master to create a new release using this buildpack.
$ heroku buildpacks:add https://github.com/gjaldon/heroku-buildpack-phoenix-static.git
Buildpack added. Next release on coin-toss will use:
1. https://github.com/HashNuke/heroku-buildpack-elixir.git
2. https://github.com/gjaldon/heroku-buildpack-phoenix-static.git
Run git push heroku master to create a new release using these buildpacks.
Next we need to make a change to config/prod.exs
to accomodate the way
Heroku passes the database credentials to the application:
use Mix.Config
config :coin_toss, CoinToss.Endpoint,
http: [port: {:system, "PORT"}],
url: [scheme: "http", host: "coin-toss.org", port: 80],
cache_static_manifest: "priv/static/manifest.json",
secret_key_base: System.get_env("SECRET_KEY_BASE")
config :logger, level: :info
# Configure your database
config :coin_toss, CoinToss.Repo,
adapter: Ecto.Adapters.Postgres,
url: System.get_env("DATABASE_URL"),
pool_size: 20
We also need to create a new secret key base (don’t worry this is not the key base I used):
$ mix phoenix.gen.secret
xvafzY4y01jYuzLm3ecJqo008dVnU3CN4f+MamNd1Zue4pXvfvUjbiXT8akaIF53
$ heroku config:set SECRET_KEY_BASE="xvafzY4y01jYuzLm3ecJqo008dVnU3CN4f+MamNd1Zue4pXvfvUjbiXT8akaIF53"
Setting config vars and restarting mysterious-meadow-6277... done, v3
SECRET_KEY_BASE: xvafzY4y01jYuzLm3ecJqo008dVnU3CN4f+MamNd1Zue4pXvfvUjbiXT8akaIF53
At last all we need to do is commit the changes and $ git push heroku master
to deploy the application. Last of all we need to run the migrations:
$ heroku run mix ecto.migrate
Conclusion
This wraps up the simple Coin-toss.org application. We went from conception to deployment in what I hope was not a too painful process. It should illustrate how easy it is to write applications in Elixi/Phoenix and what a joy it can be at the same time.