Skip to content

Instantly share code, notes, and snippets.

@chrismccord
Last active August 11, 2020 04:21
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save chrismccord/557ef22e2a03a7992624 to your computer and use it in GitHub Desktop.
Save chrismccord/557ef22e2a03a7992624 to your computer and use it in GitHub Desktop.

Phoenix 1.0.x to 1.1.0 upgrade instructions

Deps

Update your phoenix, phoenix_html deps, and if using ecto, update your phoenix_ecto dep

# mix.exs
def deps do
  [{:phoenix, "~> 1.1"},
   {:phoenix_ecto, "~> 2.0"},
   {:phoenix_html, "~> 2.3"},
   ...]
end

Now run $ mix deps.update phoenix phoenix_html phoenix_ecto

View / Template Changes

The @inner assign has been removed in favor of explicit rendering with render/3 and the new @view_module and view_template assigns.

In your web/templates/layout/app.html.eex (and other layouts), replace:

<%= @inner %>

with:

<%= render @view_module, @view_template, assigns %>

Ecto Changes

Ecto 1.1 has renamed Ecto.Model to Ecto.Schema and moved many Model functions to the Ecto module. Update your web/web.ex blocks:

def model do
  quote
-   use Ecto.Model
+   use Ecto.Schema

+   import Ecto
    import Ecto.Changeset
    ...  
  end 
end

def controller do
  quote
    ...
    alias MyApp.Repo
+   import Ecto
    import Ecto.Query, only: [from: 1, from: 2]
    ...  
  end 
end

def channel do
  quote
    ...
    alias MyApp.Repo
+   import Ecto
    import Ecto.Query, only: [from: 1, from: 2]
    ...  
  end 
end

Gettext (optional)

Gettext has been added for internationalization and localization support. See the Gettext docs for full details.

To add Gettext support to your application first add :gettext to your deps in mix.exs:

def deps do
  [...,
   {:gettext, "~> 0.9"},
   ...]
end

Next, in mix.exs, add the :gettext compiler to your project and :gettext to your :applications list in application list:

def project do
  [...,
-  compilers: [:phoenix] ++ Mix.compilers,
+  compilers: [:phoenix, :gettext] ++ Mix.compilers,
  ...]
end

def application do
  [mod: {MyApp, []},
-  applications: [:phoenix, :phoenix_html, :cowboy, :logger,
-                 :phoenix_ecto, :postgrex]]
+  applications: [:phoenix, :phoenix_html, :cowboy, :logger, :gettext
+                 :phoenix_ecto, :postgrex]]
end

Next, run $ mix deps.get

Now, create a gettext.ex file at web/gettext.ex with the following contents:

defmodule MyApp.Gettext do
  @moduledoc """
  A module providing Internationalization with a gettext-based API.

  By using [Gettext](http://hexdocs.pm/gettext),
  your module gains a set of macros for translations, for example:

      import MyApp.Gettext

      # Simple translation
      gettext "Here is the string to translate"

      # Plural translation
      ngettext "Here is the string to translate",
               "Here are the strings to translate",
               3

      # Domain-based translation
      dgettext "errors", "Here is the error message to translate"

  See the [Gettext Docs](http://hexdocs.pm/gettext) for detailed usage.
  """
  use Gettext, otp_app: :my_app
end

Replace MyApp with your app module and :my_app with your otp app.

Next run the following commands from your app root to add necessary gettext supporting files:

$ mkdir -p priv/gettext/en/LC_MESSAGES
$ curl https://raw.githubusercontent.com/phoenixframework/phoenix/277eb7dd03366b336458ffe8dbf637c133b595f0/installer/templates/new/priv/gettext/en/LC_MESSAGES/errors.po > priv/gettext/en/LC_MESSAGES/errors.po
$ curl https://raw.githubusercontent.com/phoenixframework/phoenix/277eb7dd03366b336458ffe8dbf637c133b595f0/installer/templates/new/priv/gettext/errors.pot > priv/gettext/errors.pot

Next, create a web/views/error_helpers.ex and add these contents:

defmodule MyApp.ErrorHelpers do
  @moduledoc """
  Conveniences for translating and building error messages.
  """
  use Phoenix.HTML

  @doc """
  Generates tag for inlined form input errors.
  """
  def error_tag(form, field) do
    if error = form.errors[field] do
      content_tag :span, translate_error(error), class: "help-block"
    end
  end

  @doc """
  Translates an error message using gettext.
  """
  def translate_error({msg, opts}) do
    # Because error messages were defined within Ecto, we must
    # call the Gettext module passing our Gettext backend. We
    # also use the "errors" domain as translations are placed
    # in the errors.po file. On your own code and templates,
    # this could be written simply as:
    #
    #     dngettext "errors", "1 file", "%{count} files", count
    #
    Gettext.dngettext(MyApp.Gettext, "errors", msg, msg, opts[:count], opts)
  end

  def translate_error(msg) do
    Gettext.dgettext(MyApp.Gettext, "errors", msg)
  end
end

And now you can import Gettext and MyApp.ErrorHelpers into your web.ex view block:

      import MyApp.Router.Helpers
+     import MyApp.ErrorHelpers
+     import MyApp.Gettext

ChangesetView changes

Changests are no longer encoded to errors when encoding to JSON. The changeset errors should be rendered explicitly. Update your web/views/changeset_view.ex (if it exists) with the following code:

defmodule MyApp.ChangesetView do
  use MyApp.Web, :view

  @doc """
  Traverses and translates changeset errors.

  See `Ecto.Changeset.traverse_errors/2` and
  `MyApp.ErrorHelpers.translate_error/1` for more details.
  """
  def translate_errors(changeset) do
    Ecto.Changeset.traverse_errors(changeset, &translate_error/1)
  end

  def render("error.json", %{changeset: changeset}) do
    # When encoded, the changeset returns its errors
    # as a JSON object. So we just pass it forward.
    %{errors: translate_errors(changeset)}
  end
end

If you are not using Gettext with the ErrorHelpers module, add this function to your ChangesetView:

def translate_error({msg, opts}) do
  String.replace(msg, "%{count}", to_string(opts[:count]))
end
def translate_error(msg), do: msg

Channels JavaScript Client changes

after hooks have been replaced by a timeout option on push, and a receive("timeout", callback) hook.

// 1.0.x
channel.push("new_message", {body: "hi!"})
       .receive("ok", resp => console.log(resp) )
       .after(5000, () => console.log("times! up"))

channel.push("new_message", {body: "hi!"})
       .receive("ok", resp => console.log(resp) )
       .after(12000, () => console.log("times! up"))

// 1.1.0
// timeout default to 5000
channel.push("new_message", {body: "hi!"}) 
       .receive("ok", resp => console.log(resp) )
       .receive("timeout", () => console.log("times! up"))

// custom timeout as optional 3rd arg
channel.push("new_message", {body: "hi!"}, 12000)
       .receive("ok", resp => console.log(resp) )
       .receive("timeout", () => console.log("times! up"))
@jimbeaudoin
Copy link

@notdevinclark Thank you for this. A little typo, it is translate_error without an s at the end.

@bartekupartek
Copy link

after update I can't run server, it seems that not all features are moved to Ecto.

== Compilation error on file web/models/user.ex ==
** (CompileError) web/models/user.ex:4: undefined function before_insert/1
    (stdlib) erl_eval.erl:669: :erl_eval.do_apply/6
    (elixir) lib/kernel/parallel_compiler.ex:100: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/8```

@tomwilsn
Copy link

@bartekupartek Check the Ecto 1.1 release notes (http://blog.plataformatec.com.br/2015/12/ecto-v1-1-released-and-ecto-v2-0-plans/), specifically the section titled "Goodbye callbacks"

@henrik
Copy link

henrik commented Dec 21, 2015

The diff above isn't quite accurate: web/web.ex doesn't indicate that a bunch of import Ecto.Model should also be removed (replaced by import Ecto).

I also had import Ecto.Model in test/support/model_case.ex and some other support files. I grepped for Ecto.Model and replaced every import Ecto.Model with import Ecto.

If you use ex_machina: I got errors like "** (UndefinedFunctionError) undefined function: Ecto.Association.loaded?/1" when running the tests. Went away by upgrading ex_machina from 0.5.0 to 0.6.0.

@the-guitarman
Copy link

I moved my project from phoenix 1.0.3 to 1.1 and I added gettext. Now I can't start my server:

==> gettext
could not compile dependency :gettext, "mix compile" failed. You can recompile this dependency with "mix deps.compile gettext", update it with "mix deps.update gettext" or clean it with "mix deps.clean gettext"
** (UndefinedFunctionError) undefined function: :yecc.file/2 (module :yecc is not available)
:yecc.file('src/gettext_po_parser.yrl', [parserfile: 'src/gettext_po_parser.erl', report: true])
(mix) lib/mix/compilers/erlang.ex:84: anonymous fn/3 in Mix.Compilers.Erlang.compile/3
...

UPDATE: I removed gettext and it works again.

@bansalakhil
Copy link

On ubuntu machine installing erlang-parsetools solved the issue

@TigerWolf
Copy link

When upgrading, it appears that my web.ex was missing:

      import Ecto.Changeset
      import Ecto.Query, only: [from: 1, from: 2]

This caused problems (cast/4 undefined function). It was only through creating a new project I was able to see the differences and add these missing lines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment