How to support multiple versions of a package with moved functions?

I am just playing around with Phoenix 1.18-dev and an upcoming change is the following:
Phoenix.LiveView.assign/3 moved to Phoenix.Component.assign/3

This is a breaking change (which is fine).
When compiling my updated code against Phoenix 1.17.x The expected

module Phoenix.Component is not loaded and could not be found

pops up.

Is there a way to have both locations supported… eg by doing something like (which does not work):

      try do
        import Phoenix.Component, only: [assign: 3, assign_new: 3]
      rescue
        import Phoenix.LiveView, only: [assign: 3, assign_new: 3]
      end

What would be the proper way to handle this?

You can check the phoenix version like this:

Version.match?("#{Application.spec(:phoenix_live_view, :vsn)}", "~> 0.18")
      if Version.match?("#{Application.spec(:phoenix_live_view, :vsn)}", "~> 0.18") do
        import Phoenix.Component, only: [assign: 3, assign_new: 3]
      else
        import Phoenix.LiveView, only: [assign: 3, assign_new: 3]
      end

seems to be evaluated at runtime… and needs to be handled by the compiler first.

** (CompileError) lib/somefile.ex:4: module Phoenix.Component is not loaded and could not be found
(stdlib 3.17.2.1) lists.erl:1358: :lists.mapfoldl/3
(stdlib 3.17.2.1) lists.erl:1359: :lists.mapfoldl/3
(stdlib 3.17.2.1) lists.erl:1358: :lists.mapfoldl/3
(elixir 1.13.4) expanding macro: Kernel.if/2

For this you need a macro. Here is an example script:

# required minimal config to fix warnings
Application.put_env(:phoenix, :json_library, Jason)

# we also need to install json library dependency
# and one of phoenix_live_view versions
Mix.install([:jason, phoenix_live_view: "~> 0.17.0"])
# or
Mix.install([:jason, phoenix_live_view: "~> 0.18"])

defmodule MyLib do
  defmacro my_macro(app, version_match, opts) do
    vsn = app |> Application.spec(:vsn) |> List.to_string()
    if Version.match?(vsn, version_match), do: opts[:do], else: opts[:else]
  end
end

defmodule Example do
  require MyLib

  MyLib.my_macro :phoenix_live_view, "~> 0.18" do
    import Phoenix.Component, only: [assign: 3, assign_new: 3]
  else
    import Phoenix.LiveView, only: [assign: 3, assign_new: 3]
  end
end
1 Like

Thank you… learning something new every day :slight_smile: