1
votes

From this tutorial, I have some elegant:

get("/old-path", Redirector, to: "/new-path")
get("/old-path/:id", Redirector, to: "/new-path?object=:id")

in my router.ex.

But I now have a more complex case, which requires my redirection to be dynamic. I wanted to go for:

get("/other-old-path", Redirector, to: fn conn, options -> "/whatever-path" end)

And wrote on my Redirector module:

to = if is_function(to), do: to.(conn, Keyword.drop(options, :to)), else: to

I was pretty confident with my code, but when I tried to execute it, I received the following error:

== Compilation error in file web/router.ex ==
** (ArgumentError) cannot escape #Function<5.71083082/2 in :elixir_compiler_10.__MODULE__/1>. The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, PIDs and remote functions in the format &Mod.fun/arity
    (elixir) src/elixir_quote.erl:122: :elixir_quote.argument_error/1
    (elixir) src/elixir_quote.erl:259: :elixir_quote.do_quote/3
    (elixir) src/elixir_quote.erl:310: :elixir_quote.do_escape/3
    (phoenix) lib/phoenix/router/route.ex:136: Phoenix.Router.Route.build_dispatch/1
    (phoenix) lib/phoenix/router/route.ex:73: Phoenix.Router.Route.exprs/1
    (phoenix) lib/phoenix/router.ex:334: anonymous fn/1 in Phoenix.Router."MACRO-__before_compile__"/2
    (elixir) lib/enum.ex:1327: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1327: Enum."-map/2-lists^map/1-0-"/2
    (phoenix) expanding macro: Phoenix.Router.__before_compile__/1
    web/router.ex:1: MyApp.Router (module) 
    (elixir) lib/kernel/parallel_compiler.ex:208: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

Googling it sent me to this page which mentions I can't do what I want to do. I read this has to do with quoted expressions so I tried to understand how this can be useful, but still don't.

I tried to pass an anonymous function in a keyword list in a parameter of a function in the console and it worked.

I'm guessing it breaks here because the behavior of get is more meta.

Anyway, I'm sure I'll get more comfortable along working deeper with Elixir, and one day I'll clearly understand why it did not work (explanations welcome of course!), but by then, how can I write what I want to do?

It doesn't feel like I am doing something elixir-difficult but it apparently is...

Cheers


EDIT: thanks for reply.

I did what you suggested @justin-wood but it doesn't work:

get("/other-old-path", Redirector, to: &MyApp.Router.do_redirect/2)

and at the bottom of the file

def do_redirect(conn, options), do: "/youpi"

But I get a hideous error:

== Compilation error in file web/router.ex ==
** (FunctionClauseError) no function clause matching in :v3_core.pattern/2    

    The following arguments were given to :v3_core.pattern/2:

        # 1
        {:fun, 0, {:function, {:atom, 0, Vae.Router}, {:atom, 0, :do_redirect}, {:integer, 0, 2}}}

        # 2
        {:core, 2, 0, {:redirector_path, 2}, ... [more uglyness]
1
** (FunctionClauseError) no function clause matching in :v3_core.pattern/2 is a compiler bug. Please open up a bug report with a minimal case, including the Elixir and Erlang/OTP versions! Thanks.José Valim

1 Answers

3
votes

cannot escape #Function<5.71083082/2 in :elixir_compiler_10.MODULE/1>. The supported values are: ... remote functions in the format &Mod.fun/arity

If you use a named function instead of anonymous function, it should work. So in the case of

defmodule Foo do
  def bar do
    :baz
  end
end

You would use &Foo.bar/0 to reference the function.