SlideShare a Scribd company logo
1 of 66
Download to read offline
PHOENIX
FOR RAILS DEVS
Conferencia Rails
Madrid
15/10/2016
If you're having talk problems,
I feel bad for you, son.
I got 61 problems, but a slide
ain't one; hit me!
About me:
Javier Cuevas
@javier_dev
RUBY ON RAILS SHOP
WHO EMBRACED ELIXIR
AIRBNB FOR DOGS
“MAJESTIC” RAILS 3.2 MONOLITH
GUESTS
OF THE DAY
José Valim
Former Rails Core Team member.
He was trying to make Rails really thread
safe but... ended up creating a new
programming language (Elixir). Oops!
PerformanceProductivity
Chris McCord
Author of render_sync a Ruby gem to
have real-time partials in Rails (before
ActionCable).
It got complicated and... he ended up
creating a new web framework
(Phoenix). Oops!
WHAT IS
ELIXIR?
Elixir is a dynamic, functional language
designed for building scalable and
maintainable applications.
Elixir leverages the Erlang VM, known for
running low-latency, distributed and
fault-tolerant systems.
WHAT IS
PHOENIX?
Phoenix is a productive web framework
for Elixir that does not compromise speed
and maintainability.
PHOENIX = PRODUCTIVITY + PERFORMANCE
PERFORMANCE
I don’t care about performance.
* that much
PRODUCTIVITY
SHORT
TERM
LONG
TERM
SHORT TERM
PRODUCTIVITY
The Phoenix Backpack
• Mix (generators, tasks, etc.)
• Erlang libraries + Hex.pm
• ES6 out of the box
• Live reload
• Nice error pages
• Concurrent test tools +
DocTests
• Great docs (for real)
• Channels + Presence
• OTP: humongous set of
libraries for distributed
computing
• Erlang observer
• ....
Remember the “15 min blog” by DHH?
That was productivity!
Let’s try build the “15 min real time Twitter”
(or something close to).
https://github.com/javiercr/conferencia_ror_demo
LET’S
GET STARTED
rails new twitter_demo mix phoenix.new twitter_demo
!"" twitter_demo
#"" app
$   #"" assets
$   #"" channels
$   #"" controllers
$   #"" helpers
$   #"" jobs
$   #"" mailers
$   #"" models
$   !"" views
#"" bin
#"" config
#"" db
#"" lib
#"" log
#"" public
#"" test
#"" tmp
!"" vendor
!"" twitter_demo
#"" config
#"" deps
#"" lib
#"" node_modules
#"" priv
#"" test
!"" web
#"" channels
#"" controllers
#"" models
#"" static
#"" templates
!"" views
$ cd twitter_demo
$ bundle install
$ rake db:create
$ rails server
$ cd twitter_demo
$ mix deps.get && npm install
$ mix ecto.create
$ mix phoenix.server
ROUTES
# /web/router.ex
defmodule TwitterDemo.Router do
use TwitterDemo.Web, :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 "/", TwitterDemo do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
get "/timeline", PageController, :timeline
end
# Other scopes may use custom stacks.
# scope "/api", TwitterDemo do
# pipe_through :api
# end
end
# /config/routes.rb
Rails.application.routes.draw do
root 'page#index'
get '/timeline' => 'page#timeline'
end
# /web/router.ex
defmodule TwitterDemo.Router do
use TwitterDemo.Web, :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 "/", TwitterDemo do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
get "/timeline", PageController, :timeline
end
# Other scopes may use custom stacks.
# scope "/api", TwitterDemo do
# pipe_through :api
# end
end
# /config/routes.rb
Rails.application.routes.draw do
root 'page#index'
get '/timeline' => 'page#timeline'
end
Plug
It’s an Elixir library that tries to solve the same problem than
Rack does for Ruby.
A plug is a function or module which always receives and returns
a connection, doing some data transformations in the middle.
When we compose multiple plugs we form a pipeline.
CONTROLLER
# /web/router.ex
defmodule TwitterDemo.Router do
use TwitterDemo.Web, :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 "/", TwitterDemo do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
get "/timeline", PageController, :timeline
end
# Other scopes may use custom stacks.
# scope "/api", TwitterDemo do
# pipe_through :api
# end
end
$ rails g controller Page index timeline
# /app/controllers/page_controller.rb
class PageController < ApplicationController
def index
end
def timeline
end
end
# /web/controllers/page_controller.ex
defmodule TwitterDemo.PageController do
use TwitterDemo.Web, :controller
def index(conn, _params) do
render conn, "index.html"
end
def timeline(conn, params) do
conn
|> assign(:nickname, params["nickname"])
|> render("timeline.html")
end
end
# /app/controllers/page_controller.rb
class PageController < ApplicationController
def index
end
def timeline
end
end
# /web/controllers/page_controller.ex
defmodule TwitterDemo.PageController do
use TwitterDemo.Web, :controller
def index(conn, _params) do
render conn, "index.html"
end
def timeline(conn, params) do
conn
|> assign(:nickname, params["nickname"])
|> render("timeline.html")
end
end
Typical code in OOP / imperative programming:
people = DB.find_customers
orders = Orders.for_customers(people)
tax = sales_tax(orders, 2013)
filing = prepare_filing(tax)
We could rewrite it as...
filing = prepare_filing(
sales_tax(Orders.for_customers(
DB.find_customers), 2013))
Pipe Operator |>
Pipe Operator |>
With Elixir pipe operator we can do just
filing = DB.find_customers
|> Orders.for_customers
|> sales_tax(2013)
|> prepare_filing
“|>” passes the result from the left expression as
the first argument to the right expression. Kinda
like the Unix pipe “|”. It’s just useful syntax sugar.
VIEWS /
TEMPLATES
<!-- /app/views/page/index.html.erb -->
<h1>Welcome to TwitterDemo!</h1>
<%= form_tag timeline_path, method: "get" do %>
<label for="nickname">Nickname</label>:
<input type="text" name="nickname"></input>
<button>Connect!</button>
<% end %>
<!-- /web/templates/page/index.html.eex -->
<h1>Welcome to TwitterDemo!</h1>
<%= form_tag(page_path(@conn, :timeline), method: "get") do %>
<label for="nickname">Nickname</label>:
<input type="text" name="nickname"></input>
<button>Connect!</button>
<% end %>
<!-- /app/views/page/timeline.html.erb -->
<script>window.nickname = "<%= @nickname %>";</script>
<div id="messages"></div>
<input id="chat-input" type="text"></input>
<!-- /web/templates/page/timeline.html.eex -->
<script>window.nickname = "<%= @nickname %>";</script>
<div id="messages"></div>
<input id="chat-input" type="text"></input>
MODEL
$ rails g model Message author:string
content:text
$ rake db:migrate
$ mix phoenix.gen.model Message messages
author:string content:text
$ mix ecto.create && mix ecto.migrate
# /web/models/message.ex
defmodule TwitterDemo.Message do
use TwitterDemo.Web, :model
@derive {Poison.Encoder, only: [:author, :content, :inserted_at]}
schema "messages" do
field :author, :string
field :content, :string
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params  %{}) do
struct
|> cast(params, [:author, :content])
|> validate_required([:author, :content])
end
end
# /app/models/message.rb
class Message < ApplicationRecord
validates_presence_of :author, :content
end
# /web/models/message.ex
defmodule TwitterDemo.Message do
use TwitterDemo.Web, :model
@derive {Poison.Encoder, only: [:author, :content, :inserted_at]}
schema "messages" do
field :author, :string
field :content, :string
timestamps()
end
@doc """
Builds a changeset based on the `struct` and `params`.
"""
def changeset(struct, params  %{}) do
struct
|> cast(params, [:author, :content])
|> validate_required([:author, :content])
end
end
# /app/models/message.rb
class Message < ApplicationRecord
validates_presence_of :author, :content
end
Ecto
You could think about Ecto as “the ActiveRecord of Elixir”.
But better don’t. It’s not even an ORM (in its purest definition).
It’s a database wrapper and it’s main target it’s PostgreSQL.
Other database are supported too.
Main concepts behind Ecto are:
Schemas: each Model defines a struct with its schema.
Changesets: define a pipeline of transformations (casting, validation &
filtering) over our data before it hits the database.
CHANNEL
$ rails g channel Timeline new_msg $ mix phoenix.gen.channel Timeline
# /app/channels/timeline_channel.rb
# Be sure to restart your server when you modify this file.
Action Cable runs in a loop that does not support auto
reloading.
class TimelineChannel < ApplicationCable::Channel
def subscribed
@nickname = params[:nickname]
stream_from "timeline"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def new_msg(payload)
# Careful with creating the record here
# http://www.akitaonrails.com/2015/12/28/fixing-dhh-s-
rails-5-chat-demo
message = Message.create!(content: payload['content'],
author: @nickname)
# DHH suggests doing this in a background job instead, I’m
not sure why?
ActionCable.server.broadcast 'timeline', message: message
end
end
$ mix phoenix.gen.channel Timeline
# /web/channels/user_socket.ex
defmodule TwitterDemo.UserSocket do
use Phoenix.Socket
## Channels
channel "timeline:lobby", TwitterDemo.TimelineChannel
## Transports
transport :websocket, Phoenix.Transports.WebSocket
# transport :longpoll, Phoenix.Transports.LongPoll
# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
# verification, you can put default assigns into
# the socket that will be set for all channels, ie
#
# {:ok, assign(socket, :user_id, verified_user_id)}
#
# To deny connection, return `:error`.
#
# ...
def connect(params, socket) do
{:ok, assign(socket, :nickname, params["nickname"])}
end
# ....
def id(_socket), do: nil
end
# /app/channels/timeline_channel.rb
# Be sure to restart your server when you modify this file.
Action Cable runs in a loop that does not support auto
reloading.
class TimelineChannel < ApplicationCable::Channel
def subscribed
@nickname = params[:nickname]
stream_from "timeline"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def new_msg(payload)
# Careful with creating the record here
# http://www.akitaonrails.com/2015/12/28/fixing-dhh-s-
rails-5-chat-demo
message = Message.create!(content: payload['content'],
author: @nickname)
# DHH suggests doing this in a background job instead
ActionCable.server.broadcast 'timeline', message: message
end
end
# /web/channels/timeline_channel.ex
defmodule TwitterDemo.TimelineChannel do
use TwitterDemo.Web, :channel
alias TwitterDemo.Message
def join("timeline:lobby", payload, socket) do
# Add authorization logic here as required.
{:ok, socket}
end
def handle_in("new_msg", %{"content" => content,}, socket) do
changeset = Message.changeset(%Message{}, %{
content: content,
author: socket.assigns.nickname
})
case Repo.insert(changeset) do
{:ok, message} ->
broadcast! socket, "new_msg", %{message: message}
{:noreply, socket}
{:error, _changeset} ->
{:reply, {:error, %{error: "Error saving the message"}},
socket}
end
end
def handle_out("new_msg", payload, socket) do
push socket, "new_msg", payload
{:noreply, socket}
end
end
# /app/channels/timeline_channel.rb
# Be sure to restart your server when you modify this file.
Action Cable runs in a loop that does not support auto
reloading.
class TimelineChannel < ApplicationCable::Channel
def subscribed
@nickname = params[:nickname]
stream_from "timeline"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def new_msg(payload)
# Careful with creating the record here
# http://www.akitaonrails.com/2015/12/28/fixing-dhh-s-
rails-5-chat-demo
message = Message.create!(content: payload['content'],
author: @nickname)
# DHH suggests doing this in a background job instead
ActionCable.server.broadcast 'timeline', message: message
end
end
# /web/channels/timeline_channel.ex
defmodule TwitterDemo.TimelineChannel do
use TwitterDemo.Web, :channel
alias TwitterDemo.Message
def join("timeline:lobby", payload, socket) do
# Add authorization logic here as required.
{:ok, socket}
end
def handle_in("new_msg", %{"content" => content,}, socket) do
changeset = Message.changeset(%Message{}, %{
content: content,
author: socket.assigns.nickname
})
case Repo.insert(changeset) do
{:ok, message} ->
broadcast! socket, "new_msg", %{message: message}
{:noreply, socket}
{:error, _changeset} ->
{:reply, {:error, %{error: "Error saving the message"}},
socket}
end
end
def handle_out("new_msg", payload, socket) do
push socket, "new_msg", payload
{:noreply, socket}
end
end
# /app/channels/timeline_channel.rb
# Be sure to restart your server when you modify this file.
Action Cable runs in a loop that does not support auto
reloading.
class TimelineChannel < ApplicationCable::Channel
def subscribed
@nickname = params[:nickname]
stream_from "timeline"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def new_msg(payload)
# Careful with creating the record here
# http://www.akitaonrails.com/2015/12/28/fixing-dhh-s-
rails-5-chat-demo
message = Message.create!(content: payload['content'],
author: @nickname)
# DHH suggests doing this in a background job instead
ActionCable.server.broadcast 'timeline', message: message
end
end
Pattern Matching
In Elixir: a = 1 does not mean we are assigning 1 to the variable a.
Instead of assigning a variable, in Elixir we talk about binding a variable .
The equal signs means we are asserting that the left hand side (LHS) is
equal to the right one (RHS). It’s like basic algebra.
iex> a = 1
1
iex> 1 = a
1
iex> [1, a, 3] = [1, 2, 3]
[1, 2, 3]
iex> a
2
Pattern Matching
Function signatures use pattern matching.
Therefore we can have more than one signature.
defmodule Factorial do
def of(0), do: 1
def of(x), do: x * of(x-1)
end
look mum! programming without if - else
# /web/channels/timeline_channel.ex
defmodule TwitterDemo.TimelineChannel do
use TwitterDemo.Web, :channel
alias TwitterDemo.Message
def join("timeline:lobby", payload, socket) do
# Add authorization logic here as required.
{:ok, socket}
end
def handle_in("new_msg", %{"content" => content,}, socket) do
changeset = Message.changeset(%Message{}, %{
content: content,
author: socket.assigns.nickname
})
case Repo.insert(changeset) do
{:ok, message} ->
broadcast! socket, "new_msg", %{message: message}
{:noreply, socket}
{:error, _changeset} ->
{:reply, {:error, %{error: "Error saving the message"}},
socket}
end
end
def handle_out("new_msg", payload, socket) do
push socket, "new_msg", payload
{:noreply, socket}
end
end
# /app/channels/timeline_channel.rb
# Be sure to restart your server when you modify this file.
Action Cable runs in a loop that does not support auto
reloading.
class TimelineChannel < ApplicationCable::Channel
def subscribed
@nickname = params[:nickname]
stream_from "timeline"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def new_msg(payload)
# Careful with creating the record here
# http://www.akitaonrails.com/2015/12/28/fixing-dhh-s-
rails-5-chat-demo
message = Message.create!(content: payload['content'],
author: @nickname)
# DHH suggests doing this in a background job instead
ActionCable.server.broadcast 'timeline', message: message
end
end
Jose, Don’t forget to
mention OTP!
JAVASCRIPT
# /app/assets/javascripts/channels/timeline.coffee
App.timeline = App.cable.subscriptions.create {channel:
"TimelineChannel", nickname: window.nickname},
connected: ->
# Called when the subscription is ready for use on the
server
chatInput = document.querySelector("#chat-input")
chatInput.addEventListener "keypress", (event) =>
if event.keyCode == 13
@new_msg chatInput.value
chatInput.value = ""
received: (payload) ->
@_renderdMessage(payload.message)
new_msg: (message) ->
@perform 'new_msg', {content: message}
_renderdMessage: (message) ->
# [...]
messagesContainer.appendChild(messageItem)
// /web/static/js/socket.js
import {Socket} from "phoenix"
let socket = new Socket("/socket", {
params: { token: window.userToken, nickname:
window.nickname }})
socket.connect()
let channel = socket.channel("timeline:lobby", {})
let chatInput = document.querySelector("#chat-input")
let renderMessage = (message) => {
// [...]
messagesContainer.appendChild(messageItem)
}
chatInput.addEventListener("keypress", event => {
if(event.keyCode === 13){
channel.push("new_msg", {content: chatInput.value})
chatInput.value = ""
}
})
channel.on("new_msg", payload => {
renderMessage(payload.message)
})
channel.join()
export default socket
HOMEWORK
(for you)
1. Send history of messages when connecting to
channel.
2. Add Presence module (to display who is online).
3. Create a startup with this, become a unicorn
and profit!
* Only 1. & 2. are solved here
https://github.com/diacode/talkex/tree/feature/message-db-persistence
hint: it takes 10 minutes with phoenix v1.2
tl;dr
$ rails new my_project $ mix phoenix.new my_project
$ rails g [x] $ mix phoenix.gen.[x]
$ bundle install $ mix deps.get
$ rake db:migrate $ mix ecto.migrate
$ rails server $ mix phoenix.server
$ rails console $ iex -S mix
bundle + rake Mix
RubyGems Hex.pm
Rack Plug
Minitest / RSpec ExUnit
ActiveRecord Ecto
ActionCable Channels + Presence
sprockets Brunch (npm based)
Redis / Sidekiq / Resque OTP
LONG TERM
PRODUCTIVY
EXPLICIT > IMPLICIT
or at least some reasonable balance
“Functional Programming is about
making the complex parts of your
program explicit”
– José Valim
Next steps (for you)
• Watch every talk by José Valim & Chris McCord
Really, you won’t regret.
• Books:
Programming Elixir – Dave Thomas
Programming Phoenix – Chris McCord, Bruce Tate & José Valim.
• Elixir Getting Started Guide (really good!)
http://elixir-lang.org/getting-started/introduction.html
• Phoenix Guide (really good!)
http://www.phoenixframework.org/docs/overview
• Elixir Radar (newsletter)
http://plataformatec.com.br/elixir-radar
• Madrid |> Elixir MeetUp
http://www.meetup.com/es-ES/Madrid-Elixir/
THANK YOU
Questions?
Special thanks go to Diacode’s former team:
Victor Viruete, Ricardo García, Artur Chruszcz & Bruno Bayón
<3
[ now you can blink again ]

