08448380779 Call Girls In Civil Lines Women Seeking Men
Yurii Bodarev - OTP, Phoenix & Ecto: Three Pillars of Elixir
1. OTP, Phoenix & Ecto: Three
Pillars of Elixir
Elixir Club Ternopil, 2017
2. Erlang
Functional Programming in Erlang
www.futurelearn.com/courses/functional-programming-erlang/
Concurrent Programming in Erlang
https://www.futurelearn.com/courses/concurrent-programming-erlang
Learn You Some Erlang for great good!
http://learnyousomeerlang.com/
3. Overview
1. Processes & OTP
2. Phoenix Elixir web framework
3. Ecto database wrapper and language integrated query for Elixir
4. Erlang/Elixir processes
• All code runs inside processes
• Processes are isolated from each other, run concurrent to one
another and communicate via message passing (Actor model)
• Processes are extremely lightweight in terms of memory and CPU and
managed by Erlang VM
• It is common to have tens or even hundreds of thousands of
processes running simultaneously
9. Linked processes: spawn_link
iex> spawn_link fn -> raise "something bad happened" end
23:53:50.503 [error] Process #PID<0.93.0> raised an exception
** (RuntimeError) something bad happened
:erlang.apply/2
** (EXIT from #PID<0.91.0>) an exception was raised:
** (RuntimeError) something bad happened
:erlang.apply/2
10. Linked processes & “Failing fast” philosophy
Parent process, which is the shell process, has received an EXIT signal
from another process causing the parent process to terminate.
Often we will link our processes to supervisors which will detect when
a process dies and start a new process in its place.
In Elixir we are actually fine with letting processes fail because we
expect supervisors to properly restart our systems.
12. State
def start_link do
Task.start_link(fn -> loop(%{}) end)
end
defp loop(map) do
receive do
{:get, key, caller} ->
send caller, Map.get(map, key)
loop(map)
{:put, key, value} ->
loop(Map.put(map, key, value))
end
end
15. Behaviours
• Many of the processes have similar structures, they follow similar
patterns
• Behaviours provide a way to define a set of functions that have to be
implemented by a module
• You can think of behaviours like interfaces in OO languages
17. Implementing behaviours
...
# Callbacks
def handle_call(:pop, _from, [h | t]) do
{:reply, h, t}
end
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
...
18. GenServer
• “Generic servers” (processes) that encapsulate state, provide sync
and async calls, support code reloading, and more.
• The GenServer behaviour abstracts the common client-server
interaction. Developers are only required to implement the callbacks
and functionality they are interested in.
• A GenServer is a process like any other Elixir process and it can be
used to keep state, execute code asynchronously and so on.
19. GenServer example
defmodule Stack do
use GenServer
# Callbacks
def handle_call(:pop, _from, [h | t]) do
{:reply, h, t}
end
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
end
22. Supervision Trees
• Supervision trees are a nice way
to structure fault-tolerant
applications.
• Process structuring model based
on the idea of workers and
supervisors.
S
W
S
W
S
W
23. Supervision Trees
• Workers are processes that perform computations, that is, they do
the actual work.
• Supervisors are processes that monitor the behaviour of workers. A
supervisor can restart a worker if something goes wrong.
• The supervision strategy dictates what happens when one of the
children crashes.
24. Supervisor
• A behaviour module for implementing supervision functionality.
• A supervisor is a process which supervises other processes, which we
refer to as child processes.
25. Supervisor module
defmodule MyApp.Supervisor do
use Supervisor
def start_link do
Supervisor.start_link(__MODULE__, [])
end
def init([]) do
children = [ worker(Stack, [[:hello]]) ]
supervise(children, strategy: :one_for_one)
end
end
27. Application behaviour
• In Erlang/OTP, an application is a component implementing some
specific functionality, that can be started and stopped as a unit
• Mix is responsible for compiling your source code and generating
your application .app file in Elixir.
• Mix is also responsible for configuring, starting and stopping your
application and its dependencies (mix.exs).
• .app holds our application definition
34. In umbrella dependencies
Mix supports an easy mechanism to make one umbrella child depend
on another.
...
defp deps do
[{:hello_world, in_umbrella: true}]
end
...
35. GenStage & Flow
Announcing GenStage
http://elixir-lang.org/blog/2016/07/14/announcing-genstage/
GenStage and Flow - Jose Valim | Elixir Club 5
https://www.youtube.com/watch?v=IUrfcBwkm7w
https://hex.pm/packages/gen_stage
36. GenStage is a new Elixir behaviour for exchanging
events with back-pressure between Elixir processes
producer
producer
consumer
producer
consumer
consumer
37. Flow: concurrent data processing
def process_flow(path_to_file) do
path_to_file
|> File.stream!()
|> Flow.from_enumerable()
|> Flow.flat_map(&String.split/1)
|> Flow.map(&String.replace(&1, ~r/W/u, ""))
|> Flow.filter_map(fn w -> w != "" end, &String.downcase/1)
|> Flow.partition()
|> Flow.reduce(fn -> %{} end, fn word, map ->
Map.update(map, word, 1, &(&1 + 1))
end)
|> Enum.into(%{})
end
38. Flow: concurrent data processing
def process_flow(path_to_file) do
path_to_file
|> File.stream!()
|> Flow.from_enumerable()
|> Flow.flat_map(&String.split/1)
|> Flow.map(&String.replace(&1, ~r/W/u, ""))
|> Flow.filter_map(fn w -> w != "" end, &String.downcase/1)
|> Flow.partition()
|> Flow.reduce(fn -> %{} end, fn word, map ->
Map.update(map, word, 1, &(&1 + 1))
end)
|> Enum.into(%{})
end
P
PC PC
DemandDispatcher
PartitionDispatcher
PC PC
C CReducers
%{} %{}
39. Flow
P
PC PC
PC PC
C C
%{} %{}
"The Project Gutenberg EBook of
The Complete Works of William
Shakespeare, byn"
"William Shakespearen"
"The", "Project", "Gutenberg",
"EBook", "of", "The", "Complete",
"Works", "of", "William",
"Shakespeare,", "by"
"William", "Shakespeare"
"the", "project", "of", “the",
"william", "of", "by ", "william"
"gutenberg", "ebook",
"complete", "shakespeare",
"works", "shakespeare"
40. Flow
Experimental.Flow, Yurii Bodarev at KyivElixirMeetup 3.1
https://www.youtube.com/watch?v=XhUeSUFF06w
https://github.com/yuriibodarev/elixir_flow
41. Phoenix Framework
• Phoenix is a web development framework written in Elixir which
implements the server-side MVC pattern
• Phoenix provides the best of both worlds - high developer
productivity and high application performance
• Phoenix is actually the top layer of a multi-layer system designed to be
modular and flexible. The other layers include Plug, and Ecto
• The Erlang HTTP server, Cowboy, acts as the foundation for Plug and
Phoenix
42. Phoenix Framework
• The Plug
• The Endpoint
• The Router
• Controllers
• Actions
• Views
• Templates
• Channels
43. The Plug
• Plug is a specification for constructing composable modules to build web
applications.
• Plugs are reusable modules or functions built to that specification.
• They provide discrete behaviors - like request header parsing or logging.
• Because the Plug API is small and consistent, plugs can be defined and executed
in a set order, like a pipeline.
• Core Phoenix components like Endpoints, Routers, and Controllers are all just
Plugs internally
44. Module Plug example
defmodule Example.HelloWorldPlug do
import Plug.Conn
def init(options), do: options
def call(conn, _opts) do
conn
|> put_resp_content_type("text/plain")
|> send_resp(200, "Hello World!")
end
end
%Plug.Conn{…}
45. Plug pipelines
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
46. Module Plug example
...
@locales ["en", "fr", "de"]
def init(default), do: default
def call(%Plug.Conn{params: %{"locale" => loc}} = conn, _default)
when loc in @locales
do
assign(conn, :locale, loc)
end
def call(conn, default), do: assign(conn, :locale, default)
...
47. Adding Plug to the pipeline
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug PhoenixApp.Plugs.Locale, "en"
end
48. The Endpoint
• provides a wrapper for starting and stopping the endpoint as part of a
supervision tree;
• handles all aspects of requests up until the point where the router takes over
• to define an initial plug pipeline where requests are sent through;
• to host web specific configuration for your application.
• dispatches requests into a designated router
50. Starting Endpoint
phoenix_applibphoenix_appapplication.ex
...
children = [
# Start the Ecto repository
supervisor(PhoenixApp.Repo, []),
# Start the endpoint when the application starts
supervisor(PhoenixApp.Web.Endpoint, [])
]
opts = [strategy: :one_for_one, name: PhoenixApp.Supervisor]
Supervisor.start_link(children, opts)
...
51. The Router
• parses incoming requests and dispatches them to the correct
controller/action, passing parameters as needed
• provides helpers to generate route paths or urls to resources
• defines named pipelines through which we may pass our requests
53. Controllers & Actions
Controllers provide functions, called actions, to handle requests
Actions
• prepare data and pass it into views
• invoke rendering via views
• perform redirects
54. Controller
get "/pages/:id", PageController, :show
phoenix_applibphoenix_appwebcontrollerspage_controller.ex
defmodule PhoenixApp.Web.PageController do
use PhoenixApp.Web, :controller
def show(conn, %{"id" => id}) do
user = Accounts.get_user(id)
render(conn, "show.html", user: user)
end
end
56. Views
• Defines the view layer of a Phoenix application
• Render templates
• Define helper functions, available in templates, to decorate data for
presentation
57. Rendering Templates
Phoenix assumes a strong naming convention from controllers to views
to the templates they render. The PageController requires a PageView
to render templates in the webtemplatespage directory.
phoenix_applibphoenix_appwebweb.ex
...
def view do quote do
use Phoenix.View, root: "lib/phoenix_app/web/templates",
namespace: PhoenixApp.Web
...
59. Rendering JSON
def render("index.json", %{pages: pages}) do
%{data: render_many(pages, PhoenixApp.PageView, "page.json")}
end
def render("show.json", %{page: page}) do
%{data: render_one(page, PhoenixApp.PageView, "page.json")}
end
def render("page.json", %{page: page}) do
%{title: page.title}
end
60. Templates
foo.html.eex
• templates are precompiled and fast
• template name - is the name of the template as given by the user,
without the template engine extension, for example: “foo.html”
• template path - is the complete path of the template in the
filesystem, for example, “path/to/foo.html.eex”
• template root - the directory where templates are defined
• template engine (EEx)- a module that receives a template path and
transforms its source code into Elixir quoted expressions.
61. Template examples
Hello <%= @name %>
<h3>Keys for the conn Struct</h3>
<%= for key <- connection_keys @conn do %>
<p><%= key %></p>
<% end %>
function that returns List of keys
62. Channels
• manage sockets for easy real-time communication
• are analogous to controllers except that they allow bi-directional
communication with persistent connections
• Every time you join a channel, you need to choose which particular
topic you want to listen to. The topic is just an identifier, but by
convention it is often made of two parts: "topic:subtopic".
64. Joining Channels
defmodule PhoenixApp.Web.RoomChannel do
use Phoenix.Channel
def join("room:lobby", _message, socket) do
{:ok, socket}
end
def join("room:" <> _private_room_id, _params, _socket) do
{:error, %{reason: "unauthorized"}}
end
end
65. JS
phoenix_appassetsjssocket.js
...
socket.connect()
// Now that you are connected, you can join channels with a topic:
let channel = socket.channel("topic:subtopic", {}) channel.join()
.receive("ok", resp => { console.log("Joined successfully", resp) })
.receive("error", resp => { console.log("Unable to join", resp) })
67. Incoming Events
We handle incoming events with handle_in/3. We can pattern match
on the event names, like “new_msg”
def handle_in("new_msg", %{"body" => body}, socket) do
broadcast! socket, "new_msg", %{body: body}
{:noreply, socket}
end
68. Outgoing Events: default implementation
def handle_out("new_msg", payload, socket) do
push socket, "new_msg", payload
{:noreply, socket}
end
69. Intercepting Outgoing Events
intercept ["smth_important"]
def handle_out("smth_important", msg, socket) do
if … do
{:noreply, socket}
else
push socket, " smth_important", msg
{:noreply, socket}
end
end
80. The boundary for the Sales system.
libphoenix_newsalessales.ex
defmodule PhoenixNew.Sales do
def list_orders do
Repo.all(Order)
end
def get_order!(id), do: Repo.get!(Order, id)
def create_order(attrs %{}) do
%Order{} |> order_changeset(attrs) |> Repo.insert()
end
...
81. Action Fallback
On Phoenix 1.2, every controller needed to return a valid %Plug.Conn{} for
every request. Otherwise, an exception would rise.
On Phoenix 1.3 we can register the plug to call as a fallback to the
controller action. If the controller action fails to return a %Plug.Conn{},
the provided plug will be called and receive the controller’s
%Plug.Conn{} as it was before the action was invoked along with the
value returned from the controller action.
85. Ecto
Domain specific language for writing queries and interacting with
databases in Elixir.
pages.plataformatec.com.br/ebook-whats-new-in-ecto-2-0
86. Ecto
Ecto is split into 4 main components:
• Ecto.Repo - repositories are wrappers around the data store.
• Ecto.Schema - schemas are used to map any data source into an Elixir
struct.
• Ecto.Changeset - allow developers to filter, cast, and validate changes
before we apply them to the data.
• Ecto.Query - written in Elixir syntax, queries are used to retrieve
information from a given repository.
88. Repositories
Ecto.Repo is a wrapper around the database. We can define a
repository as follows:
defmodule Blog.Repo do
use Ecto.Repo, otp_app: :blog
end
89. Repositories
A repository needs an adapter and credentials to communicate to the database.
Configuration for the Repo usually defined in your config/config.exs:
config :blog, Blog.Repo,
adapter: Ecto.Adapters.Postgres,
database: "blog_repo",
username: "postgres",
password: "postgres",
hostname: "localhost"
90. Repositories
Each repository in Ecto defines a start_link/0. Usually this function is invoked as part
of your application supervision tree:
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [ worker(Blog.Repo, []), ]
opts = [strategy: :one_for_one, name: Blog.Supervisor]
Supervisor.start_link(children, opts)
end
91. Schema
Schemas allows developers to define the shape of their data.
defmodule Blog.User do
use Ecto.Schema
schema "users" do
field :name, :string
field :reputation, :integer, default: 0
has_many :posts, Blog.Post, on_delete: :delete_all
timestamps
end
end
92. Schema
By defining a schema, Ecto automatically defines a struct:
iex> user = %Blog.User{name: "Bill"}
%Blog.User{__meta__: #Ecto.Schema.Metadata<:built,
"users">, id: nil, inserted_at: nil, name: "Bill"},
posts: #Ecto.Association.NotLoaded<association
:posts is not loaded>, reputation: 0, updated_at:
nil}
93. Schema
Using Schema we can interact with a repository:
iex> user = %Blog.User{name: "Bill", reputation: 10}
%Blog.User{…}
iex> Blog.Repo.insert!(user)
%Blog.User{__meta__: #Ecto.Schema.Metadata<:loaded,
"users">, id: 6, inserted_at: ~N[2016-12-13
16:16:35.983000], name: "Bill", posts:
#Ecto.Association.NotLoaded<association :posts is not
loaded>, reputation: 10, updated_at: ~N[2016-12-13
16:16:36.001000]}
94. Schema
# Get the user back
iex> newuser = Blog.Repo.get(Blog.User, 6)
iex> newuser.id
6
# Delete it
iex> Blog.Repo.delete(newuser)
{:ok, %Blog.User{…, id: 6,…}}
95. Schema
We can use pattern matching on Structs created with Schemas:
iex> %{name: name, reputation: reputation} =
...> Blog.Repo.get(Blog.User, 1)
iex> name
"Alex"
iex> reputation
144
96. Changesets
We can add changesets to our schemas to validate changes before we
apply them to the data:
def changeset(user, params %{}) do
user
|> cast(params, [:name, :reputation])
|> validate_required([:name, :reputation])
|> validate_inclusion(:reputation, -999..999)
end
100. Changeset with Repository functions
case Blog.Repo.update(changeset) do
{:ok, user} ->
# user updated
{:error, changeset} ->
# an error occurred
end
101. We can provide different changeset functions
for different use cases
def registration_changeset(user, params) do
# Changeset on create
end
def update_changeset(user, params) do
# Changeset on update
end
102. Query
Ecto allows you to write queries in Elixir and send them to the
repository, which translates them to the underlying database.
103. Query using predefined Schema
# Query using predefined Schema
query = from u in User,
where: u.reputation > 35,
select: u
# Returns %User{} structs matching the query
Repo.all(query)
[%Blog.User{…, id: 2, …, name: "Bender", …, reputation: 42, …},
%Blog.User{…, id: 1, …, name: "Alex", …, reputation: 144, …}]
104. Direct query with “users” table
# Directly querying the “users” table
query = from u in "users",
where: u.reputation > 30,
select: %{name: u.name, reputation: u.reputation}
# Returns maps as defined in select
Repo.all(query)
[%{name: "Bender", reputation: 42}, %{name: "Alex", reputation: 144}]
105. External values in Queries
# ^ operator
min = 33
query = from u in "users",
where: u.reputation > ^min,
select: u.name
# casting
mins = "33"
query = from u in "users",
where: u.reputation > type(^mins, :integer),
select: u.name
106. External values in Queries
If the query is made against Schema than Ecto will
automatically cast external value
min = "35"
Repo.all(from u in User, where: u.reputation > ^min)
You can also skip Select to retrieve all fields specified in the Schema
107. Ecto Multi
Ecto.Multi is a data structure for grouping multiple Repo operations in a single database
transaction.
def reset(account, params) do
Multi.new
|> Multi.update(:account, Account.password_reset_changeset(account, params))
|> Multi.insert(:log, Log.password_reset_changeset(account, params))
|> Multi.delete_all(:sessions, Ecto.assoc(account, :sessions))
end
Repo.transaction(PasswordManager.reset(account, params))
108. Ecto Multi
case result do
{:ok, %{account: account, log: log, sessions: sessions}} ->
# We can access results under keys we used
# for naming the operations.
{:error, failed_operation, failed_value, changes_so_far} ->
# One of the operations failed.
# We can access the operation's failure value (changeset)
# Successful operations would have been rolled back.
end
109. Books
Saša Jurić “Elixir in Action”
https://www.manning.com/books/elixir-in-action
Benjamin Tan Wei Hao “The Little Elixir & OTP Guidebook”
https://www.manning.com/books/the-little-elixir-and-otp-guidebook
New! Lance Halvorsen “Functional Web Development with Elixir, OTP, and Phoenix”
https://pragprog.com/book/lhelph/functional-web-development-with-elixir-otp-and-phoenix
Chris McCord “Programming Phoenix” (1.2 -> 1.3)
https://pragprog.com/book/phoenix/programming-phoenix
“What's new in Ecto 2.0”
http://pages.plataformatec.com.br/ebook-whats-new-in-ecto-2-0