SlideShare a Scribd company logo
1 of 87
Download to read offline
HELLO ELIXIR (AND OTP)
Abel Muiño (@amuino) & Rok Biderman (@RokBiderman)
ULTRA SHORT INTROS
Abel Muiño
Lead developer at Cabify. Works with ruby for a living.
abel.muino@cabify.com / @amuino
Rok Biderman
Senior Go developer at Cabify. Has an interesting past position.
Go ask him.
rok.biderman@cabify.com / @RokBiderman
WE ARE HIRING
Ruby, Go, Javascript, Android, iOS
(Just not for Elixir, yet)
HELLO ELIXIR (AND OTP)
Abel Muiño (@amuino) & Rok Biderman (@RokBiderman)
GOALS
➤ Show some code, this is a programming meet up
➤ Share our Elixir learning path
➤ Learn something from feedback and criticism
➤ Hopefully at least one other person will learn one thing
“This is not production code
-Abel Muiño
BUILDING AN
OCR MODULE
Extracting quotes from
memes
TL;DR
http://github.com/amuino/ocr
MIX NEW
$ mix new ocr
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/ocr.ex
* creating test
* creating test/test_helper.exs
* creating test/ocr_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd ocr
mix test
Run "mix help" for more commands.
MIX NEW
$ mix new ocr
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/ocr.ex
* creating test
* creating test/test_helper.exs
* creating test/ocr_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd ocr
mix test
Run "mix help" for more commands.
Project Definition
MIX NEW
$ mix new ocr
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/ocr.ex
* creating test
* creating test/test_helper.exs
* creating test/ocr_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd ocr
mix test
Run "mix help" for more commands.
Project Definition
App config
MIX NEW
$ mix new ocr
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/ocr.ex
* creating test
* creating test/test_helper.exs
* creating test/ocr_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd ocr
mix test
Run "mix help" for more commands.
Project Definition
App config
Main module
MIX NEW
$ mix new ocr
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/ocr.ex
* creating test
* creating test/test_helper.exs
* creating test/ocr_test.exs
Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:
cd ocr
mix test
Run "mix help" for more commands.
Project Definition
App config
Main module
😓Not talking about tests today
THE INTERACTIVE SHELL
$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-
threads:10] [hipe] [kernel-poll:false] [dtrace]
Compiled lib/ocr.ex
Generated ocr app
Consolidated List.Chars
Consolidated Collectable
Consolidated String.Chars
Consolidated Enumerable
Consolidated IEx.Info
Consolidated Inspect
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h()
ENTER for help)
iex(1)> Ocr
Ocr
THE INTERACTIVE SHELL
$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-
threads:10] [hipe] [kernel-poll:false] [dtrace]
Compiled lib/ocr.ex
Generated ocr app
Consolidated List.Chars
Consolidated Collectable
Consolidated String.Chars
Consolidated Enumerable
Consolidated IEx.Info
Consolidated Inspect
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h()
ENTER for help)
iex(1)> Ocr
Ocr
Automatically
compiles new files
THE INTERACTIVE SHELL
$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-
threads:10] [hipe] [kernel-poll:false] [dtrace]
Compiled lib/ocr.ex
Generated ocr app
Consolidated List.Chars
Consolidated Collectable
Consolidated String.Chars
Consolidated Enumerable
Consolidated IEx.Info
Consolidated Inspect
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h()
ENTER for help)
iex(1)> Ocr
Ocr
Automatically
compiles new files
Lots of first-run noise
THE INTERACTIVE SHELL
$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-
threads:10] [hipe] [kernel-poll:false] [dtrace]
Compiled lib/ocr.ex
Generated ocr app
Consolidated List.Chars
Consolidated Collectable
Consolidated String.Chars
Consolidated Enumerable
Consolidated IEx.Info
Consolidated Inspect
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h()
ENTER for help)
iex(1)> Ocr
Ocr
Automatically
compiles new files
Lots of first-run noise
SUCCESS!
Our module exists
💡TIP: TRAINING WHEELS
➤ We are learning, so getting quick feedback is useful
➤ Credo is a static code analysis tool for the Elixir language with a
focus on teaching and code consistency.

