SlideShare a Scribd company logo
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 AWS
Allan Denot
 
Ansible and AWS
Ansible and AWSAnsible and AWS
Ansible and AWS
Peter Sankauskas
 
Testing Ansible with Jenkins and Docker
Testing Ansible with Jenkins and DockerTesting Ansible with Jenkins and Docker
Testing Ansible with Jenkins and Docker
Dennis 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 2021
TomStraub5
 
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
 
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
London HashiCorp User Group
 
An Introduction to Kube-Lego
An Introduction to Kube-LegoAn Introduction to Kube-Lego
An Introduction to Kube-Lego
Matthew 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 Group
Orestes Carracedo
 
ElixirConf Lightning Talk: Elixir |> Production
ElixirConf Lightning Talk: Elixir |> ProductionElixirConf Lightning Talk: Elixir |> Production
ElixirConf Lightning Talk: Elixir |> Production
Jeff 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 @WebEngage
Vishal 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 Meetup
Abel Muíño
 
Ansible 2 and Ansible Galaxy 2
Ansible 2 and Ansible Galaxy 2Ansible 2 and Ansible Galaxy 2
Ansible 2 and Ansible Galaxy 2
Jeff Geerling
 
Spinnaker 파트 1
Spinnaker 파트 1Spinnaker 파트 1
Spinnaker 파트 1
Steven 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 Vagrant
Brian 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 Swift
Wan Muzaffar Wan Hashim
 
Kube-AWS
Kube-AWSKube-AWS
Kube-AWS
CoreOS
 
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
Bram Vogelaar
 
Akka.net versus microsoft orleans
Akka.net versus microsoft orleansAkka.net versus microsoft orleans
Akka.net versus microsoft orleans
Bill 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.0
Allan 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 Spring
Joe 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 Rack
sickill
 
Intro to Rack
Intro to RackIntro to Rack
Intro to Rack
Rubyc Slides
 
RoR 101: Session 2
RoR 101: Session 2RoR 101: Session 2
RoR 101: Session 2
Rory Gianni
 
Ruby on Rails - Introduction
Ruby on Rails - IntroductionRuby on Rails - Introduction
Ruby on Rails - Introduction
Vagmi Mudumbai
 
Dev streams2
Dev streams2Dev streams2
Dev streams2
David Mc Donagh
 
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
Daniel Spector
 
Wider than rails
Wider than railsWider than rails
Wider than rails
Alexey Nayden
 
Introduction to Ruby on Rails
Introduction to Ruby on RailsIntroduction to Ruby on Rails
Introduction to Ruby on Rails
Alessandro 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 application
mirrec
 
Rails missing features
Rails missing featuresRails missing features
Rails missing features
Astrails
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - Keynote
Dr 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 Tutorial
Jim Yang
 
Workshop 16: EmberJS Parte I
Workshop 16: EmberJS Parte IWorkshop 16: EmberJS Parte I
Workshop 16: EmberJS Parte I
Visual Engineering
 
Supa fast Ruby + Rails
Supa fast Ruby + RailsSupa fast Ruby + Rails
Supa fast Ruby + Rails
Jean-Baptiste Feldis
 
Curso rails
Curso railsCurso rails
Curso rails
Icalia Labs
 
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
Sebastian Springer
 
Building Cloud Castles
Building Cloud CastlesBuilding Cloud Castles
Building Cloud Castles
Ben 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 nomads
Diacode
 
Ruby on Rails & TDD con RSpec
Ruby on Rails & TDD con RSpecRuby on Rails & TDD con RSpec
Ruby on Rails & TDD con RSpec
Diacode
 
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 Madrid
Diacode
 
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
Diacode
 
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
Diacode
 
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 Thing
Diacode
 
Front-End Frameworks: a quick overview
Front-End Frameworks: a quick overviewFront-End Frameworks: a quick overview
Front-End Frameworks: a quick overview
Diacode
 
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 Rails
Diacode
 

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

DDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systemsDDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systems
Gerardo Pardo-Castellote
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
Peter Muessig
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
Ayan Halder
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Undress Baby
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
Philip Schwarz
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
brainerhub1
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
Yara Milbes
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke
 
E-commerce Application Development Company.pdf
E-commerce Application Development Company.pdfE-commerce Application Development Company.pdf
E-commerce Application Development Company.pdf
Hornet Dynamics
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
Hornet Dynamics
 
Microservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we workMicroservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we work
Sven Peters
 
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
kalichargn70th171
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
SOCRadar
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
rodomar2
 
How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
Rakesh Kumar R
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
ICS
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
Octavian Nadolu
 

Recently uploaded (20)

DDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systemsDDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systems
 
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s EcosystemUI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
UI5con 2024 - Keynote: Latest News about UI5 and it’s Ecosystem
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
 
Unveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdfUnveiling the Advantages of Agile Software Development.pdf
Unveiling the Advantages of Agile Software Development.pdf
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
 
Vitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdfVitthal Shirke Java Microservices Resume.pdf
Vitthal Shirke Java Microservices Resume.pdf
 
E-commerce Application Development Company.pdf
E-commerce Application Development Company.pdfE-commerce Application Development Company.pdf
E-commerce Application Development Company.pdf
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
 
Microservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we workMicroservice Teams - How the cloud changes the way we work
Microservice Teams - How the cloud changes the way we work
 
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
 
socradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdfsocradar-q1-2024-aviation-industry-report.pdf
socradar-q1-2024-aviation-industry-report.pdf
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
 
How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
Webinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for EmbeddedWebinar On-Demand: Using Flutter for Embedded
Webinar On-Demand: Using Flutter for Embedded
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
 

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 ]