More Related Content

What's hot

Ansible with AWS
Ansible with AWSAnsible with AWS
Ansible with AWSAllan Denot
 
Testing Ansible with Jenkins and Docker
Testing Ansible with Jenkins and DockerTesting Ansible with Jenkins and Docker
Testing Ansible with Jenkins and DockerDennis Rowe
 
Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021TomStraub5
 
DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'
DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'
DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'rmcleay
 
An Introduction to Kube-Lego
An Introduction to Kube-LegoAn Introduction to Kube-Lego
An Introduction to Kube-LegoMatthew Barker
 
Ansible Intro - June 2015 / Ansible Barcelona User Group
Ansible Intro - June 2015 / Ansible Barcelona User GroupAnsible Intro - June 2015 / Ansible Barcelona User Group
Ansible Intro - June 2015 / Ansible Barcelona User GroupOrestes Carracedo
 
ElixirConf Lightning Talk: Elixir |> Production
ElixirConf Lightning Talk: Elixir |> ProductionElixirConf Lightning Talk: Elixir |> Production
ElixirConf Lightning Talk: Elixir |> ProductionJeff Weiss
 
Automating aws infrastructure and code deployments using Ansible @WebEngage
Automating aws infrastructure and code deployments using Ansible @WebEngageAutomating aws infrastructure and code deployments using Ansible @WebEngage
Automating aws infrastructure and code deployments using Ansible @WebEngageVishal Uderani
 
