As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure, first class umbrella project support, and scaffolding that re-enforces phoenix as a web-interface to your greater Elixir application. We have also included a new action_fallback
feature in Phoenix.Controller that allows you to translate common datastructures in your domain to valid responses. In practice, this cleans up your controller code and gives you a single plug to handle otherwise duplicated code paths. It is particularly nice for JSON API controllers.
For those interested in a detailed overview of the changes and design decisions, check out my LonestarElixir keynote that just went live: https://www.youtube.com/watch?v=tMO28ar0lW8
To use the new phx.new
project generator, you can install the archive with the following command:
$ mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez
As always, we have an upgrade guide with detailed instructions for migrating from 1.2.x projects: https://gist.github.com/chrismccord/71ab10d433c98b714b75c886eff17357
1.3.0 is a backwards compatible release, so upgrading can be as easy as bumping your :phoenix dep in mix.exs to “~> 1.3”. For those wanting to adopt the new conventions, the upgrade guides will take you step-by-step. Before you upgrade, it’s worth watching the keynote or exploring the design decisions outlined below.
Phoenix 1.3 – Design With Intent
The new project and code generators take the lessons learned from the last two years and push folks towards better design decisions as they’re learning. Changes to new projects include moving web
inside lib
to avoid any special directories, as well as the introduction of the new Web
namespace convention to further signal that your Phoenix related web modules are the web interface into your greater Elixir application. Along with new project structure, comes new phx.gen.html
and phx.html.json
generators that adopt these goals of isolating your web interface from your domain.
Contexts
When you generate a HTML or JSON resource with phx.gen.html|json
, Phoenix will generate code inside a Context, which is simply a well-named module, with well-named functions representing an API boundary to part of your application domain.
For example, to generate a “user” resource we’d run:
$ mix phx.gen.html Accounts User users email:string:unique
Notice how “Accounts” is a new required first parameter. This is the context module where your code will live that carries out the business logic of user accounts in your application. Here’s a peek at part of the code that’s generated:
# lib/my_app/web/controllers/user_controller.ex
defmodule MyApp.Web.UserController do
...
alias MyApp.Accounts
def index(conn, _params) do
users = Accounts.list_users()
render(conn, "index.html", users: users)
end
def create(conn, %{"user" => user_params}) do
case Accounts.create_user(user_params) do
{:ok, user} ->
conn
|> put_flash(:info, "user created successfully.")
|> redirect(to: user_path(conn, :show, user))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end
...
end
# lib/my_app/accounts/accounts.ex
defmodule MyApp.Accounts do
@moduledoc """
The boundary for the Accounts system.
"""
alias MyApp.Accounts.User
def list_users do
Repo.all(User)
end
def create_user(attrs \\ %{}) do
%User{}
|> user_changeset(attrs)
|> Repo.insert()
end
...
end
You will also have an Ecto schema generated inside lib/my_app/accounts/user.ex
. Notice how our controller calls into an API boundary to create or fetch users in the system. We are still using Ecto, but the database and application logic are separated. Our web-interface doesn’t need to know the details of the storage or DB representatin of our User. The Accounts module could internally store users in an agent, ETS, or elsewhere and our controller code remains untouched.
By asking users to think about the boundaries of their APIs, we end up with more maintainable, well structured code. Additionally, we can get a glimpse of what the application does and its feature-set just be exploring the application directory structure:
lib/my_app
├── accounts
│ ├── user.ex
│ └── accounts.ex
├── sales
│ ├── ticket.ex
│ ├── manager.ex
│ └── sales.ex
├── repo.ex
└── web
├── channels
├── controllers
├── templates
└── views
With just a glance at the directory structure, we can see this application has a User Accounts system, as well as sales system. We can also infer that there is a natural boundary between these systems thru the sales.ex
and accounts.ex
modules. We can gain this insight without seeing a single line of code. Contrast that to the previous web/models
, which did not reveal any relationship between files, and mostly reflected your database structure, providing no insight on how they actually related to your domain.
We are excited about these changes and their long-term payoff in maintainability. We also feel they’ll lead to sharable, isolated libraries that the whole community can take advantage of – inside and outside of Phoenix related projects.
If you have issues upgrading, please find us on #elixir-lang irc or slack and we’ll get things sorted out!
I would also like to give a special thank you to @wojtekmach for his help getting the new generators ready for prime-time. <3
Happy coding!
-Chris
Full changelog:
1.3.0-rc.0 (2017-03-01)
-
Enhancements
- [Generator] Add new
phx.new
,phx.new.web
,phx.new.ecto
project generators with improved application structure and support for
umbrella applications - [Generator] Add new
phx.gen.html
andphx.gen.json
resource
generators with improved isolation of API boundaries - [Controller] Add
current_path
andcurrent_url
to generate a
connection’s path and url - [Controller] Introduce
action_fallback
to registers a plug to
call as a fallback to the controller action - [Controller] Wrap exceptions at controller to maintain connection
state - [Channel] Add ability to configure channel event logging with
:log_join
and:log_handle_in
options - [Channel] Warn on unhandled
handle_info/2
messages - [Channel] Channels now distinguish from graceful exits and
application restarts, allowing clients to enter error mode and
reconnected after cold deploys. - [Router] document
match
support for matching on any http method
with the special:*
argument - [ConnTest] Add
redirected_params/1
to return the named params
matched in the router for the redirected URL
- [Generator] Add new
-
Deprecations
- [Generator] All
phoenix.*
mix tasks have been deprecated in
favor of newphx.*
tasks
- [Generator] All
-
JavaScript client enhancements
- Add ability to pass
encode
anddecode
functions to socket
constructor for custom encoding and decoding of outgoing and incoming
messages. - Detect heartbeat timeouts on client to handle ungraceful
connection loss for faster socket error detection - Add support for AMD/RequireJS
- Add ability to pass