Skip to content

Instantly share code, notes, and snippets.

@jeregrine
Forked from chrismccord/phoenix-0.15-upgrade.md
Last active August 29, 2015 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeregrine/0a096ac131ecf200590c to your computer and use it in GitHub Desktop.
Save jeregrine/0a096ac131ecf200590c to your computer and use it in GitHub Desktop.
Phoenix upgrade instructions 0.14.x to 0.15.0

Sockets and Channels

A new socket behaviour has been introduced to handle socket authentication in a single place, wire up default channel assigns, and disconnect a user's multiplex connection as needed.

First things first, create a UserSocket module in web/channels/user_socket.ex and move all your channel routes from web/route.ex to the user socket: (replace MyApp with your application module)

0.14.x - web/router.ex:

defmodule MyApp.Router do
   ...
  socket "/ws", MyApp do
    channel "rooms:*", RoomChannel
  end
end

0.15.0 - web/channels/user_socket.ex:

defmodule MyApp.UserSocket do
  use Phoenix.Socket

  ## Channels
  channel "rooms:*", MyApp.RoomChannel

  ## Transports
  transport :websocket, Phoenix.Transports.WebSocket
  transport :longpoll, Phoenix.Transports.LongPoll

  # Socket params are passed from the client and can
  # be used to verify and authenticate a user. After
  # verification, you can put default assigns into
  # the socket that will be set for all channels, ie
  #
  #     {:ok, assign(socket, :user_id, verified_user_id)}
  #
  #  To deny connection, return `:error`.
  def connect(_params, socket) do
    {:ok, socket}
  end

  # Socket id's are topics that allow you to identify all sockets for a given user:
  #
  #     def id(socket), do: "users_socket:#{socket.assigns.user_id}"
  #
  # Would allow you to broadcast a "disconnect" event and terminate
  # all active sockets and channels for a given user:
  #
  #     MyApp.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{})
  #
  # Returning `nil` makes this socket anonymous.
  def id(_socket), do: nil
end

Leaving the default implementations of connect/2 and id/1 will give you the same behavour you had with 0.14.x.

Next, remove the socket mount from your Router and add it to your lib/my_app/endpoint.ex and add a mount for phoenix live reload if using :phoenix_live_reload:

defmodule MyApp.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  socket "/ws", MyApp.UserSocket

  plug Plug.Static, ...

  # Code reloading can be explicitly enabled under the
  # :code_reloader configuration of your endpoint.
  if code_reloading? do
    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
    plug Phoenix.LiveReloader
    plug Phoenix.CodeReloader
  end
  
  ...
end

Channel handle_out callbacks

If using handle_out/3 callbacks in your channels, you now must explicit list which events you want your channels to intercept and pass through handle_out. This change brings large performance improvements by caching encodings across all subscribers if the event is not intercepted by the channel:

0.14.x:

defmodule MyApp.RoomChannel do
  use MyApp.Web, :channel
  ...
  
  def handle_out("new_msg", payload, socket) do
    push socket, "new_msg", %{role: socket.assigns.role, body: payload["body"]}
    {:noreply, socket}
  end
end

0.15.0:

defmodule MyApp.RoomChannel do
  use MyApp.Web, :channel
  
  intercept ["new_msg"]
  ...
  
  def handle_out("new_msg", payload, socket) do
    push socket, "new_msg", %{role: socket.assigns.role, body: payload["body"]}
    {:noreply, socket}
  end
end

Bump your deps

Next update your :phoenix, :phoenix_html, :phoenix_ecto, and :phoenix_live_reload deps in mix.exs:

def deps do
  [...,
   {:phoenix, "~> 0.15"},
   {:phoenix_ecto, "~> 0.8"},
   {:phoenix_html, "~> 1.4"},
   {:phoenix_live_reload, "~> 0.5", only: :dev},
  ...]
end

Next, run $ mix deps.update

Ecto changes (can skip if not using Ecto)

We are updating two versions fo Ecto from version 0.12 to 0.14/0.15-dev most of the changes you need to make will be reflected in this section taken from the Ecto Changelog. If you have further issues or compiler errors please check the Ecto Changelog for further details.

Below is taken from the change for v0.13 backwards incompatible changes.

  • Ecto.Repo.update!/2 no longer invokes callbacks if there were no changes, avoiding writing to the database at all (use :force to force callback execution)
  • Ecto.Repo.transaction/2 is now flattened. This means that multiple transaction calls will no longer use savepoints, instead it will be considered as a single transaction, where a failure in any transaction block will trigger the outmost transaction to rollback, even if failures are rescued. This should only affect users that were explicitly relying on the savepoints.
  • :date, :time and :datetime were removed in favor of Ecto.Date, Ecto.Time and Ecto.DateTime
  • Ecto.Changeset.errors now return {"must be less than %{count}", count: 3} instead of {"must be less than %{count}", 3}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment