Scott's Recipes Logo

Learning Phoenix from Sonny

Overview

Below are my roughly stream of consciousness notes from Sonny Scrogson’s Taking off With Phoenix class. Sonny did a fantastic job on this class and he is to be absolutely commended for it. Any errors below, and I know they exist, are mine not his. He earned every bit of the applause he got. Thanks to Esteban for pushing me to post this publicly. Some notes were omitted due to markdown conflicts; I’ll get them up when I can.

Update

My version of the class repo is here: https://github.com/fuzzygroup/taking-off-with-phoenix I put JSJ in the code where there was something I thought important enough to remember. Bear in mind that I’m still new to Elixir.

I added screenshots at the bottom for all of the screen shots showing errors / surprising things. When you see big blanks that means that nothing was output.

Quick Links:

Examples of Mix Commands

Mix is the equvalent of bundler, the rails executable and rake:

Things I Learned

Equivalencies to the Rails / Ruby World

Layers:

Surprising

There are other flavors of languages on top of erlang:

Erlang VM

1 thread is run for every core on the machine (each scheduler above has a thread assigned to it)

Processes

Process

Isolated & Concurrent

OTP

GenServers, Supervisors, Processes, Oh My!

Phoenix

Phoenix is a framework for building modern web apps, API backends and distributed systems. Written in Elixir, you get beautiful syntax, productive tooling and a fast and efficient runtime (thanks to the BEAM)

Phoenix is an OTP application that provides functionality to your OTP application

Getting Stuff Built from Scratch

Misc

Processing by Workshop.PageController.index/2

/2 is the “ARITY OF THE FUNCTION” arity = number of arguments that the function accepts

2 top level directories - lib, web The web directory is likely to go to lib down the road

This is a list of two tuples:

[“ecto.setup”: [“ecto.create”, “ecto.migrate”, “run priv/repo/seeds.exs”], “ecto.reset”: [“ecto.drop”, “ecto.setup”], “test”: [“ecto.create –quiet”, “ecto.migrate”, “test”]]

toople <== correct pronunciation is toople (according to Sonny at least)

tuple

elem {:key, “value”}, 0 :key

elem {:key, “value”}, 1 “value”

[{:key, “value”}] <== keyword list

[{:key, “value”}]

[“key”:, “value”] <== the : makes it an atom. THE : MUST GO AT THE END

[“key.value”:, “value”] <== the . would change things to a method send so you have to

keyword lists are preferred over maps because you can omit the braces for the last argument

keyword lists can have the same key multiple times ; maps you cannot

maps are easier to pattern match on so used more

(A few examples omitted here due to escaping issues; there’s irony in that due to Tom Preston Warner’s ruby orientation) COFFEE BREAK

Building blocks

(get from photo)

Plug

https://github.com/elixir-lang/plug

At the core of phoenix

a specification for constructing composable modules to build web applications. Plugs are reusable modules or functions built to this specification

Plugs can be written to handle almost anything form authentication to paramenter preprocessing and rendering

Provides adapters to http servers which will ultimately deliver content to users

Plug Specification

Function Plugs

Function Plug Example

def json_header_plug(conn, opts) do
  conn 
  |> put_resp_content_type("application/json")
end

Most of the applications I build now are like this - functions which transform state bit by bit

Module Plugs

A module plug is an extension of the function plug. It is a module that must export:

The result report by init/1 is passed as a second argument to call/2

Module Plug Example

defmodule JSONHeaderPlug do import Plug.Conn

def init(opts), do: opts
  
def call(conn, _opts) do
  conn
  |> put_resp_content_type("application/json")
end   end

plug.conn is huge (look it up)

Request Fields

Fetchable Fields

Response Fields

Connection Fields

Plugs in Phoenix

Endpoint

Endpoint is boundary of the app. Where all requests flow (slide transition too fast)

Endpoint

plug Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: [“/”], json_decoder: Poison

Router

Provides a bunch of macors for generating routes that dispatch to specific controllers and actions. Those macros are named after HTTP verbs.

defmodule Myapp.Router do use Myapp.Web, :router

scope “/”, Myapp do get “/”, PageController, :index resources “/rooms”, RoomController do resources “/users”, UserController end end end

####Router

this

get “/”, PageController, :index

compiles to this (showing full function definition)

defp match(conn, “GET”, [], _) do conn |> Plug.conn.put_private(:phoenix_pipelines, [:browser]) |> Plug.conn …(omitted)

end

Router

mix task

mix phoenix.routes

page_path get /

Pipelines

connection |> endpoint |> router |> controller |> send_resp

Router Pipelines

pipeline :browser do plug :accepts, [“html”] plug :fetch_session plug :fetch_flash plg end

Now into the workshop!

Ecto

mix help grep ecto

mix ecto.gen.migration create_users

priv is an ERLANG thing and represents “files private to the application”

mix ecto.migrate

Repo is the ecto repository

Repo is in charge of sending the queries to the db

There could be a 1 week course on using ecto

Web is where all our phoenix specific stuff goes

model right now are business logic and WILL BE MOVING but for now are in web

In the future will be more small apps that are composed up to bigger apps

“dunder” means “__” (its short for double underscore)

in mix.exs you define the name Workshop (initial name to the directory structure)

Ecto does not have call backs

Immutable Immutable Immutable !!!

Remember everything is immutable

%Ecto.Changeset{} #Ecto.Changeset<action: nil, changes: %{}, errors: [], data: nil, valid?: false>

Workshop.User.changeset(%Workshop.User{})
#Ecto.Changeset<action: nil, changes: %{},
 errors: [name: {"can't be blank", []}, email: {"can't be blank", []},
  password: {"can't be blank", []},
  password_confirmation: {"can't be blank", []}], data: #Workshop.User<>,
 valid?: false>
 
  Workshop.User.changeset(%Workshop.User{email: "foo@bar.com"})
 #Ecto.Changeset<action: nil, changes: %{},
  errors: [name: {"can't be blank", []}, password: {"can't be blank", []},
   password_confirmation: {"can't be blank", []}], data: #Workshop.User<>,
  valid?: false>
  
  h v (help on v)
  
  c = v(2)  (get the 2nd previous thing out of the history)
  
  
  if true, do: 1, else: 2
  
  if true do 
    1
  else
    2
  end
  
  quote do: 1 + 1
  {:+, [context: Elixir, import: Kernel], [1, 1]}   <== this is the AST internal to elixir (3 element tuple)
  
  quote do: if true, do: 1, else: 2
  
  {:if, [context: Elixir, import: Kernel], [true, [do: 1, else: 2]]}

https://hexdocs.pm/ecto/Ecto.html

Goal is that your web interface is separate from your domain logic

Storage != Database

ETS = Erlang Table System

http://erlang.org/doc/man/ets.html

You can use ecto to handle data validations that ARE NOT a db

Wrap the form in an ecto changeset to allow signup for an ejabberd system

View

view is a module that renders the template

~E is equivalent to .html_safe but it goes before not after

IT IS ALL ABOUT DATA STRUCTURES NOT GETTERS / SETTERS !!!!

Changeset

Workshop.User.changeset

Workshop.Repo.get_by([email: “fuzzygroup@gmail.com”])

Screenshots

This was the progress of error messages we all saw as we went thru the training.

taking_on_phoenix_screen_01.png

taking_on_phoenix_screen_02.png

taking_on_phoenix_screen_03.png

taking_on_phoenix_screen_04.png

taking_on_phoenix_screen_05.png

taking_on_phoenix_screen_06.png

taking_on_phoenix_screen_07.png

taking_on_phoenix_screen_08.png

taking_on_phoenix_screen_09.png

taking_on_phoenix_screen_10.png

taking_on_phoenix_screen_11.png

taking_on_phoenix_screen_12.png

taking_on_phoenix_screen_13.png

taking_on_phoenix_screen_14.png

taking_on_phoenix_screen_15.png

taking_on_phoenix_screen_16.png

taking_on_phoenix_screen_17.png

taking_on_phoenix_screen_18.png

taking_on_phoenix_screen_19.png