https://github.com/rrrene/credo
➤ Credo installs as a project dependency
➤ Adds a new task to mix to analyse our code
➤ Excellent, very detailed, feedback
ADD A DEPENDENCY
Find the dependency info in hex.pm
Edit mix.exs
defp deps do
[
{:credo, "~> 0.3", only: [:dev]}
]
end
Install it locally
$ mix geps.get
ADD A DEPENDENCY
Find the dependency info in hex.pm
Edit mix.exs
defp deps do
[
{:credo, "~> 0.3", only: [:dev]}
]
end
Install it locally
$ mix geps.get
Dependencies are an
array of tuples.
ADD A DEPENDENCY
Find the dependency info in hex.pm
Edit mix.exs
defp deps do
[
{:credo, "~> 0.3", only: [:dev]}
]
end
Install it locally
$ mix geps.get
Dependencies are an
array of tuples.
Only installs the
dependency in the :dev
environment
TRY IT
$ mix credo
…
Code Readability
[R] ! Modules should have a @moduledoc tag.
lib/ocr.ex:1:11 (Ocr)
$ mix credo lib/ocr.ex:1:11
TRY IT
$ mix credo
…
Code Readability
[R] ! Modules should have a @moduledoc tag.
lib/ocr.ex:1:11 (Ocr)
$ mix credo lib/ocr.ex:1:11
OMG!
TRY IT
$ mix credo
…
Code Readability
[R] ! Modules should have a @moduledoc tag.
lib/ocr.ex:1:11 (Ocr)
$ mix credo lib/ocr.ex:1:11
OMG!
Detailed explanation on the
error, how to suppress it,
etc…
NOW WHAT?
➤ Use Google Vision API to perform the actual OCR
➤ Has no client in hex.pm
➤ It is a REST API → {:httpoison, "~> 0.8.3"}
➤ Returns JSON → {:poison, "~> 2.1.0"}
➤ Needs authentication → {:goth, "~> 0.1.2”}
➤ Build a nice façade
MIX.EXS
def application do
[applications: [:logger, :httpoison, :goth]]
end
defp deps do
[
{:httpoison, "~> 0.8.3"},
{:poison, "~> 2.1.0"},
{:goth, "~> 0.1.2"},
{:credo, "~> 0.3", only: [:dev]}
]
end
MIX.EXS
def application do
[applications: [:logger, :httpoison, :goth]]
end
defp deps do
[
{:httpoison, "~> 0.8.3"},
{:poison, "~> 2.1.0"},
{:goth, "~> 0.1.2"},
{:credo, "~> 0.3", only: [:dev]}
]
end
Some deps also need
their app to be started
CONFIG/CONFIG.EXS
use Mix.Config
config :goth,
json: "config/google-creds.json" |> File.read!
CONFIG/CONFIG.EXS
use Mix.Config
config :goth,
json: "config/google-creds.json" |> File.read!
Some deps also have
their own config
NOW WHAT?
➤ We will write 2 modules:
➤ Ocr.GoogleVision for the API client.
➤ Ocr for our façade
💡TIP: MODULE NAMES
➤ Convention:
➤ Ocr ! lib/ocr.ex
➤ Ocr.GoogleVision ! lib/ocr/google_vision.ex
➤ Modules names are just names. Dots in the name do not
represent any parent/child relationship.
LIB/OCR/GOOGLE_VISION.EX
defmodule Ocr.GoogleVision do
def extract_text(image64) do
image64 |> make_request |> read_body
end
# MAGIC!
end
LIB/OCR/GOOGLE_VISION.EX
defmodule Ocr.GoogleVision do
def extract_text(image64) do
image64 |> make_request |> read_body
end
# MAGIC!
end
base64
encoded
image
LIB/OCR/GOOGLE_VISION.EX
defmodule Ocr.GoogleVision do
def extract_text(image64) do
image64 |> make_request |> read_body
end
# MAGIC!
end
base64
encoded
image
send to
Google
LIB/OCR/GOOGLE_VISION.EX
defmodule Ocr.GoogleVision do
def extract_text(image64) do
image64 |> make_request |> read_body
end
# MAGIC!
end
base64
encoded
image
send to
Google
get the text
from the
response
LIB/OCR/GOOGLE_VISION.EX
@url "https://vision.googleapis.com/v1/images:annotate"
@feature_text_detection "TEXT_DETECTION"
@auth_scope "https://www.googleapis.com/auth/cloud-platform"
def make_request(image64) do
HTTPoison.post!(@url, payload(image64), headers)
end
defp payload(image64) do
%{requests: [
%{image: %{content: image64},
features: [%{type: @feature_text_detection}]}
]
} |> Poison.encode!
end
defp headers do
{:ok, token} = Goth.Token.for_scope(@auth_scope)
[{"Authorization", "#{token.type} #{token.token}"}]
end
LIB/OCR/GOOGLE_VISION.EX
@url "https://vision.googleapis.com/v1/images:annotate"
@feature_text_detection "TEXT_DETECTION"
@auth_scope "https://www.googleapis.com/auth/cloud-platform"
def make_request(image64) do
HTTPoison.post!(@url, payload(image64), headers)
end
defp payload(image64) do
%{requests: [
%{image: %{content: image64},
features: [%{type: @feature_text_detection}]}
]
} |> Poison.encode!
end
defp headers do
{:ok, token} = Goth.Token.for_scope(@auth_scope)
[{"Authorization", "#{token.type} #{token.token}"}]
end
Module attributes (used as a constants)
LIB/OCR/GOOGLE_VISION.EX
@url "https://vision.googleapis.com/v1/images:annotate"
@feature_text_detection "TEXT_DETECTION"
@auth_scope "https://www.googleapis.com/auth/cloud-platform"
def make_request(image64) do
HTTPoison.post!(@url, payload(image64), headers)
end
defp payload(image64) do
%{requests: [
%{image: %{content: image64},
features: [%{type: @feature_text_detection}]}
]
} |> Poison.encode!
end
defp headers do
{:ok, token} = Goth.Token.for_scope(@auth_scope)
[{"Authorization", "#{token.type} #{token.token}"}]
end
Module attributes (used as a constants)
HTTP POST some JSON to some URL
with some Headers
LIB/OCR/GOOGLE_VISION.EX
@url "https://vision.googleapis.com/v1/images:annotate"
@feature_text_detection "TEXT_DETECTION"
@auth_scope "https://www.googleapis.com/auth/cloud-platform"
def make_request(image64) do
HTTPoison.post!(@url, payload(image64), headers)
end
defp payload(image64) do
%{requests: [
%{image: %{content: image64},
features: [%{type: @feature_text_detection}]}
]
} |> Poison.encode!
end
defp headers do
{:ok, token} = Goth.Token.for_scope(@auth_scope)
[{"Authorization", "#{token.type} #{token.token}"}]
end
Module attributes (used as a constants)
HTTP POST some JSON to some URL
with some Headers
The JSON Google wants
LIB/OCR/GOOGLE_VISION.EX
@url "https://vision.googleapis.com/v1/images:annotate"
@feature_text_detection "TEXT_DETECTION"
@auth_scope "https://www.googleapis.com/auth/cloud-platform"
def make_request(image64) do
HTTPoison.post!(@url, payload(image64), headers)
end
defp payload(image64) do
%{requests: [
%{image: %{content: image64},
features: [%{type: @feature_text_detection}]}
]
} |> Poison.encode!
end
defp headers do
{:ok, token} = Goth.Token.for_scope(@auth_scope)
[{"Authorization", "#{token.type} #{token.token}"}]
end
Module attributes (used as a constants)
HTTP POST some JSON to some URL
with some Headers
The JSON Google wants
Get a token
LIB/OCR/GOOGLE_VISION.EX
@url "https://vision.googleapis.com/v1/images:annotate"
@feature_text_detection "TEXT_DETECTION"
@auth_scope "https://www.googleapis.com/auth/cloud-platform"
def make_request(image64) do
HTTPoison.post!(@url, payload(image64), headers)
end
defp payload(image64) do
%{requests: [
%{image: %{content: image64},
features: [%{type: @feature_text_detection}]}
]
} |> Poison.encode!
end
defp headers do
{:ok, token} = Goth.Token.for_scope(@auth_scope)
[{"Authorization", "#{token.type} #{token.token}"}]
end
Module attributes (used as a constants)
HTTP POST some JSON to some URL
with some Headers
The JSON Google wants
Get a token
Put the token on the request headers
LIB/OCR/GOOGLE_VISION.EX
def read_body(%HTTPoison.Response{body: body, status_code: 200})
do
body |>
Poison.decode! |>
get_in(["responses", &first/3, "textAnnotations",
&first/3, "description"])
end
defp first(:get, nil, _), do: nil
defp first(:get, data, next) do
data |> List.first |> next.()
end
LIB/OCR/GOOGLE_VISION.EX
def read_body(%HTTPoison.Response{body: body, status_code: 200})
do
body |>
Poison.decode! |>
get_in(["responses", &first/3, "textAnnotations",
&first/3, "description"])
end
defp first(:get, nil, _), do: nil
defp first(:get, data, next) do
data |> List.first |> next.()
end
Only care about body and success http status
LIB/OCR/GOOGLE_VISION.EX
def read_body(%HTTPoison.Response{body: body, status_code: 200})
do
body |>
Poison.decode! |>
get_in(["responses", &first/3, "textAnnotations",
&first/3, "description"])
end
defp first(:get, nil, _), do: nil
defp first(:get, data, next) do
data |> List.first |> next.()
end
Only care about body and success http status
Parse JSON response
LIB/OCR/GOOGLE_VISION.EX
def read_body(%HTTPoison.Response{body: body, status_code: 200})
do
body |>
Poison.decode! |>
get_in(["responses", &first/3, "textAnnotations",
&first/3, "description"])
end
defp first(:get, nil, _), do: nil
defp first(:get, data, next) do
data |> List.first |> next.()
end
Only care about body and success http status
Parse JSON response
Extract the text
LIB/OCR/GOOGLE_VISION.EX
def read_body(%HTTPoison.Response{body: body, status_code: 200})
do
body |>
Poison.decode! |>
get_in(["responses", &first/3, "textAnnotations",
&first/3, "description"])
end
defp first(:get, nil, _), do: nil
defp first(:get, data, next) do
data |> List.first |> next.()
end
Only care about body and success http status
Parse JSON response
Extract the text
Custom lookup functions
LIB/OCR/GOOGLE_VISION.EX
def read_body(%HTTPoison.Response{body: body, status_code: 200})
do
body |>
Poison.decode! |>
get_in(["responses", &first/3, "textAnnotations",
&first/3, "description"])
end
defp first(:get, nil, _), do: nil
defp first(:get, data, next) do
data |> List.first |> next.()
end
Only care about body and success http status
Parse JSON response
Extract the text
Custom lookup functions
funky syntax to invoke an
anonymous function
💡TIP: GET_IN IS AWESOME
➤ Navigates nested structures (maps)
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_in(users, ["john", :age])
27
➤ Returns nil on missing keys
iex> get_in(users, ["unknown", :age])
nil
➤ Accepts functions for navigating other types
➤ Elixir 1.3 will have common functions predefined for tuples
and lists
➤ Access.at(0) will replace my custom first (PR #4719)
➤ Also check get_and_update_in, put_in, update_in
LIB/OCR.EX
defmodule Ocr do
def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64)
def from_image(image_data) do
image_data |> Base.encode64 |> from_base64
end
def from_path(path), do: path |> File.read! |> from_image
def from_url(url), do: HTTPoison.get!(url).body |> from_image
end
LIB/OCR.EX
defmodule Ocr do
def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64)
def from_image(image_data) do
image_data |> Base.encode64 |> from_base64
end
def from_path(path), do: path |> File.read! |> from_image
def from_url(url), do: HTTPoison.get!(url).body |> from_image
end
LIB/OCR.EX
defmodule Ocr do
def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64)
def from_image(image_data) do
image_data |> Base.encode64 |> from_base64
end
def from_path(path), do: path |> File.read! |> from_image
def from_url(url), do: HTTPoison.get!(url).body |> from_image
end
LIB/OCR.EX
defmodule Ocr do
def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64)
def from_image(image_data) do
image_data |> Base.encode64 |> from_base64
end
def from_path(path), do: path |> File.read! |> from_image
def from_url(url), do: HTTPoison.get!(url).body |> from_image
end
FUN!
$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:
10] [hipe] [kernel-poll:false] [dtrace]
A new Hex version is available (0.12.0), please update with `mix
local.hex`
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for
help)
iex(1)> meme_url = "http://ih0.redbubble.net/image.16611809.2383/fc,
550x550,black.jpg"
"http://ih0.redbubble.net/image.16611809.2383/fc,550x550,black.jpg"
iex(2)> IO.puts Ocr.from_url meme_url
GETS ELIKIR PR ACCEPTED
I SAID WHO WANTS TO
FUCKING TOUCH ME?
Suranyami
:ok
FUN!
$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:
10] [hipe] [kernel-poll:false] [dtrace]
A new Hex version is available (0.12.0), please update with `mix
local.hex`
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for
help)
iex(1)> meme_url = "http://ih0.redbubble.net/image.16611809.2383/fc,
550x550,black.jpg"
"http://ih0.redbubble.net/image.16611809.2383/fc,550x550,black.jpg"
iex(2)> IO.puts Ocr.from_url meme_url
GETS ELIKIR PR ACCEPTED
I SAID WHO WANTS TO
FUCKING TOUCH ME?
Suranyami
:ok
STATEFUL? STATELESS?
Cast your vote!
ANSWER: STATEFUL
➤ Auth tokens are not requested every time
➤ Requested on first use
➤ Refreshed on the background when about to expire
➤ Goth.TokenStore is a GenServer
➤ Just one of the predefined behaviours to make easier to
work with processes
➤ Starts a process with some state
➤ Receives messages and updates the state
➤ There is more state (like Goth.Config)
DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX
defmodule Goth.TokenStore do
use Xenserver
alias Goth.Token
def start_link do
GenServer.start_link(__MODULE__, %{}, [name: __MODULE__])
end
def handle_call({:store, scope, token}, _from, state) do
pid_or_timer = Token.queue_for_refresh(token)
{:reply, pid_or_timer, Map.put(state, scope, token)}
end
def handle_call({:find, scope}, _from, state) do
{:reply, Map.fetch(state, scope), state}
end
end
DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX
defmodule Goth.TokenStore do
use Xenserver
alias Goth.Token
def start_link do
GenServer.start_link(__MODULE__, %{}, [name: __MODULE__])
end
def handle_call({:store, scope, token}, _from, state) do
pid_or_timer = Token.queue_for_refresh(token)
{:reply, pid_or_timer, Map.put(state, scope, token)}
end
def handle_call({:find, scope}, _from, state) do
{:reply, Map.fetch(state, scope), state}
end
end
Start the process,
DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX
defmodule Goth.TokenStore do
use Xenserver
alias Goth.Token
def start_link do
GenServer.start_link(__MODULE__, %{}, [name: __MODULE__])
end
def handle_call({:store, scope, token}, _from, state) do
pid_or_timer = Token.queue_for_refresh(token)
{:reply, pid_or_timer, Map.put(state, scope, token)}
end
def handle_call({:find, scope}, _from, state) do
{:reply, Map.fetch(state, scope), state}
end
end
Start the process, Initial state is an empty map
DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX
defmodule Goth.TokenStore do
use Xenserver
alias Goth.Token
def start_link do
GenServer.start_link(__MODULE__, %{}, [name: __MODULE__])
end
def handle_call({:store, scope, token}, _from, state) do
pid_or_timer = Token.queue_for_refresh(token)
{:reply, pid_or_timer, Map.put(state, scope, token)}
end
def handle_call({:find, scope}, _from, state) do
{:reply, Map.fetch(state, scope), state}
end
end
Start the process, Initial state is an empty map
The process has a name
DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX
defmodule Goth.TokenStore do
use Xenserver
alias Goth.Token
def start_link do
GenServer.start_link(__MODULE__, %{}, [name: __MODULE__])
end
def handle_call({:store, scope, token}, _from, state) do
pid_or_timer = Token.queue_for_refresh(token)
{:reply, pid_or_timer, Map.put(state, scope, token)}
end
def handle_call({:find, scope}, _from, state) do
{:reply, Map.fetch(state, scope), state}
end
end
Start the process, Initial state is an empty map
The process has a name
Handle 2 types of messages, returning something
FUN WITH TOKEN STORE
$ iex -S mix
iex(1)> token = %Goth.Token{token: "FAKE",
expires: :os.system_time + 10_000_000}
%Goth.Token{expires: 1464647952951907000, scope: nil,
token: "FAKE", type: nil}
iex(2)> GenServer.call Goth.TokenStore, {:find, "Elixir"}
:error
iex(3)> GenServer.call Goth.TokenStore, {:store, "Elixir",
token}
{:ok, {1464647950910798703208727, #Reference<0.0.7.228>}}
iex(4)> GenServer.call Goth.TokenStore, {:find, "Elixir"}
{:ok,
%Goth.Token{expires: 1464647952951907000, scope: nil,
token: “FAKE", type: nil}}
FUN WITH TOKEN STORE
$ iex -S mix
iex(1)> token = %Goth.Token{token: "FAKE",
expires: :os.system_time + 10_000_000}
%Goth.Token{expires: 1464647952951907000, scope: nil,
token: "FAKE", type: nil}
iex(2)> GenServer.call Goth.TokenStore, {:find, "Elixir"}
:error
iex(3)> GenServer.call Goth.TokenStore, {:store, "Elixir",
token}
{:ok, {1464647950910798703208727, #Reference<0.0.7.228>}}
iex(4)> GenServer.call Goth.TokenStore, {:find, "Elixir"}
{:ok,
%Goth.Token{expires: 1464647952951907000, scope: nil,
token: “FAKE", type: nil}}
The process name
FUN WITH TOKEN STORE
$ iex -S mix
iex(1)> token = %Goth.Token{token: "FAKE",
expires: :os.system_time + 10_000_000}
%Goth.Token{expires: 1464647952951907000, scope: nil,
token: "FAKE", type: nil}
iex(2)> GenServer.call Goth.TokenStore, {:find, "Elixir"}
:error
iex(3)> GenServer.call Goth.TokenStore, {:store, "Elixir",
token}
{:ok, {1464647950910798703208727, #Reference<0.0.7.228>}}
iex(4)> GenServer.call Goth.TokenStore, {:find, "Elixir"}
{:ok,
%Goth.Token{expires: 1464647952951907000, scope: nil,
token: “FAKE", type: nil}}
The process name
The message
DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX
defmodule Goth.TokenStore do
def store(%Token{}=token), do: store(token.scope, token)
def store(scopes, %Token{} = token) do
GenServer.call(__MODULE__, {:store, scopes, token})
end
def find(scope) do
GenServer.call(__MODULE__, {:find, scope})
end
end
DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX
defmodule Goth.TokenStore do
def store(%Token{}=token), do: store(token.scope, token)
def store(scopes, %Token{} = token) do
GenServer.call(__MODULE__, {:store, scopes, token})
end
def find(scope) do
GenServer.call(__MODULE__, {:find, scope})
end
end
Provide a client API (usually in the same module)
with nicer methods hiding the use of
Genserver.call
LET’S TALK ABOUT OTP
Processes, state, concurrency, supervisors,… oh my!
RECAP
➤ Erlang is:
➤ general-purpose
➤ concurrent
➤ garbage-collected
➤ programming language
➤ and runtime system
➤ Elixir:
➤ Builds on top of all that
START A PROCESS
➤ Basic concurrency primitive
➤ Simplest way to create, use spawn with a function
defmodule BasicMessagePassing.Call do
def concat(a, b) do
IO.puts("#{a} #{b}")
end
end
iex(2)> BasicMessagePassing.Call.concat "Elixir", "Madrid"
Elixir Madrid
:ok
iex(3)> spawn BasicMessagePassing.Call, :concat, ["Elixir", "Madrid"]
Elixir Madrid
#PID<0.69.0>
START A PROCESS
➤ Basic concurrency primitive
➤ Simplest way to create, use spawn with a function
defmodule BasicMessagePassing.Call do
def concat(a, b) do
IO.puts("#{a} #{b}")
end
end
iex(2)> BasicMessagePassing.Call.concat "Elixir", "Madrid"
Elixir Madrid
:ok
iex(3)> spawn BasicMessagePassing.Call, :concat, ["Elixir", "Madrid"]
Elixir Madrid
#PID<0.69.0>
Same process
START A PROCESS
➤ Basic concurrency primitive
➤ Simplest way to create, use spawn with a function
defmodule BasicMessagePassing.Call do
def concat(a, b) do
IO.puts("#{a} #{b}")
end
end
iex(2)> BasicMessagePassing.Call.concat "Elixir", "Madrid"
Elixir Madrid
:ok
iex(3)> spawn BasicMessagePassing.Call, :concat, ["Elixir", "Madrid"]
Elixir Madrid
#PID<0.69.0>
Same process
Spawned process id
LISTEN FOR MESSAGES
defmodule BasicMessagePassing.Listen do
def listen do
receive do
{:ok, input} -> IO.puts "#{input} Madrid"
end
end
end
iex(5)> pid = spawn(BasicMessagePassing.Listen, :listen, [])
#PID<0.82.0>
iex(6)> send pid, {:ok, "Elixir"}
Elixir Madrid
{:ok, "Elixir"}
iex(8)> Process.alive? pid
false
FIBONACCI TIME!
defmodule FibSerial do
def calculate(ns) do
ns
|> Enum.map(&(calc(&1)))
|> inspect
|> IO.puts
end
def calc(n) do
calc(n, 1, 0)
end
defp calc(0, _, _) do
0
end
defp calc(1, a, b) do
a + b
end
defp calc(n, a, b) do
calc(n - 1, b, a + b)
end
end
FibSerial.calculate(Enum.to_list(1..10000))
FIBONACCI TIME!
defmodule FibSerial do
def calculate(ns) do
ns
|> Enum.map(&(calc(&1)))
|> inspect
|> IO.puts
end
def calc(n) do
calc(n, 1, 0)
end
defp calc(0, _, _) do
0
end
defp calc(1, a, b) do
a + b
end
defp calc(n, a, b) do
calc(n - 1, b, a + b)
end
end
FibSerial.calculate(Enum.to_list(1..10000)) About 6 seconds
PARALLEL FIBONACCI TIME!
defmodule FibParallel do
def calculate(ns) do
ns
|> Enum.with_index
|> Enum.map(fn(ni) -> spawn FibParallel, :send_calc, [self, ni] end)
listen(length(ns), [])
end
def send_calc(pid, {n, i}) do
send pid, {calc(n), i}
end
defp listen(lns, result) do
receive do
fib ->
result = [fib | result]
if lns == 1 do
result
|> Enum.sort(fn({_, a}, {_, b}) -> a < b end)
|> Enum.map(fn({f, _}) -> f end)
|> inspect
|> IO.puts
else
listen(lns - 1, result)
end
end
end
end
FibSerial.calculate(Enum.to_list(1..10000))
PARALLEL FIBONACCI TIME!
defmodule FibParallel do
def calculate(ns) do
ns
|> Enum.with_index
|> Enum.map(fn(ni) -> spawn FibParallel, :send_calc, [self, ni] end)
listen(length(ns), [])
end
def send_calc(pid, {n, i}) do
send pid, {calc(n), i}
end
defp listen(lns, result) do
receive do
fib ->
result = [fib | result]
if lns == 1 do
result
|> Enum.sort(fn({_, a}, {_, b}) -> a < b end)
|> Enum.map(fn({f, _}) -> f end)
|> inspect
|> IO.puts
else
listen(lns - 1, result)
end
end
end
end
FibSerial.calculate(Enum.to_list(1..10000)) About 2 seconds (4 cores)
LINKING PROCESSES
➤ If child dies, parent dies
defmodule BasicMessagePassing.Linking do
def exit, do: exit(:crash)
def start do
spawn_link(BasicMessagePassing.Linking, :exit, [])
receive do
{:done} -> IO.puts "no more waiting"
end
end
end
iex(13)> BasicMessagePassing.Linking.start
** (EXIT from #PID<0.57.0>) :crash
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for
help)
iex(1)>
LINKING PROCESSES
➤ If child dies, parent dies
defmodule BasicMessagePassing.Linking do
def exit, do: exit(:crash)
def start do
spawn_link(BasicMessagePassing.Linking, :exit, [])
receive do
{:done} -> IO.puts "no more waiting"
end
end
end
iex(13)> BasicMessagePassing.Linking.start
** (EXIT from #PID<0.57.0>) :crash
Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for
help)
iex(1)>
iex died! and was restarted
LINKING PROCESSES
➤ If child dies, parent dies… unless it handles the dead
defmodule BasicMessagePassing.Linking do
def exit, do: exit(:crash)
def start do
Process.flag(:trap_exit, true)
spawn_link(BasicMessagePassing.Linking, :exit, [])
receive do
{:EXIT, from_pid, reason} -> IO.puts "#{inspect(self)} is
aware #{inspect(from_pid)} exited because of #{reason}"
end
end
end
iex(24)> BasicMessagePassing.Linking.start
#PID<0.57.0> is aware #PID<0.157.0> exited because of crash
:ok
LINKING PROCESSES
➤ If child dies, parent dies… unless it handles the dead
defmodule BasicMessagePassing.Linking do
def exit, do: exit(:crash)
def start do
Process.flag(:trap_exit, true)
spawn_link(BasicMessagePassing.Linking, :exit, [])
receive do
{:EXIT, from_pid, reason} -> IO.puts "#{inspect(self)} is
aware #{inspect(from_pid)} exited because of #{reason}"
end
end
end
iex(24)> BasicMessagePassing.Linking.start
#PID<0.57.0> is aware #PID<0.157.0> exited because of crash
:ok
survive children
LINKING PROCESSES
➤ If child dies, parent dies… unless it handles the dead
defmodule BasicMessagePassing.Linking do
def exit, do: exit(:crash)
def start do
Process.flag(:trap_exit, true)
spawn_link(BasicMessagePassing.Linking, :exit, [])
receive do
{:EXIT, from_pid, reason} -> IO.puts "#{inspect(self)} is
aware #{inspect(from_pid)} exited because of #{reason}"
end
end
end
iex(24)> BasicMessagePassing.Linking.start
#PID<0.57.0> is aware #PID<0.157.0> exited because of crash
:ok
survive children
handle deads
GENSERVER
➤ Simplifies all this stuff
➤ Is a process like any other Elixir process
➤ Standard set of interface functions, tracing and error reporting
➤ call: request with response
➤ cast: request without response
A STACK
defmodule Stack do
use GenServer
def start_link(state, opts  []) do
GenServer.start_link(__MODULE__, state, opts)
end
def handle_call(:pop, _from, [h|t]) do
{:reply, h, t}
end
def handle_cast({:push, h}, t) do
{:noreply, [h|t]}
end
end
A SUPERVISED STACK
iex(7)> import Supervisor.Spec
nil
iex(8)> children = [
...(8)> worker(Stack, [[:first], [name: :stack_name]])
...(8)> ]
[{Stack, {Stack, :start_link, [[:first], [name: :stack_name]]}, :permanent,
5000, :worker, [Stack]}]
iex(9)> {:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)
{:ok, #PID<0.247.0>}
iex(10)> GenServer.call(:stack_name, :pop)
:first
iex(11)> GenServer.call(:stack_name, :pop)
18:04:35.012 [error] GenServer :stack_name terminating
** (FunctionClauseError) no function clause matching in Stack.handle_call/3
iex:13: Stack.handle_call(:pop, {#PID<0.135.0>, #Reference<0.0.1.317>}, [])
[..]
Last message: :pop
State: []
(elixir) lib/gen_server.ex:564: GenServer.call/3
iex(11)> GenServer.call(:stack_name, :pop)
:first
SUPERVISOR FLAVORS
➤ one_for_one: dead worker is replaced by another one
➤ rest_for_all: after one dies, all others have to be restarted
➤ rest_for_one: all workers started after this one will be
restarted
➤ simple_one_for_one: for dynamically attached children,
Supervisor is required to contain only one child
QUESTIONS?
THANKS!
Abel Muiño (@amuino) & Rok Biderman (@RokBiderman)

More Related Content

What's hot

Learning Elixir as a Rubyist
Learning Elixir as a RubyistLearning Elixir as a Rubyist
Learning Elixir as a RubyistAlex Kira
 
Actor Clustering with Docker Containers and Akka.Net in F#
Actor Clustering with Docker Containers and Akka.Net in F#Actor Clustering with Docker Containers and Akka.Net in F#
Actor Clustering with Docker Containers and Akka.Net in F#Riccardo Terrell
 
Akka.net versus microsoft orleans
Akka.net versus microsoft orleansAkka.net versus microsoft orleans
Akka.net versus microsoft orleansBill Tulloch
 
Elixir and Phoenix for Rubyists
Elixir and Phoenix for RubyistsElixir and Phoenix for Rubyists
Elixir and Phoenix for RubyistsBrooklyn Zelenka
 
Introduction to Ansible (Pycon7 2016)
Introduction to Ansible (Pycon7 2016)Introduction to Ansible (Pycon7 2016)
Introduction to Ansible (Pycon7 2016)Ivan Rossi
 
Intro to React
Intro to ReactIntro to React
Intro to ReactTroy Miles
 
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
 
KKBOX WWDC17 Xcode debug - Oliver
KKBOX WWDC17  Xcode debug - OliverKKBOX WWDC17  Xcode debug - Oliver
KKBOX WWDC17 Xcode debug - OliverLiyao Chen
 
Automating the Cloud with Terraform, and Ansible
Automating the Cloud with Terraform, and AnsibleAutomating the Cloud with Terraform, and Ansible
Automating the Cloud with Terraform, and AnsibleBrian Hogan
 
Ansible on aws - Pop-up Loft Tel Aviv
Ansible on aws - Pop-up Loft Tel AvivAnsible on aws - Pop-up Loft Tel Aviv
Ansible on aws - Pop-up Loft Tel AvivAmazon Web Services
 
Tame cloud complexity with F# powered DSLs (build stuff)
Tame cloud complexity with F# powered DSLs (build stuff)Tame cloud complexity with F# powered DSLs (build stuff)
Tame cloud complexity with F# powered DSLs (build stuff)Yan Cui
 

What's hot (20)

Learning Elixir as a Rubyist
Learning Elixir as a RubyistLearning Elixir as a Rubyist
Learning Elixir as a Rubyist
 
Actor Clustering with Docker Containers and Akka.Net in F#
Actor Clustering with Docker Containers and Akka.Net in F#Actor Clustering with Docker Containers and Akka.Net in F#
Actor Clustering with Docker Containers and Akka.Net in F#
 
Akka.net versus microsoft orleans
Akka.net versus microsoft orleansAkka.net versus microsoft orleans
Akka.net versus microsoft orleans
 
Erlang and Elixir
Erlang and ElixirErlang and Elixir
Erlang and Elixir
 
Elixir and Phoenix for Rubyists
Elixir and Phoenix for RubyistsElixir and Phoenix for Rubyists
Elixir and Phoenix for Rubyists
 
Atmosphere 2014
Atmosphere 2014Atmosphere 2014
Atmosphere 2014
 
Concurrecny inf sharp
Concurrecny inf sharpConcurrecny inf sharp
Concurrecny inf sharp
 
Introduction to Ansible (Pycon7 2016)
Introduction to Ansible (Pycon7 2016)Introduction to Ansible (Pycon7 2016)
Introduction to Ansible (Pycon7 2016)
 
Intro to React
Intro to ReactIntro to React
Intro to React
 
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
 
Wider than rails
Wider than railsWider than rails
Wider than rails
 
Mini Rails Framework
Mini Rails FrameworkMini Rails Framework
Mini Rails Framework
 
Phoenix Framework
Phoenix FrameworkPhoenix Framework
Phoenix Framework
 
Designing net-aws-glacier
Designing net-aws-glacierDesigning net-aws-glacier
Designing net-aws-glacier
 
KKBOX WWDC17 Xcode debug - Oliver
KKBOX WWDC17  Xcode debug - OliverKKBOX WWDC17  Xcode debug - Oliver
KKBOX WWDC17 Xcode debug - Oliver
 
infra-as-code
infra-as-codeinfra-as-code
infra-as-code
 
effective_r27
effective_r27effective_r27
effective_r27
 
Automating the Cloud with Terraform, and Ansible
Automating the Cloud with Terraform, and AnsibleAutomating the Cloud with Terraform, and Ansible
Automating the Cloud with Terraform, and Ansible
 
Ansible on aws - Pop-up Loft Tel Aviv
Ansible on aws - Pop-up Loft Tel AvivAnsible on aws - Pop-up Loft Tel Aviv
Ansible on aws - Pop-up Loft Tel Aviv
 
Tame cloud complexity with F# powered DSLs (build stuff)
Tame cloud complexity with F# powered DSLs (build stuff)Tame cloud complexity with F# powered DSLs (build stuff)
Tame cloud complexity with F# powered DSLs (build stuff)
 

Viewers also liked

How Elixir helped us scale our Video User Profile Service for the Olympics
How Elixir helped us scale our Video User Profile Service for the OlympicsHow Elixir helped us scale our Video User Profile Service for the Olympics
How Elixir helped us scale our Video User Profile Service for the OlympicsEmerson Macedo
 
Elixir – Peeking into Elixir's Processes, OTP and Supervisors
Elixir – Peeking into Elixir's Processes, OTP and SupervisorsElixir – Peeking into Elixir's Processes, OTP and Supervisors
Elixir – Peeking into Elixir's Processes, OTP and SupervisorsBenjamin Tan
 
Mad scalability: Scaling when you are not Google
Mad scalability: Scaling when you are not GoogleMad scalability: Scaling when you are not Google
Mad scalability: Scaling when you are not GoogleAbel Muíño
 
Intro to elixir and phoenix
Intro to elixir and phoenixIntro to elixir and phoenix
Intro to elixir and phoenixJared Smith
 
Elixir - Easy fun for busy developers @ Devoxx 2016
Elixir - Easy fun for busy developers @ Devoxx 2016Elixir - Easy fun for busy developers @ Devoxx 2016
Elixir - Easy fun for busy developers @ Devoxx 2016David Schmitz
 
Brief Intro to Phoenix - Elixir Meetup at BukaLapak
Brief Intro to Phoenix - Elixir Meetup at BukaLapakBrief Intro to Phoenix - Elixir Meetup at BukaLapak
Brief Intro to Phoenix - Elixir Meetup at BukaLapakRiza Fahmi
 
Flow-based programming with Elixir
Flow-based programming with ElixirFlow-based programming with Elixir
Flow-based programming with ElixirAnton Mishchuk
 
Elixir & Phoenix 推坑
Elixir & Phoenix 推坑Elixir & Phoenix 推坑
Elixir & Phoenix 推坑Chao-Ju Huang
 
Build Your Own Real-Time Web Service with Elixir Phoenix
Build Your Own Real-Time Web Service with Elixir PhoenixBuild Your Own Real-Time Web Service with Elixir Phoenix
Build Your Own Real-Time Web Service with Elixir PhoenixChi-chi Ekweozor
 
Learn Elixir at Manchester Lambda Lounge
Learn Elixir at Manchester Lambda LoungeLearn Elixir at Manchester Lambda Lounge
Learn Elixir at Manchester Lambda LoungeChi-chi Ekweozor
 
ITB2016 - Mixing up the front end with ColdBox elixir
ITB2016 - Mixing up the front end with ColdBox elixirITB2016 - Mixing up the front end with ColdBox elixir
ITB2016 - Mixing up the front end with ColdBox elixirOrtus Solutions, Corp
 
Maven 3… so what?
Maven 3… so what?Maven 3… so what?
Maven 3… so what?Abel Muíño
 
BioContainers on ELIXIR All Hands 2017
BioContainers on ELIXIR All Hands 2017BioContainers on ELIXIR All Hands 2017
BioContainers on ELIXIR All Hands 2017Yasset Perez-Riverol
 
Clojure made-simple - John Stevenson
Clojure made-simple - John StevensonClojure made-simple - John Stevenson
Clojure made-simple - John StevensonJAX London
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)Pavlo Baron
 
Messaging With Erlang And Jabber
Messaging With  Erlang And  JabberMessaging With  Erlang And  Jabber
Messaging With Erlang And Jabberl xf
 
Winning the Erlang Edit•Build•Test Cycle
Winning the Erlang Edit•Build•Test CycleWinning the Erlang Edit•Build•Test Cycle
Winning the Erlang Edit•Build•Test CycleRusty Klophaus
 

Viewers also liked (20)

Elixir intro
Elixir introElixir intro
Elixir intro
 
How Elixir helped us scale our Video User Profile Service for the Olympics
How Elixir helped us scale our Video User Profile Service for the OlympicsHow Elixir helped us scale our Video User Profile Service for the Olympics
How Elixir helped us scale our Video User Profile Service for the Olympics
 
Elixir basics-2
Elixir basics-2Elixir basics-2
Elixir basics-2
 
Elixir – Peeking into Elixir's Processes, OTP and Supervisors
Elixir – Peeking into Elixir's Processes, OTP and SupervisorsElixir – Peeking into Elixir's Processes, OTP and Supervisors
Elixir – Peeking into Elixir's Processes, OTP and Supervisors
 
Mad scalability: Scaling when you are not Google
Mad scalability: Scaling when you are not GoogleMad scalability: Scaling when you are not Google
Mad scalability: Scaling when you are not Google
 
Intro to elixir and phoenix
Intro to elixir and phoenixIntro to elixir and phoenix
Intro to elixir and phoenix
 
Elixir - Easy fun for busy developers @ Devoxx 2016
Elixir - Easy fun for busy developers @ Devoxx 2016Elixir - Easy fun for busy developers @ Devoxx 2016
Elixir - Easy fun for busy developers @ Devoxx 2016
 
Brief Intro to Phoenix - Elixir Meetup at BukaLapak
Brief Intro to Phoenix - Elixir Meetup at BukaLapakBrief Intro to Phoenix - Elixir Meetup at BukaLapak
Brief Intro to Phoenix - Elixir Meetup at BukaLapak
 
Flow-based programming with Elixir
Flow-based programming with ElixirFlow-based programming with Elixir
Flow-based programming with Elixir
 
Elixir & Phoenix 推坑
Elixir & Phoenix 推坑Elixir & Phoenix 推坑
Elixir & Phoenix 推坑
 
Build Your Own Real-Time Web Service with Elixir Phoenix
Build Your Own Real-Time Web Service with Elixir PhoenixBuild Your Own Real-Time Web Service with Elixir Phoenix
Build Your Own Real-Time Web Service with Elixir Phoenix
 
Learn Elixir at Manchester Lambda Lounge
Learn Elixir at Manchester Lambda LoungeLearn Elixir at Manchester Lambda Lounge
Learn Elixir at Manchester Lambda Lounge
 
ITB2016 - Mixing up the front end with ColdBox elixir
ITB2016 - Mixing up the front end with ColdBox elixirITB2016 - Mixing up the front end with ColdBox elixir
ITB2016 - Mixing up the front end with ColdBox elixir
 
Maven 3… so what?
Maven 3… so what?Maven 3… so what?
Maven 3… so what?
 
BioContainers on ELIXIR All Hands 2017
BioContainers on ELIXIR All Hands 2017BioContainers on ELIXIR All Hands 2017
BioContainers on ELIXIR All Hands 2017
 
Clojure made-simple - John Stevenson
Clojure made-simple - John StevensonClojure made-simple - John Stevenson
Clojure made-simple - John Stevenson
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)
 
Messaging With Erlang And Jabber
Messaging With  Erlang And  JabberMessaging With  Erlang And  Jabber
Messaging With Erlang And Jabber
 
High Performance Erlang
High  Performance  ErlangHigh  Performance  Erlang
High Performance Erlang
 
Winning the Erlang Edit•Build•Test Cycle
Winning the Erlang Edit•Build•Test CycleWinning the Erlang Edit•Build•Test Cycle
Winning the Erlang Edit•Build•Test Cycle
 

Similar to Hello elixir (and otp)

Docker for developers on mac and windows
Docker for developers on mac and windowsDocker for developers on mac and windows
Docker for developers on mac and windowsDocker, Inc.
 
Parse cloud code
Parse cloud codeParse cloud code
Parse cloud code維佋 唐
 
State ofappdevelopment
State ofappdevelopmentState ofappdevelopment
State ofappdevelopmentgillygize
 
SECCOM 2017 - Conan.io o gerente de pacote para C e C++
SECCOM 2017 - Conan.io o gerente de pacote para C e C++SECCOM 2017 - Conan.io o gerente de pacote para C e C++
SECCOM 2017 - Conan.io o gerente de pacote para C e C++Uilian Ries
 
NLUUG Spring 2012 - OpenShift Primer
NLUUG Spring 2012 - OpenShift PrimerNLUUG Spring 2012 - OpenShift Primer
NLUUG Spring 2012 - OpenShift PrimerEric D. Schabell
 
Going open source with small teams
Going open source with small teamsGoing open source with small teams
Going open source with small teamsJamie Thomas
 
Deliver Python Apps with Docker
Deliver Python Apps with DockerDeliver Python Apps with Docker
Deliver Python Apps with DockerAnton Egorov
 
Plug yourself in and your app will never be the same (1 hr edition)
Plug yourself in and your app will never be the same (1 hr edition)Plug yourself in and your app will never be the same (1 hr edition)
Plug yourself in and your app will never be the same (1 hr edition)Mikkel Flindt Heisterberg
 
Cape Cod Web Technology Meetup - 3
Cape Cod Web Technology Meetup - 3Cape Cod Web Technology Meetup - 3
Cape Cod Web Technology Meetup - 3Asher Martin
 
OpenShift State of the Union, brought to you by JBoss
OpenShift State of the Union, brought to you by JBossOpenShift State of the Union, brought to you by JBoss
OpenShift State of the Union, brought to you by JBossEric D. Schabell
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014biicode
 
Create Your First Cocoa pods
Create Your First Cocoa podsCreate Your First Cocoa pods
Create Your First Cocoa podsPawan Ramteke
 
Gitlab and Lingvokot
Gitlab and LingvokotGitlab and Lingvokot
Gitlab and LingvokotLingvokot
 
Continuous Integration and Code Coverage in Xcode
Continuous Integration and Code Coverage in XcodeContinuous Integration and Code Coverage in Xcode
Continuous Integration and Code Coverage in XcodeHiep Luong
 
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...MongoDB
 
CI/CD with Jenkins and Docker - DevOps Meetup Day Thailand
CI/CD with Jenkins and Docker - DevOps Meetup Day ThailandCI/CD with Jenkins and Docker - DevOps Meetup Day Thailand
CI/CD with Jenkins and Docker - DevOps Meetup Day ThailandTroublemaker Khunpech
 
OpenShift Primer - get your business into the Cloud today!
OpenShift Primer - get your business into the Cloud today!OpenShift Primer - get your business into the Cloud today!
OpenShift Primer - get your business into the Cloud today!Eric D. Schabell
 

Similar to Hello elixir (and otp) (20)

Docker for developers on mac and windows
Docker for developers on mac and windowsDocker for developers on mac and windows
Docker for developers on mac and windows
 
Parse cloud code
Parse cloud codeParse cloud code
Parse cloud code
 
Docker Starter Pack
Docker Starter PackDocker Starter Pack
Docker Starter Pack
 
State ofappdevelopment
State ofappdevelopmentState ofappdevelopment
State ofappdevelopment
 
SECCOM 2017 - Conan.io o gerente de pacote para C e C++
SECCOM 2017 - Conan.io o gerente de pacote para C e C++SECCOM 2017 - Conan.io o gerente de pacote para C e C++
SECCOM 2017 - Conan.io o gerente de pacote para C e C++
 
NLUUG Spring 2012 - OpenShift Primer
NLUUG Spring 2012 - OpenShift PrimerNLUUG Spring 2012 - OpenShift Primer
NLUUG Spring 2012 - OpenShift Primer
 
Going open source with small teams
Going open source with small teamsGoing open source with small teams
Going open source with small teams
 
Deliver Python Apps with Docker
Deliver Python Apps with DockerDeliver Python Apps with Docker
Deliver Python Apps with Docker
 
Plug yourself in and your app will never be the same (1 hr edition)
Plug yourself in and your app will never be the same (1 hr edition)Plug yourself in and your app will never be the same (1 hr edition)
Plug yourself in and your app will never be the same (1 hr edition)
 
Cape Cod Web Technology Meetup - 3
Cape Cod Web Technology Meetup - 3Cape Cod Web Technology Meetup - 3
Cape Cod Web Technology Meetup - 3
 
OpenShift State of the Union, brought to you by JBoss
OpenShift State of the Union, brought to you by JBossOpenShift State of the Union, brought to you by JBoss
OpenShift State of the Union, brought to you by JBoss
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
 
Create Your First Cocoa pods
Create Your First Cocoa podsCreate Your First Cocoa pods
Create Your First Cocoa pods
 
Gitlab and Lingvokot
Gitlab and LingvokotGitlab and Lingvokot
Gitlab and Lingvokot
 
Continuous Integration and Code Coverage in Xcode
Continuous Integration and Code Coverage in XcodeContinuous Integration and Code Coverage in Xcode
Continuous Integration and Code Coverage in Xcode
 
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...
 
Elixir on Containers
Elixir on ContainersElixir on Containers
Elixir on Containers
 
CI/CD with Jenkins and Docker - DevOps Meetup Day Thailand
CI/CD with Jenkins and Docker - DevOps Meetup Day ThailandCI/CD with Jenkins and Docker - DevOps Meetup Day Thailand
CI/CD with Jenkins and Docker - DevOps Meetup Day Thailand
 
OpenShift Primer - get your business into the Cloud today!
OpenShift Primer - get your business into the Cloud today!OpenShift Primer - get your business into the Cloud today!
OpenShift Primer - get your business into the Cloud today!
 
From Zero to Docker
From Zero to DockerFrom Zero to Docker
From Zero to Docker
 

Recently uploaded

Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsAndolasoft Inc
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...OnePlan Solutions
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsArshad QA
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providermohitmore19
 

Recently uploaded (20)

Exploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the ProcessExploring iOS App Development: Simplifying the Process
Exploring iOS App Development: Simplifying the Process
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 

Hello elixir (and otp)

  • 1. HELLO ELIXIR (AND OTP) Abel Muiño (@amuino) & Rok Biderman (@RokBiderman)
  • 2. ULTRA SHORT INTROS Abel Muiño Lead developer at Cabify. Works with ruby for a living. abel.muino@cabify.com / @amuino Rok Biderman Senior Go developer at Cabify. Has an interesting past position. Go ask him. rok.biderman@cabify.com / @RokBiderman
  • 3. WE ARE HIRING Ruby, Go, Javascript, Android, iOS (Just not for Elixir, yet)
  • 4. HELLO ELIXIR (AND OTP) Abel Muiño (@amuino) & Rok Biderman (@RokBiderman)
  • 5. GOALS ➤ Show some code, this is a programming meet up ➤ Share our Elixir learning path ➤ Learn something from feedback and criticism ➤ Hopefully at least one other person will learn one thing
  • 6. “This is not production code -Abel Muiño
  • 9. MIX NEW $ mix new ocr * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/ocr.ex * creating test * creating test/test_helper.exs * creating test/ocr_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd ocr mix test Run "mix help" for more commands.
  • 10. MIX NEW $ mix new ocr * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/ocr.ex * creating test * creating test/test_helper.exs * creating test/ocr_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd ocr mix test Run "mix help" for more commands. Project Definition
  • 11. MIX NEW $ mix new ocr * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/ocr.ex * creating test * creating test/test_helper.exs * creating test/ocr_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd ocr mix test Run "mix help" for more commands. Project Definition App config
  • 12. MIX NEW $ mix new ocr * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/ocr.ex * creating test * creating test/test_helper.exs * creating test/ocr_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd ocr mix test Run "mix help" for more commands. Project Definition App config Main module
  • 13. MIX NEW $ mix new ocr * creating README.md * creating .gitignore * creating mix.exs * creating config * creating config/config.exs * creating lib * creating lib/ocr.ex * creating test * creating test/test_helper.exs * creating test/ocr_test.exs Your Mix project was created successfully. You can use "mix" to compile it, test it, and more: cd ocr mix test Run "mix help" for more commands. Project Definition App config Main module 😓Not talking about tests today
  • 14. THE INTERACTIVE SHELL $ iex -S mix Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async- threads:10] [hipe] [kernel-poll:false] [dtrace] Compiled lib/ocr.ex Generated ocr app Consolidated List.Chars Consolidated Collectable Consolidated String.Chars Consolidated Enumerable Consolidated IEx.Info Consolidated Inspect Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Ocr Ocr
  • 15. THE INTERACTIVE SHELL $ iex -S mix Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async- threads:10] [hipe] [kernel-poll:false] [dtrace] Compiled lib/ocr.ex Generated ocr app Consolidated List.Chars Consolidated Collectable Consolidated String.Chars Consolidated Enumerable Consolidated IEx.Info Consolidated Inspect Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Ocr Ocr Automatically compiles new files
  • 16. THE INTERACTIVE SHELL $ iex -S mix Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async- threads:10] [hipe] [kernel-poll:false] [dtrace] Compiled lib/ocr.ex Generated ocr app Consolidated List.Chars Consolidated Collectable Consolidated String.Chars Consolidated Enumerable Consolidated IEx.Info Consolidated Inspect Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Ocr Ocr Automatically compiles new files Lots of first-run noise
  • 17. THE INTERACTIVE SHELL $ iex -S mix Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async- threads:10] [hipe] [kernel-poll:false] [dtrace] Compiled lib/ocr.ex Generated ocr app Consolidated List.Chars Consolidated Collectable Consolidated String.Chars Consolidated Enumerable Consolidated IEx.Info Consolidated Inspect Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Ocr Ocr Automatically compiles new files Lots of first-run noise SUCCESS! Our module exists
  • 18. 💡TIP: TRAINING WHEELS ➤ We are learning, so getting quick feedback is useful ➤ Credo is a static code analysis tool for the Elixir language with a focus on teaching and code consistency.
 https://github.com/rrrene/credo ➤ Credo installs as a project dependency ➤ Adds a new task to mix to analyse our code ➤ Excellent, very detailed, feedback
  • 19. ADD A DEPENDENCY Find the dependency info in hex.pm Edit mix.exs defp deps do [ {:credo, "~> 0.3", only: [:dev]} ] end Install it locally $ mix geps.get
  • 20. ADD A DEPENDENCY Find the dependency info in hex.pm Edit mix.exs defp deps do [ {:credo, "~> 0.3", only: [:dev]} ] end Install it locally $ mix geps.get Dependencies are an array of tuples.
  • 21. ADD A DEPENDENCY Find the dependency info in hex.pm Edit mix.exs defp deps do [ {:credo, "~> 0.3", only: [:dev]} ] end Install it locally $ mix geps.get Dependencies are an array of tuples. Only installs the dependency in the :dev environment
  • 22. TRY IT $ mix credo … Code Readability [R] ! Modules should have a @moduledoc tag. lib/ocr.ex:1:11 (Ocr) $ mix credo lib/ocr.ex:1:11
  • 23. TRY IT $ mix credo … Code Readability [R] ! Modules should have a @moduledoc tag. lib/ocr.ex:1:11 (Ocr) $ mix credo lib/ocr.ex:1:11 OMG!
  • 24. TRY IT $ mix credo … Code Readability [R] ! Modules should have a @moduledoc tag. lib/ocr.ex:1:11 (Ocr) $ mix credo lib/ocr.ex:1:11 OMG! Detailed explanation on the error, how to suppress it, etc…
  • 25. NOW WHAT? ➤ Use Google Vision API to perform the actual OCR ➤ Has no client in hex.pm ➤ It is a REST API → {:httpoison, "~> 0.8.3"} ➤ Returns JSON → {:poison, "~> 2.1.0"} ➤ Needs authentication → {:goth, "~> 0.1.2”} ➤ Build a nice façade
  • 26. MIX.EXS def application do [applications: [:logger, :httpoison, :goth]] end defp deps do [ {:httpoison, "~> 0.8.3"}, {:poison, "~> 2.1.0"}, {:goth, "~> 0.1.2"}, {:credo, "~> 0.3", only: [:dev]} ] end
  • 27. MIX.EXS def application do [applications: [:logger, :httpoison, :goth]] end defp deps do [ {:httpoison, "~> 0.8.3"}, {:poison, "~> 2.1.0"}, {:goth, "~> 0.1.2"}, {:credo, "~> 0.3", only: [:dev]} ] end Some deps also need their app to be started
  • 28. CONFIG/CONFIG.EXS use Mix.Config config :goth, json: "config/google-creds.json" |> File.read!
  • 29. CONFIG/CONFIG.EXS use Mix.Config config :goth, json: "config/google-creds.json" |> File.read! Some deps also have their own config
  • 30. NOW WHAT? ➤ We will write 2 modules: ➤ Ocr.GoogleVision for the API client. ➤ Ocr for our façade
  • 31. 💡TIP: MODULE NAMES ➤ Convention: ➤ Ocr ! lib/ocr.ex ➤ Ocr.GoogleVision ! lib/ocr/google_vision.ex ➤ Modules names are just names. Dots in the name do not represent any parent/child relationship.
  • 32. LIB/OCR/GOOGLE_VISION.EX defmodule Ocr.GoogleVision do def extract_text(image64) do image64 |> make_request |> read_body end # MAGIC! end
  • 33. LIB/OCR/GOOGLE_VISION.EX defmodule Ocr.GoogleVision do def extract_text(image64) do image64 |> make_request |> read_body end # MAGIC! end base64 encoded image
  • 34. LIB/OCR/GOOGLE_VISION.EX defmodule Ocr.GoogleVision do def extract_text(image64) do image64 |> make_request |> read_body end # MAGIC! end base64 encoded image send to Google
  • 35. LIB/OCR/GOOGLE_VISION.EX defmodule Ocr.GoogleVision do def extract_text(image64) do image64 |> make_request |> read_body end # MAGIC! end base64 encoded image send to Google get the text from the response
  • 36. LIB/OCR/GOOGLE_VISION.EX @url "https://vision.googleapis.com/v1/images:annotate" @feature_text_detection "TEXT_DETECTION" @auth_scope "https://www.googleapis.com/auth/cloud-platform" def make_request(image64) do HTTPoison.post!(@url, payload(image64), headers) end defp payload(image64) do %{requests: [ %{image: %{content: image64}, features: [%{type: @feature_text_detection}]} ] } |> Poison.encode! end defp headers do {:ok, token} = Goth.Token.for_scope(@auth_scope) [{"Authorization", "#{token.type} #{token.token}"}] end
  • 37. LIB/OCR/GOOGLE_VISION.EX @url "https://vision.googleapis.com/v1/images:annotate" @feature_text_detection "TEXT_DETECTION" @auth_scope "https://www.googleapis.com/auth/cloud-platform" def make_request(image64) do HTTPoison.post!(@url, payload(image64), headers) end defp payload(image64) do %{requests: [ %{image: %{content: image64}, features: [%{type: @feature_text_detection}]} ] } |> Poison.encode! end defp headers do {:ok, token} = Goth.Token.for_scope(@auth_scope) [{"Authorization", "#{token.type} #{token.token}"}] end Module attributes (used as a constants)
  • 38. LIB/OCR/GOOGLE_VISION.EX @url "https://vision.googleapis.com/v1/images:annotate" @feature_text_detection "TEXT_DETECTION" @auth_scope "https://www.googleapis.com/auth/cloud-platform" def make_request(image64) do HTTPoison.post!(@url, payload(image64), headers) end defp payload(image64) do %{requests: [ %{image: %{content: image64}, features: [%{type: @feature_text_detection}]} ] } |> Poison.encode! end defp headers do {:ok, token} = Goth.Token.for_scope(@auth_scope) [{"Authorization", "#{token.type} #{token.token}"}] end Module attributes (used as a constants) HTTP POST some JSON to some URL with some Headers
  • 39. LIB/OCR/GOOGLE_VISION.EX @url "https://vision.googleapis.com/v1/images:annotate" @feature_text_detection "TEXT_DETECTION" @auth_scope "https://www.googleapis.com/auth/cloud-platform" def make_request(image64) do HTTPoison.post!(@url, payload(image64), headers) end defp payload(image64) do %{requests: [ %{image: %{content: image64}, features: [%{type: @feature_text_detection}]} ] } |> Poison.encode! end defp headers do {:ok, token} = Goth.Token.for_scope(@auth_scope) [{"Authorization", "#{token.type} #{token.token}"}] end Module attributes (used as a constants) HTTP POST some JSON to some URL with some Headers The JSON Google wants
  • 40. LIB/OCR/GOOGLE_VISION.EX @url "https://vision.googleapis.com/v1/images:annotate" @feature_text_detection "TEXT_DETECTION" @auth_scope "https://www.googleapis.com/auth/cloud-platform" def make_request(image64) do HTTPoison.post!(@url, payload(image64), headers) end defp payload(image64) do %{requests: [ %{image: %{content: image64}, features: [%{type: @feature_text_detection}]} ] } |> Poison.encode! end defp headers do {:ok, token} = Goth.Token.for_scope(@auth_scope) [{"Authorization", "#{token.type} #{token.token}"}] end Module attributes (used as a constants) HTTP POST some JSON to some URL with some Headers The JSON Google wants Get a token
  • 41. LIB/OCR/GOOGLE_VISION.EX @url "https://vision.googleapis.com/v1/images:annotate" @feature_text_detection "TEXT_DETECTION" @auth_scope "https://www.googleapis.com/auth/cloud-platform" def make_request(image64) do HTTPoison.post!(@url, payload(image64), headers) end defp payload(image64) do %{requests: [ %{image: %{content: image64}, features: [%{type: @feature_text_detection}]} ] } |> Poison.encode! end defp headers do {:ok, token} = Goth.Token.for_scope(@auth_scope) [{"Authorization", "#{token.type} #{token.token}"}] end Module attributes (used as a constants) HTTP POST some JSON to some URL with some Headers The JSON Google wants Get a token Put the token on the request headers
  • 42. LIB/OCR/GOOGLE_VISION.EX def read_body(%HTTPoison.Response{body: body, status_code: 200}) do body |> Poison.decode! |> get_in(["responses", &first/3, "textAnnotations", &first/3, "description"]) end defp first(:get, nil, _), do: nil defp first(:get, data, next) do data |> List.first |> next.() end
  • 43. LIB/OCR/GOOGLE_VISION.EX def read_body(%HTTPoison.Response{body: body, status_code: 200}) do body |> Poison.decode! |> get_in(["responses", &first/3, "textAnnotations", &first/3, "description"]) end defp first(:get, nil, _), do: nil defp first(:get, data, next) do data |> List.first |> next.() end Only care about body and success http status
  • 44. LIB/OCR/GOOGLE_VISION.EX def read_body(%HTTPoison.Response{body: body, status_code: 200}) do body |> Poison.decode! |> get_in(["responses", &first/3, "textAnnotations", &first/3, "description"]) end defp first(:get, nil, _), do: nil defp first(:get, data, next) do data |> List.first |> next.() end Only care about body and success http status Parse JSON response
  • 45. LIB/OCR/GOOGLE_VISION.EX def read_body(%HTTPoison.Response{body: body, status_code: 200}) do body |> Poison.decode! |> get_in(["responses", &first/3, "textAnnotations", &first/3, "description"]) end defp first(:get, nil, _), do: nil defp first(:get, data, next) do data |> List.first |> next.() end Only care about body and success http status Parse JSON response Extract the text
  • 46. LIB/OCR/GOOGLE_VISION.EX def read_body(%HTTPoison.Response{body: body, status_code: 200}) do body |> Poison.decode! |> get_in(["responses", &first/3, "textAnnotations", &first/3, "description"]) end defp first(:get, nil, _), do: nil defp first(:get, data, next) do data |> List.first |> next.() end Only care about body and success http status Parse JSON response Extract the text Custom lookup functions
  • 47. LIB/OCR/GOOGLE_VISION.EX def read_body(%HTTPoison.Response{body: body, status_code: 200}) do body |> Poison.decode! |> get_in(["responses", &first/3, "textAnnotations", &first/3, "description"]) end defp first(:get, nil, _), do: nil defp first(:get, data, next) do data |> List.first |> next.() end Only care about body and success http status Parse JSON response Extract the text Custom lookup functions funky syntax to invoke an anonymous function
  • 48. 💡TIP: GET_IN IS AWESOME ➤ Navigates nested structures (maps) iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}} iex> get_in(users, ["john", :age]) 27 ➤ Returns nil on missing keys iex> get_in(users, ["unknown", :age]) nil ➤ Accepts functions for navigating other types ➤ Elixir 1.3 will have common functions predefined for tuples and lists ➤ Access.at(0) will replace my custom first (PR #4719) ➤ Also check get_and_update_in, put_in, update_in
  • 49. LIB/OCR.EX defmodule Ocr do def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64) def from_image(image_data) do image_data |> Base.encode64 |> from_base64 end def from_path(path), do: path |> File.read! |> from_image def from_url(url), do: HTTPoison.get!(url).body |> from_image end
  • 50. LIB/OCR.EX defmodule Ocr do def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64) def from_image(image_data) do image_data |> Base.encode64 |> from_base64 end def from_path(path), do: path |> File.read! |> from_image def from_url(url), do: HTTPoison.get!(url).body |> from_image end
  • 51. LIB/OCR.EX defmodule Ocr do def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64) def from_image(image_data) do image_data |> Base.encode64 |> from_base64 end def from_path(path), do: path |> File.read! |> from_image def from_url(url), do: HTTPoison.get!(url).body |> from_image end
  • 52. LIB/OCR.EX defmodule Ocr do def from_base64(b64), do: Ocr.GoogleVision.extract_text(b64) def from_image(image_data) do image_data |> Base.encode64 |> from_base64 end def from_path(path), do: path |> File.read! |> from_image def from_url(url), do: HTTPoison.get!(url).body |> from_image end
  • 53. FUN! $ iex -S mix Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads: 10] [hipe] [kernel-poll:false] [dtrace] A new Hex version is available (0.12.0), please update with `mix local.hex` Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> meme_url = "http://ih0.redbubble.net/image.16611809.2383/fc, 550x550,black.jpg" "http://ih0.redbubble.net/image.16611809.2383/fc,550x550,black.jpg" iex(2)> IO.puts Ocr.from_url meme_url GETS ELIKIR PR ACCEPTED I SAID WHO WANTS TO FUCKING TOUCH ME? Suranyami :ok
  • 54. FUN! $ iex -S mix Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads: 10] [hipe] [kernel-poll:false] [dtrace] A new Hex version is available (0.12.0), please update with `mix local.hex` Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> meme_url = "http://ih0.redbubble.net/image.16611809.2383/fc, 550x550,black.jpg" "http://ih0.redbubble.net/image.16611809.2383/fc,550x550,black.jpg" iex(2)> IO.puts Ocr.from_url meme_url GETS ELIKIR PR ACCEPTED I SAID WHO WANTS TO FUCKING TOUCH ME? Suranyami :ok
  • 56. ANSWER: STATEFUL ➤ Auth tokens are not requested every time ➤ Requested on first use ➤ Refreshed on the background when about to expire ➤ Goth.TokenStore is a GenServer ➤ Just one of the predefined behaviours to make easier to work with processes ➤ Starts a process with some state ➤ Receives messages and updates the state ➤ There is more state (like Goth.Config)
  • 57. DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX defmodule Goth.TokenStore do use Xenserver alias Goth.Token def start_link do GenServer.start_link(__MODULE__, %{}, [name: __MODULE__]) end def handle_call({:store, scope, token}, _from, state) do pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, scope, token)} end def handle_call({:find, scope}, _from, state) do {:reply, Map.fetch(state, scope), state} end end
  • 58. DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX defmodule Goth.TokenStore do use Xenserver alias Goth.Token def start_link do GenServer.start_link(__MODULE__, %{}, [name: __MODULE__]) end def handle_call({:store, scope, token}, _from, state) do pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, scope, token)} end def handle_call({:find, scope}, _from, state) do {:reply, Map.fetch(state, scope), state} end end Start the process,
  • 59. DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX defmodule Goth.TokenStore do use Xenserver alias Goth.Token def start_link do GenServer.start_link(__MODULE__, %{}, [name: __MODULE__]) end def handle_call({:store, scope, token}, _from, state) do pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, scope, token)} end def handle_call({:find, scope}, _from, state) do {:reply, Map.fetch(state, scope), state} end end Start the process, Initial state is an empty map
  • 60. DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX defmodule Goth.TokenStore do use Xenserver alias Goth.Token def start_link do GenServer.start_link(__MODULE__, %{}, [name: __MODULE__]) end def handle_call({:store, scope, token}, _from, state) do pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, scope, token)} end def handle_call({:find, scope}, _from, state) do {:reply, Map.fetch(state, scope), state} end end Start the process, Initial state is an empty map The process has a name
  • 61. DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX defmodule Goth.TokenStore do use Xenserver alias Goth.Token def start_link do GenServer.start_link(__MODULE__, %{}, [name: __MODULE__]) end def handle_call({:store, scope, token}, _from, state) do pid_or_timer = Token.queue_for_refresh(token) {:reply, pid_or_timer, Map.put(state, scope, token)} end def handle_call({:find, scope}, _from, state) do {:reply, Map.fetch(state, scope), state} end end Start the process, Initial state is an empty map The process has a name Handle 2 types of messages, returning something
  • 62. FUN WITH TOKEN STORE $ iex -S mix iex(1)> token = %Goth.Token{token: "FAKE", expires: :os.system_time + 10_000_000} %Goth.Token{expires: 1464647952951907000, scope: nil, token: "FAKE", type: nil} iex(2)> GenServer.call Goth.TokenStore, {:find, "Elixir"} :error iex(3)> GenServer.call Goth.TokenStore, {:store, "Elixir", token} {:ok, {1464647950910798703208727, #Reference<0.0.7.228>}} iex(4)> GenServer.call Goth.TokenStore, {:find, "Elixir"} {:ok, %Goth.Token{expires: 1464647952951907000, scope: nil, token: “FAKE", type: nil}}
  • 63. FUN WITH TOKEN STORE $ iex -S mix iex(1)> token = %Goth.Token{token: "FAKE", expires: :os.system_time + 10_000_000} %Goth.Token{expires: 1464647952951907000, scope: nil, token: "FAKE", type: nil} iex(2)> GenServer.call Goth.TokenStore, {:find, "Elixir"} :error iex(3)> GenServer.call Goth.TokenStore, {:store, "Elixir", token} {:ok, {1464647950910798703208727, #Reference<0.0.7.228>}} iex(4)> GenServer.call Goth.TokenStore, {:find, "Elixir"} {:ok, %Goth.Token{expires: 1464647952951907000, scope: nil, token: “FAKE", type: nil}} The process name
  • 64. FUN WITH TOKEN STORE $ iex -S mix iex(1)> token = %Goth.Token{token: "FAKE", expires: :os.system_time + 10_000_000} %Goth.Token{expires: 1464647952951907000, scope: nil, token: "FAKE", type: nil} iex(2)> GenServer.call Goth.TokenStore, {:find, "Elixir"} :error iex(3)> GenServer.call Goth.TokenStore, {:store, "Elixir", token} {:ok, {1464647950910798703208727, #Reference<0.0.7.228>}} iex(4)> GenServer.call Goth.TokenStore, {:find, "Elixir"} {:ok, %Goth.Token{expires: 1464647952951907000, scope: nil, token: “FAKE", type: nil}} The process name The message
  • 65. DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX defmodule Goth.TokenStore do def store(%Token{}=token), do: store(token.scope, token) def store(scopes, %Token{} = token) do GenServer.call(__MODULE__, {:store, scopes, token}) end def find(scope) do GenServer.call(__MODULE__, {:find, scope}) end end
  • 66. DEPS/GOTH/LIB/GOTH/TOKEN_STORE.EX defmodule Goth.TokenStore do def store(%Token{}=token), do: store(token.scope, token) def store(scopes, %Token{} = token) do GenServer.call(__MODULE__, {:store, scopes, token}) end def find(scope) do GenServer.call(__MODULE__, {:find, scope}) end end Provide a client API (usually in the same module) with nicer methods hiding the use of Genserver.call
  • 67. LET’S TALK ABOUT OTP Processes, state, concurrency, supervisors,… oh my!
  • 68. RECAP ➤ Erlang is: ➤ general-purpose ➤ concurrent ➤ garbage-collected ➤ programming language ➤ and runtime system ➤ Elixir: ➤ Builds on top of all that
  • 69. START A PROCESS ➤ Basic concurrency primitive ➤ Simplest way to create, use spawn with a function defmodule BasicMessagePassing.Call do def concat(a, b) do IO.puts("#{a} #{b}") end end iex(2)> BasicMessagePassing.Call.concat "Elixir", "Madrid" Elixir Madrid :ok iex(3)> spawn BasicMessagePassing.Call, :concat, ["Elixir", "Madrid"] Elixir Madrid #PID<0.69.0>
  • 70. START A PROCESS ➤ Basic concurrency primitive ➤ Simplest way to create, use spawn with a function defmodule BasicMessagePassing.Call do def concat(a, b) do IO.puts("#{a} #{b}") end end iex(2)> BasicMessagePassing.Call.concat "Elixir", "Madrid" Elixir Madrid :ok iex(3)> spawn BasicMessagePassing.Call, :concat, ["Elixir", "Madrid"] Elixir Madrid #PID<0.69.0> Same process
  • 71. START A PROCESS ➤ Basic concurrency primitive ➤ Simplest way to create, use spawn with a function defmodule BasicMessagePassing.Call do def concat(a, b) do IO.puts("#{a} #{b}") end end iex(2)> BasicMessagePassing.Call.concat "Elixir", "Madrid" Elixir Madrid :ok iex(3)> spawn BasicMessagePassing.Call, :concat, ["Elixir", "Madrid"] Elixir Madrid #PID<0.69.0> Same process Spawned process id
  • 72. LISTEN FOR MESSAGES defmodule BasicMessagePassing.Listen do def listen do receive do {:ok, input} -> IO.puts "#{input} Madrid" end end end iex(5)> pid = spawn(BasicMessagePassing.Listen, :listen, []) #PID<0.82.0> iex(6)> send pid, {:ok, "Elixir"} Elixir Madrid {:ok, "Elixir"} iex(8)> Process.alive? pid false
  • 73. FIBONACCI TIME! defmodule FibSerial do def calculate(ns) do ns |> Enum.map(&(calc(&1))) |> inspect |> IO.puts end def calc(n) do calc(n, 1, 0) end defp calc(0, _, _) do 0 end defp calc(1, a, b) do a + b end defp calc(n, a, b) do calc(n - 1, b, a + b) end end FibSerial.calculate(Enum.to_list(1..10000))
  • 74. FIBONACCI TIME! defmodule FibSerial do def calculate(ns) do ns |> Enum.map(&(calc(&1))) |> inspect |> IO.puts end def calc(n) do calc(n, 1, 0) end defp calc(0, _, _) do 0 end defp calc(1, a, b) do a + b end defp calc(n, a, b) do calc(n - 1, b, a + b) end end FibSerial.calculate(Enum.to_list(1..10000)) About 6 seconds
  • 75. PARALLEL FIBONACCI TIME! defmodule FibParallel do def calculate(ns) do ns |> Enum.with_index |> Enum.map(fn(ni) -> spawn FibParallel, :send_calc, [self, ni] end) listen(length(ns), []) end def send_calc(pid, {n, i}) do send pid, {calc(n), i} end defp listen(lns, result) do receive do fib -> result = [fib | result] if lns == 1 do result |> Enum.sort(fn({_, a}, {_, b}) -> a < b end) |> Enum.map(fn({f, _}) -> f end) |> inspect |> IO.puts else listen(lns - 1, result) end end end end FibSerial.calculate(Enum.to_list(1..10000))
  • 76. PARALLEL FIBONACCI TIME! defmodule FibParallel do def calculate(ns) do ns |> Enum.with_index |> Enum.map(fn(ni) -> spawn FibParallel, :send_calc, [self, ni] end) listen(length(ns), []) end def send_calc(pid, {n, i}) do send pid, {calc(n), i} end defp listen(lns, result) do receive do fib -> result = [fib | result] if lns == 1 do result |> Enum.sort(fn({_, a}, {_, b}) -> a < b end) |> Enum.map(fn({f, _}) -> f end) |> inspect |> IO.puts else listen(lns - 1, result) end end end end FibSerial.calculate(Enum.to_list(1..10000)) About 2 seconds (4 cores)
  • 77. LINKING PROCESSES ➤ If child dies, parent dies defmodule BasicMessagePassing.Linking do def exit, do: exit(:crash) def start do spawn_link(BasicMessagePassing.Linking, :exit, []) receive do {:done} -> IO.puts "no more waiting" end end end iex(13)> BasicMessagePassing.Linking.start ** (EXIT from #PID<0.57.0>) :crash Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)>
  • 78. LINKING PROCESSES ➤ If child dies, parent dies defmodule BasicMessagePassing.Linking do def exit, do: exit(:crash) def start do spawn_link(BasicMessagePassing.Linking, :exit, []) receive do {:done} -> IO.puts "no more waiting" end end end iex(13)> BasicMessagePassing.Linking.start ** (EXIT from #PID<0.57.0>) :crash Interactive Elixir (1.2.5) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> iex died! and was restarted
  • 79. LINKING PROCESSES ➤ If child dies, parent dies… unless it handles the dead defmodule BasicMessagePassing.Linking do def exit, do: exit(:crash) def start do Process.flag(:trap_exit, true) spawn_link(BasicMessagePassing.Linking, :exit, []) receive do {:EXIT, from_pid, reason} -> IO.puts "#{inspect(self)} is aware #{inspect(from_pid)} exited because of #{reason}" end end end iex(24)> BasicMessagePassing.Linking.start #PID<0.57.0> is aware #PID<0.157.0> exited because of crash :ok
  • 80. LINKING PROCESSES ➤ If child dies, parent dies… unless it handles the dead defmodule BasicMessagePassing.Linking do def exit, do: exit(:crash) def start do Process.flag(:trap_exit, true) spawn_link(BasicMessagePassing.Linking, :exit, []) receive do {:EXIT, from_pid, reason} -> IO.puts "#{inspect(self)} is aware #{inspect(from_pid)} exited because of #{reason}" end end end iex(24)> BasicMessagePassing.Linking.start #PID<0.57.0> is aware #PID<0.157.0> exited because of crash :ok survive children
  • 81. LINKING PROCESSES ➤ If child dies, parent dies… unless it handles the dead defmodule BasicMessagePassing.Linking do def exit, do: exit(:crash) def start do Process.flag(:trap_exit, true) spawn_link(BasicMessagePassing.Linking, :exit, []) receive do {:EXIT, from_pid, reason} -> IO.puts "#{inspect(self)} is aware #{inspect(from_pid)} exited because of #{reason}" end end end iex(24)> BasicMessagePassing.Linking.start #PID<0.57.0> is aware #PID<0.157.0> exited because of crash :ok survive children handle deads
  • 82. GENSERVER ➤ Simplifies all this stuff ➤ Is a process like any other Elixir process ➤ Standard set of interface functions, tracing and error reporting ➤ call: request with response ➤ cast: request without response
  • 83. A STACK defmodule Stack do use GenServer def start_link(state, opts []) do GenServer.start_link(__MODULE__, state, opts) end def handle_call(:pop, _from, [h|t]) do {:reply, h, t} end def handle_cast({:push, h}, t) do {:noreply, [h|t]} end end
  • 84. A SUPERVISED STACK iex(7)> import Supervisor.Spec nil iex(8)> children = [ ...(8)> worker(Stack, [[:first], [name: :stack_name]]) ...(8)> ] [{Stack, {Stack, :start_link, [[:first], [name: :stack_name]]}, :permanent, 5000, :worker, [Stack]}] iex(9)> {:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one) {:ok, #PID<0.247.0>} iex(10)> GenServer.call(:stack_name, :pop) :first iex(11)> GenServer.call(:stack_name, :pop) 18:04:35.012 [error] GenServer :stack_name terminating ** (FunctionClauseError) no function clause matching in Stack.handle_call/3 iex:13: Stack.handle_call(:pop, {#PID<0.135.0>, #Reference<0.0.1.317>}, []) [..] Last message: :pop State: [] (elixir) lib/gen_server.ex:564: GenServer.call/3 iex(11)> GenServer.call(:stack_name, :pop) :first
  • 85. SUPERVISOR FLAVORS ➤ one_for_one: dead worker is replaced by another one ➤ rest_for_all: after one dies, all others have to be restarted ➤ rest_for_one: all workers started after this one will be restarted ➤ simple_one_for_one: for dynamically attached children, Supervisor is required to contain only one child
  • 87. THANKS! Abel Muiño (@amuino) & Rok Biderman (@RokBiderman)