Cachopo - Scalable Stateful Services - Madrid Elixir Meetup
Cachopo - Scalable Stateful Services - Madrid Elixir MeetupCachopo - Scalable Stateful Services - Madrid Elixir Meetup
Cachopo - Scalable Stateful Services - Madrid Elixir MeetupAbel Muíño
 
Ansible 2 and Ansible Galaxy 2
Ansible 2 and Ansible Galaxy 2Ansible 2 and Ansible Galaxy 2
Ansible 2 and Ansible Galaxy 2Jeff Geerling
 
Spinnaker 파트 1
Spinnaker 파트 1Spinnaker 파트 1
Spinnaker 파트 1Steven Shim
 
Create Development and Production Environments with Vagrant
Create Development and Production Environments with VagrantCreate Development and Production Environments with Vagrant
Create Development and Production Environments with VagrantBrian Hogan
 
Cocoapods and Most common used library in Swift
Cocoapods and Most common used library in SwiftCocoapods and Most common used library in Swift
Cocoapods and Most common used library in SwiftWan Muzaffar Wan Hashim
 
Kube-AWS
Kube-AWSKube-AWS
Kube-AWSCoreOS
 
Bootstrap your Cloud Infrastructure using puppet and hashicorp stack
Bootstrap your Cloud Infrastructure using puppet and hashicorp stackBootstrap your Cloud Infrastructure using puppet and hashicorp stack
Bootstrap your Cloud Infrastructure using puppet and hashicorp stackBram Vogelaar
 
Akka.net versus microsoft orleans
Akka.net versus microsoft orleansAkka.net versus microsoft orleans
Akka.net versus microsoft orleansBill Tulloch
 
What's new in Ansible 2.0
What's new in Ansible 2.0What's new in Ansible 2.0
What's new in Ansible 2.0Allan Denot
 
I can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and SpringI can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and SpringJoe Kutner
 

What's hot (20)

Ansible with AWS
Ansible with AWSAnsible with AWS
Ansible with AWS
 
Ansible and AWS
Ansible and AWSAnsible and AWS
Ansible and AWS
 
Testing Ansible with Jenkins and Docker
Testing Ansible with Jenkins and DockerTesting Ansible with Jenkins and Docker
Testing Ansible with Jenkins and Docker
 
Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021Developing Terraform Modules at Scale - HashiTalks 2021
Developing Terraform Modules at Scale - HashiTalks 2021
 
DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'
DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'
DevOps in a Regulated World - aka 'Ansible, AWS, and Jenkins'
 
London Hug 19/5 - Terraform in Production
London Hug 19/5 - Terraform in ProductionLondon Hug 19/5 - Terraform in Production
London Hug 19/5 - Terraform in Production
 
An Introduction to Kube-Lego
An Introduction to Kube-LegoAn Introduction to Kube-Lego
An Introduction to Kube-Lego
 
Ansible Intro - June 2015 / Ansible Barcelona User Group
Ansible Intro - June 2015 / Ansible Barcelona User GroupAnsible Intro - June 2015 / Ansible Barcelona User Group
Ansible Intro - June 2015 / Ansible Barcelona User Group
 
ElixirConf Lightning Talk: Elixir |> Production
ElixirConf Lightning Talk: Elixir |> ProductionElixirConf Lightning Talk: Elixir |> Production
ElixirConf Lightning Talk: Elixir |> Production
 
Automating aws infrastructure and code deployments using Ansible @WebEngage
Automating aws infrastructure and code deployments using Ansible @WebEngageAutomating aws infrastructure and code deployments using Ansible @WebEngage
Automating aws infrastructure and code deployments using Ansible @WebEngage
 
Cachopo - Scalable Stateful Services - Madrid Elixir Meetup
Cachopo - Scalable Stateful Services - Madrid Elixir MeetupCachopo - Scalable Stateful Services - Madrid Elixir Meetup
Cachopo - Scalable Stateful Services - Madrid Elixir Meetup
 
Ansible 2 and Ansible Galaxy 2
Ansible 2 and Ansible Galaxy 2Ansible 2 and Ansible Galaxy 2
Ansible 2 and Ansible Galaxy 2
 
Spinnaker 파트 1
Spinnaker 파트 1Spinnaker 파트 1
Spinnaker 파트 1
 
Create Development and Production Environments with Vagrant
Create Development and Production Environments with VagrantCreate Development and Production Environments with Vagrant
Create Development and Production Environments with Vagrant
 
Cocoapods and Most common used library in Swift
Cocoapods and Most common used library in SwiftCocoapods and Most common used library in Swift
Cocoapods and Most common used library in Swift
 
Kube-AWS
Kube-AWSKube-AWS
Kube-AWS
 
Bootstrap your Cloud Infrastructure using puppet and hashicorp stack
Bootstrap your Cloud Infrastructure using puppet and hashicorp stackBootstrap your Cloud Infrastructure using puppet and hashicorp stack
Bootstrap your Cloud Infrastructure using puppet and hashicorp stack
 
Akka.net versus microsoft orleans
Akka.net versus microsoft orleansAkka.net versus microsoft orleans
Akka.net versus microsoft orleans
 
What's new in Ansible 2.0
What's new in Ansible 2.0What's new in Ansible 2.0
What's new in Ansible 2.0
 
I can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and SpringI can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and Spring
 

Similar to Phoenix for Rails Devs

Building web framework with Rack
Building web framework with RackBuilding web framework with Rack
Building web framework with Racksickill
 
RoR 101: Session 2
RoR 101: Session 2RoR 101: Session 2
RoR 101: Session 2Rory Gianni
 
Ruby on Rails - Introduction
Ruby on Rails - IntroductionRuby on Rails - Introduction
Ruby on Rails - IntroductionVagmi Mudumbai
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkDaniel Spector
 
Introduction to Ruby on Rails
Introduction to Ruby on RailsIntroduction to Ruby on Rails
Introduction to Ruby on RailsAlessandro DS
 
Rails web api 开发
Rails web api 开发Rails web api 开发
Rails web api 开发shaokun
 
Rails Engine | Modular application
Rails Engine | Modular applicationRails Engine | Modular application
Rails Engine | Modular applicationmirrec
 
Rails missing features
Rails missing featuresRails missing features
Rails missing featuresAstrails
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteDr Nic Williams
 
Creating a modern web application using Symfony API Platform, ReactJS and Red...
Creating a modern web application using Symfony API Platform, ReactJS and Red...Creating a modern web application using Symfony API Platform, ReactJS and Red...
Creating a modern web application using Symfony API Platform, ReactJS and Red...Jesus Manuel Olivas
 
Example Cosmos SDK Application Tutorial
Example Cosmos SDK Application TutorialExample Cosmos SDK Application Tutorial
Example Cosmos SDK Application TutorialJim Yang
 
Divide and Conquer – Microservices with Node.js
Divide and Conquer – Microservices with Node.jsDivide and Conquer – Microservices with Node.js
Divide and Conquer – Microservices with Node.jsSebastian Springer
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud CastlesBen Scofield
 
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...Codemotion
 

Similar to Phoenix for Rails Devs (20)

Building web framework with Rack
Building web framework with RackBuilding web framework with Rack
Building web framework with Rack
 
Intro to Rack
Intro to RackIntro to Rack
Intro to Rack
 
RoR 101: Session 2
RoR 101: Session 2RoR 101: Session 2
RoR 101: Session 2
 
Ruby on Rails - Introduction
Ruby on Rails - IntroductionRuby on Rails - Introduction
Ruby on Rails - Introduction
 
Dev streams2
Dev streams2Dev streams2
Dev streams2
 
Crossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end FrameworkCrossing the Bridge: Connecting Rails and your Front-end Framework
Crossing the Bridge: Connecting Rails and your Front-end Framework
 
Wider than rails
Wider than railsWider than rails
Wider than rails
 
Introduction to Ruby on Rails
Introduction to Ruby on RailsIntroduction to Ruby on Rails
Introduction to Ruby on Rails
 
Rails web api 开发
Rails web api 开发Rails web api 开发
Rails web api 开发
 
Rails Engine | Modular application
Rails Engine | Modular applicationRails Engine | Modular application
Rails Engine | Modular application
 
Rails missing features
Rails missing featuresRails missing features
Rails missing features
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - Keynote
 
Creating a modern web application using Symfony API Platform, ReactJS and Red...
Creating a modern web application using Symfony API Platform, ReactJS and Red...Creating a modern web application using Symfony API Platform, ReactJS and Red...
Creating a modern web application using Symfony API Platform, ReactJS and Red...
 
Example Cosmos SDK Application Tutorial
Example Cosmos SDK Application TutorialExample Cosmos SDK Application Tutorial
Example Cosmos SDK Application Tutorial
 
Workshop 16: EmberJS Parte I
Workshop 16: EmberJS Parte IWorkshop 16: EmberJS Parte I
Workshop 16: EmberJS Parte I
 
Supa fast Ruby + Rails
Supa fast Ruby + RailsSupa fast Ruby + Rails
Supa fast Ruby + Rails
 
Curso rails
Curso railsCurso rails
Curso rails
 
Divide and Conquer – Microservices with Node.js
Divide and Conquer – Microservices with Node.jsDivide and Conquer – Microservices with Node.js
Divide and Conquer – Microservices with Node.js
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud Castles
 
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
 

More from Diacode

Startup nomads
Startup nomadsStartup nomads
Startup nomadsDiacode
 
Ruby on Rails & TDD con RSpec
Ruby on Rails & TDD con RSpecRuby on Rails & TDD con RSpec
Ruby on Rails & TDD con RSpecDiacode
 
Hacking your bank with Ruby and reverse engineering (Madrid.rb)
Hacking your bank with Ruby and reverse engineering (Madrid.rb)Hacking your bank with Ruby and reverse engineering (Madrid.rb)
Hacking your bank with Ruby and reverse engineering (Madrid.rb)Diacode
 
TLKR.io @ Betabeers Madrid
TLKR.io @ Betabeers MadridTLKR.io @ Betabeers Madrid
TLKR.io @ Betabeers MadridDiacode
 
Métricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyectoMétricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyectoDiacode
 
Métricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyectoMétricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyectoDiacode
 
Presentación de Kogi
Presentación de KogiPresentación de Kogi
Presentación de KogiDiacode
 
Educación: The Next Big Thing
Educación: The Next Big ThingEducación: The Next Big Thing
Educación: The Next Big ThingDiacode
 
Front-End Frameworks: a quick overview
Front-End Frameworks: a quick overviewFront-End Frameworks: a quick overview
Front-End Frameworks: a quick overviewDiacode
 
Taller de Introducción a Ruby on Rails (2ª parte)
Taller de Introducción a Ruby on Rails (2ª parte)Taller de Introducción a Ruby on Rails (2ª parte)
Taller de Introducción a Ruby on Rails (2ª parte)Diacode
 
Taller de Introducción a Ruby on Rails
Taller de Introducción a Ruby on RailsTaller de Introducción a Ruby on Rails
Taller de Introducción a Ruby on RailsDiacode
 

More from Diacode (11)

Startup nomads
Startup nomadsStartup nomads
Startup nomads
 
Ruby on Rails & TDD con RSpec
Ruby on Rails & TDD con RSpecRuby on Rails & TDD con RSpec
Ruby on Rails & TDD con RSpec
 
Hacking your bank with Ruby and reverse engineering (Madrid.rb)
Hacking your bank with Ruby and reverse engineering (Madrid.rb)Hacking your bank with Ruby and reverse engineering (Madrid.rb)
Hacking your bank with Ruby and reverse engineering (Madrid.rb)
 
TLKR.io @ Betabeers Madrid
TLKR.io @ Betabeers MadridTLKR.io @ Betabeers Madrid
TLKR.io @ Betabeers Madrid
 
Métricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyectoMétricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyecto
 
Métricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyectoMétricas para hacer crecer tu proyecto
Métricas para hacer crecer tu proyecto
 
Presentación de Kogi
Presentación de KogiPresentación de Kogi
Presentación de Kogi
 
Educación: The Next Big Thing
Educación: The Next Big ThingEducación: The Next Big Thing
Educación: The Next Big Thing
 
Front-End Frameworks: a quick overview
Front-End Frameworks: a quick overviewFront-End Frameworks: a quick overview
Front-End Frameworks: a quick overview
 
Taller de Introducción a Ruby on Rails (2ª parte)
Taller de Introducción a Ruby on Rails (2ª parte)Taller de Introducción a Ruby on Rails (2ª parte)
Taller de Introducción a Ruby on Rails (2ª parte)
 
Taller de Introducción a Ruby on Rails
Taller de Introducción a Ruby on RailsTaller de Introducción a Ruby on Rails
Taller de Introducción a Ruby on Rails
 

Recently uploaded

Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalLionel Briand
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Developmentvyaparkranti
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringHironori Washizaki
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfFerryKemperman
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 

Recently uploaded (20)

Precise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive GoalPrecise and Complete Requirements? An Elusive Goal
Precise and Complete Requirements? An Elusive Goal
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
VK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web DevelopmentVK Business Profile - provides IT solutions and Web Development
VK Business Profile - provides IT solutions and Web Development
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
Machine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their EngineeringMachine Learning Software Engineering Patterns and Their Engineering
Machine Learning Software Engineering Patterns and Their Engineering
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Introduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdfIntroduction Computer Science - Software Design.pdf
Introduction Computer Science - Software Design.pdf
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 

Phoenix for Rails Devs

  • 1. PHOENIX FOR RAILS DEVS Conferencia Rails Madrid 15/10/2016
  • 2. If you're having talk problems, I feel bad for you, son. I got 61 problems, but a slide ain't one; hit me!
  • 3. About me: Javier Cuevas @javier_dev RUBY ON RAILS SHOP WHO EMBRACED ELIXIR AIRBNB FOR DOGS “MAJESTIC” RAILS 3.2 MONOLITH
  • 5. José Valim Former Rails Core Team member. He was trying to make Rails really thread safe but... ended up creating a new programming language (Elixir). Oops! PerformanceProductivity
  • 6. Chris McCord Author of render_sync a Ruby gem to have real-time partials in Rails (before ActionCable). It got complicated and... he ended up creating a new web framework (Phoenix). Oops!
  • 8. Elixir is a dynamic, functional language designed for building scalable and maintainable applications. Elixir leverages the Erlang VM, known for running low-latency, distributed and fault-tolerant systems.
  • 10. Phoenix is a productive web framework for Elixir that does not compromise speed and maintainability.
  • 11. PHOENIX = PRODUCTIVITY + PERFORMANCE
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19. I don’t care about performance. * that much
  • 22. The Phoenix Backpack • Mix (generators, tasks, etc.) • Erlang libraries + Hex.pm • ES6 out of the box • Live reload • Nice error pages • Concurrent test tools + DocTests • Great docs (for real) • Channels + Presence • OTP: humongous set of libraries for distributed computing • Erlang observer • ....
  • 23. Remember the “15 min blog” by DHH? That was productivity! Let’s try build the “15 min real time Twitter” (or something close to).
  • 26. rails new twitter_demo mix phoenix.new twitter_demo
  • 27. !"" twitter_demo #"" app $   #"" assets $   #"" channels $   #"" controllers $   #"" helpers $   #"" jobs $   #"" mailers $   #"" models $   !"" views #"" bin #"" config #"" db #"" lib #"" log #"" public #"" test #"" tmp !"" vendor !"" twitter_demo #"" config #"" deps #"" lib #"" node_modules #"" priv #"" test !"" web #"" channels #"" controllers #"" models #"" static #"" templates !"" views
  • 28. $ cd twitter_demo $ bundle install $ rake db:create $ rails server $ cd twitter_demo $ mix deps.get && npm install $ mix ecto.create $ mix phoenix.server
  • 29.
  • 31. # /web/router.ex defmodule TwitterDemo.Router do use TwitterDemo.Web, :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 "/", TwitterDemo do pipe_through :browser # Use the default browser stack get "/", PageController, :index get "/timeline", PageController, :timeline end # Other scopes may use custom stacks. # scope "/api", TwitterDemo do # pipe_through :api # end end # /config/routes.rb Rails.application.routes.draw do root 'page#index' get '/timeline' => 'page#timeline' end
  • 32. # /web/router.ex defmodule TwitterDemo.Router do use TwitterDemo.Web, :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 "/", TwitterDemo do pipe_through :browser # Use the default browser stack get "/", PageController, :index get "/timeline", PageController, :timeline end # Other scopes may use custom stacks. # scope "/api", TwitterDemo do # pipe_through :api # end end # /config/routes.rb Rails.application.routes.draw do root 'page#index' get '/timeline' => 'page#timeline' end
  • 33. Plug It’s an Elixir library that tries to solve the same problem than Rack does for Ruby. A plug is a function or module which always receives and returns a connection, doing some data transformations in the middle. When we compose multiple plugs we form a pipeline.
  • 35. # /web/router.ex defmodule TwitterDemo.Router do use TwitterDemo.Web, :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 "/", TwitterDemo do pipe_through :browser # Use the default browser stack get "/", PageController, :index get "/timeline", PageController, :timeline end # Other scopes may use custom stacks. # scope "/api", TwitterDemo do # pipe_through :api # end end $ rails g controller Page index timeline
  • 36. # /app/controllers/page_controller.rb class PageController < ApplicationController def index end def timeline end end # /web/controllers/page_controller.ex defmodule TwitterDemo.PageController do use TwitterDemo.Web, :controller def index(conn, _params) do render conn, "index.html" end def timeline(conn, params) do conn |> assign(:nickname, params["nickname"]) |> render("timeline.html") end end
  • 37. # /app/controllers/page_controller.rb class PageController < ApplicationController def index end def timeline end end # /web/controllers/page_controller.ex defmodule TwitterDemo.PageController do use TwitterDemo.Web, :controller def index(conn, _params) do render conn, "index.html" end def timeline(conn, params) do conn |> assign(:nickname, params["nickname"]) |> render("timeline.html") end end
  • 38. Typical code in OOP / imperative programming: people = DB.find_customers orders = Orders.for_customers(people) tax = sales_tax(orders, 2013) filing = prepare_filing(tax) We could rewrite it as... filing = prepare_filing( sales_tax(Orders.for_customers( DB.find_customers), 2013)) Pipe Operator |>
  • 39. Pipe Operator |> With Elixir pipe operator we can do just filing = DB.find_customers |> Orders.for_customers |> sales_tax(2013) |> prepare_filing “|>” passes the result from the left expression as the first argument to the right expression. Kinda like the Unix pipe “|”. It’s just useful syntax sugar.
  • 41. <!-- /app/views/page/index.html.erb --> <h1>Welcome to TwitterDemo!</h1> <%= form_tag timeline_path, method: "get" do %> <label for="nickname">Nickname</label>: <input type="text" name="nickname"></input> <button>Connect!</button> <% end %> <!-- /web/templates/page/index.html.eex --> <h1>Welcome to TwitterDemo!</h1> <%= form_tag(page_path(@conn, :timeline), method: "get") do %> <label for="nickname">Nickname</label>: <input type="text" name="nickname"></input> <button>Connect!</button> <% end %>
  • 42. <!-- /app/views/page/timeline.html.erb --> <script>window.nickname = "<%= @nickname %>";</script> <div id="messages"></div> <input id="chat-input" type="text"></input> <!-- /web/templates/page/timeline.html.eex --> <script>window.nickname = "<%= @nickname %>";</script> <div id="messages"></div> <input id="chat-input" type="text"></input>
  • 43. MODEL
  • 44. $ rails g model Message author:string content:text $ rake db:migrate $ mix phoenix.gen.model Message messages author:string content:text $ mix ecto.create && mix ecto.migrate
  • 45. # /web/models/message.ex defmodule TwitterDemo.Message do use TwitterDemo.Web, :model @derive {Poison.Encoder, only: [:author, :content, :inserted_at]} schema "messages" do field :author, :string field :content, :string timestamps() end @doc """ Builds a changeset based on the `struct` and `params`. """ def changeset(struct, params %{}) do struct |> cast(params, [:author, :content]) |> validate_required([:author, :content]) end end # /app/models/message.rb class Message < ApplicationRecord validates_presence_of :author, :content end
  • 46. # /web/models/message.ex defmodule TwitterDemo.Message do use TwitterDemo.Web, :model @derive {Poison.Encoder, only: [:author, :content, :inserted_at]} schema "messages" do field :author, :string field :content, :string timestamps() end @doc """ Builds a changeset based on the `struct` and `params`. """ def changeset(struct, params %{}) do struct |> cast(params, [:author, :content]) |> validate_required([:author, :content]) end end # /app/models/message.rb class Message < ApplicationRecord validates_presence_of :author, :content end
  • 47. Ecto You could think about Ecto as “the ActiveRecord of Elixir”. But better don’t. It’s not even an ORM (in its purest definition). It’s a database wrapper and it’s main target it’s PostgreSQL. Other database are supported too. Main concepts behind Ecto are: Schemas: each Model defines a struct with its schema. Changesets: define a pipeline of transformations (casting, validation & filtering) over our data before it hits the database.
  • 49. $ rails g channel Timeline new_msg $ mix phoenix.gen.channel Timeline
  • 50. # /app/channels/timeline_channel.rb # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. class TimelineChannel < ApplicationCable::Channel def subscribed @nickname = params[:nickname] stream_from "timeline" end def unsubscribed # Any cleanup needed when channel is unsubscribed end def new_msg(payload) # Careful with creating the record here # http://www.akitaonrails.com/2015/12/28/fixing-dhh-s- rails-5-chat-demo message = Message.create!(content: payload['content'], author: @nickname) # DHH suggests doing this in a background job instead, I’m not sure why? ActionCable.server.broadcast 'timeline', message: message end end $ mix phoenix.gen.channel Timeline
  • 51. # /web/channels/user_socket.ex defmodule TwitterDemo.UserSocket do use Phoenix.Socket ## Channels channel "timeline:lobby", TwitterDemo.TimelineChannel ## Transports transport :websocket, Phoenix.Transports.WebSocket # transport :longpoll, Phoenix.Transports.LongPoll # Socket params are passed from the client and can # be used to verify and authenticate a user. After # verification, you can put default assigns into # the socket that will be set for all channels, ie # # {:ok, assign(socket, :user_id, verified_user_id)} # # To deny connection, return `:error`. # # ... def connect(params, socket) do {:ok, assign(socket, :nickname, params["nickname"])} end # .... def id(_socket), do: nil end # /app/channels/timeline_channel.rb # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. class TimelineChannel < ApplicationCable::Channel def subscribed @nickname = params[:nickname] stream_from "timeline" end def unsubscribed # Any cleanup needed when channel is unsubscribed end def new_msg(payload) # Careful with creating the record here # http://www.akitaonrails.com/2015/12/28/fixing-dhh-s- rails-5-chat-demo message = Message.create!(content: payload['content'], author: @nickname) # DHH suggests doing this in a background job instead ActionCable.server.broadcast 'timeline', message: message end end
  • 52. # /web/channels/timeline_channel.ex defmodule TwitterDemo.TimelineChannel do use TwitterDemo.Web, :channel alias TwitterDemo.Message def join("timeline:lobby", payload, socket) do # Add authorization logic here as required. {:ok, socket} end def handle_in("new_msg", %{"content" => content,}, socket) do changeset = Message.changeset(%Message{}, %{ content: content, author: socket.assigns.nickname }) case Repo.insert(changeset) do {:ok, message} -> broadcast! socket, "new_msg", %{message: message} {:noreply, socket} {:error, _changeset} -> {:reply, {:error, %{error: "Error saving the message"}}, socket} end end def handle_out("new_msg", payload, socket) do push socket, "new_msg", payload {:noreply, socket} end end # /app/channels/timeline_channel.rb # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. class TimelineChannel < ApplicationCable::Channel def subscribed @nickname = params[:nickname] stream_from "timeline" end def unsubscribed # Any cleanup needed when channel is unsubscribed end def new_msg(payload) # Careful with creating the record here # http://www.akitaonrails.com/2015/12/28/fixing-dhh-s- rails-5-chat-demo message = Message.create!(content: payload['content'], author: @nickname) # DHH suggests doing this in a background job instead ActionCable.server.broadcast 'timeline', message: message end end
  • 53. # /web/channels/timeline_channel.ex defmodule TwitterDemo.TimelineChannel do use TwitterDemo.Web, :channel alias TwitterDemo.Message def join("timeline:lobby", payload, socket) do # Add authorization logic here as required. {:ok, socket} end def handle_in("new_msg", %{"content" => content,}, socket) do changeset = Message.changeset(%Message{}, %{ content: content, author: socket.assigns.nickname }) case Repo.insert(changeset) do {:ok, message} -> broadcast! socket, "new_msg", %{message: message} {:noreply, socket} {:error, _changeset} -> {:reply, {:error, %{error: "Error saving the message"}}, socket} end end def handle_out("new_msg", payload, socket) do push socket, "new_msg", payload {:noreply, socket} end end # /app/channels/timeline_channel.rb # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. class TimelineChannel < ApplicationCable::Channel def subscribed @nickname = params[:nickname] stream_from "timeline" end def unsubscribed # Any cleanup needed when channel is unsubscribed end def new_msg(payload) # Careful with creating the record here # http://www.akitaonrails.com/2015/12/28/fixing-dhh-s- rails-5-chat-demo message = Message.create!(content: payload['content'], author: @nickname) # DHH suggests doing this in a background job instead ActionCable.server.broadcast 'timeline', message: message end end
  • 54. Pattern Matching In Elixir: a = 1 does not mean we are assigning 1 to the variable a. Instead of assigning a variable, in Elixir we talk about binding a variable . The equal signs means we are asserting that the left hand side (LHS) is equal to the right one (RHS). It’s like basic algebra. iex> a = 1 1 iex> 1 = a 1 iex> [1, a, 3] = [1, 2, 3] [1, 2, 3] iex> a 2
  • 55. Pattern Matching Function signatures use pattern matching. Therefore we can have more than one signature. defmodule Factorial do def of(0), do: 1 def of(x), do: x * of(x-1) end look mum! programming without if - else
  • 56. # /web/channels/timeline_channel.ex defmodule TwitterDemo.TimelineChannel do use TwitterDemo.Web, :channel alias TwitterDemo.Message def join("timeline:lobby", payload, socket) do # Add authorization logic here as required. {:ok, socket} end def handle_in("new_msg", %{"content" => content,}, socket) do changeset = Message.changeset(%Message{}, %{ content: content, author: socket.assigns.nickname }) case Repo.insert(changeset) do {:ok, message} -> broadcast! socket, "new_msg", %{message: message} {:noreply, socket} {:error, _changeset} -> {:reply, {:error, %{error: "Error saving the message"}}, socket} end end def handle_out("new_msg", payload, socket) do push socket, "new_msg", payload {:noreply, socket} end end # /app/channels/timeline_channel.rb # Be sure to restart your server when you modify this file. Action Cable runs in a loop that does not support auto reloading. class TimelineChannel < ApplicationCable::Channel def subscribed @nickname = params[:nickname] stream_from "timeline" end def unsubscribed # Any cleanup needed when channel is unsubscribed end def new_msg(payload) # Careful with creating the record here # http://www.akitaonrails.com/2015/12/28/fixing-dhh-s- rails-5-chat-demo message = Message.create!(content: payload['content'], author: @nickname) # DHH suggests doing this in a background job instead ActionCable.server.broadcast 'timeline', message: message end end Jose, Don’t forget to mention OTP!
  • 58. # /app/assets/javascripts/channels/timeline.coffee App.timeline = App.cable.subscriptions.create {channel: "TimelineChannel", nickname: window.nickname}, connected: -> # Called when the subscription is ready for use on the server chatInput = document.querySelector("#chat-input") chatInput.addEventListener "keypress", (event) => if event.keyCode == 13 @new_msg chatInput.value chatInput.value = "" received: (payload) -> @_renderdMessage(payload.message) new_msg: (message) -> @perform 'new_msg', {content: message} _renderdMessage: (message) -> # [...] messagesContainer.appendChild(messageItem) // /web/static/js/socket.js import {Socket} from "phoenix" let socket = new Socket("/socket", { params: { token: window.userToken, nickname: window.nickname }}) socket.connect() let channel = socket.channel("timeline:lobby", {}) let chatInput = document.querySelector("#chat-input") let renderMessage = (message) => { // [...] messagesContainer.appendChild(messageItem) } chatInput.addEventListener("keypress", event => { if(event.keyCode === 13){ channel.push("new_msg", {content: chatInput.value}) chatInput.value = "" } }) channel.on("new_msg", payload => { renderMessage(payload.message) }) channel.join() export default socket
  • 60. 1. Send history of messages when connecting to channel. 2. Add Presence module (to display who is online). 3. Create a startup with this, become a unicorn and profit! * Only 1. & 2. are solved here https://github.com/diacode/talkex/tree/feature/message-db-persistence hint: it takes 10 minutes with phoenix v1.2
  • 61. tl;dr $ rails new my_project $ mix phoenix.new my_project $ rails g [x] $ mix phoenix.gen.[x] $ bundle install $ mix deps.get $ rake db:migrate $ mix ecto.migrate $ rails server $ mix phoenix.server $ rails console $ iex -S mix bundle + rake Mix RubyGems Hex.pm Rack Plug Minitest / RSpec ExUnit ActiveRecord Ecto ActionCable Channels + Presence sprockets Brunch (npm based) Redis / Sidekiq / Resque OTP
  • 63. EXPLICIT > IMPLICIT or at least some reasonable balance
  • 64. “Functional Programming is about making the complex parts of your program explicit” – José Valim
  • 65. Next steps (for you) • Watch every talk by José Valim & Chris McCord Really, you won’t regret. • Books: Programming Elixir – Dave Thomas Programming Phoenix – Chris McCord, Bruce Tate & José Valim. • Elixir Getting Started Guide (really good!) http://elixir-lang.org/getting-started/introduction.html • Phoenix Guide (really good!) http://www.phoenixframework.org/docs/overview • Elixir Radar (newsletter) http://plataformatec.com.br/elixir-radar • Madrid |> Elixir MeetUp http://www.meetup.com/es-ES/Madrid-Elixir/
  • 66. THANK YOU Questions? Special thanks go to Diacode’s former team: Victor Viruete, Ricardo García, Artur Chruszcz & Bruno Bayón <3 [ now you can blink again ]