1
votes

I'm currently at learning Elixir Phoenix framework. I stuck at https://hexdocs.pm/phoenix/routing.html#forward now, it's about how to use forward command.

It's written there, that it's not neccessary to learn about Plugs now, so if I want to try out that forward command I can just copy their implementation of Plug into my application. But where exactly? I tried to search it out and tried to use what I found, but I am doing something wrong.

That's the scope from my .\lib\hello_web\router.ex file:

scope "/", HelloWeb do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  forward "/jobs", BackgroundJob.Plug, name: "Hello, Phoenix"
end

And that is the code for a Plug:

defmodule BackgroundJob.Plug do
  def init(opts), do: opts
  def call(conn, opts) do
    conn
    |> Plug.Conn.assign(:name, Keyword.get(opts, :name, "Background Job"))
    |> BackgroundJob.Router.call(opts)
  end
end

defmodule BackgroundJob.Router do
  use Plug.Router

  plug :match
  plug :dispatch

  get "/", do: send_resp(conn, 200, "Welcome to #{conn.assigns.name}")
  get "/active", do: send_resp(conn, 200, "5 Active Jobs")
  get "/pending", do: send_resp(conn, 200, "3 Pending Jobs")
  match _, do: send_resp(conn, 404, "Not found")
end

I supposed to know almost nothing about Plugs and Phoenix at all at this point, so it is probably something obvious, but I really stuck there for hours. Can anyone help me to find out where to place that code?

2

2 Answers

1
votes

Yes, I understand your confusion. It could be explained a little more clearly in the guides.

If you pass a module name to a scope, the passed in module name will get prefixed to all the modules in the scope.

So for example, assuming your passed in module name is HelloWeb, When you pass it into a scope like this:

scope "/", HelloWeb do
  ...
  get "/", PageController, :index
  forward "/jobs", BackgroundJob.Plug
end

the "/" route will look for HelloWeb.PageController
the "/jobs" route will look for HelloWeb.BackgroundJob.Plug

So to make your error go away, which I'm assuming is something like this:

helloweb.backgroundjob.plug.init/1 is undefined

You can either remove the passed in module name from the scope and prepend the module names by hand, leaving BackgroundJob.Plug unchanged like this:

scope "/",  do
  ...
  get "/", HelloWeb.PageController, :index
  forward "/jobs", BackgroundJob.Plug
end

Or, maybe save a little repetitive typing and add a "/jobs" scope instead, like this:

  scope "/", HelloWeb do
    ...
    get "/", PageController, :index
  end

  scope "/jobs" do
    forward "/", BackgroundJob.Plug, name: "Hello Phoenix"
  end

Please note, the ... in the examples represents pipe_through methods and other routes which were not essential to convey the idea.

Hope that helps.

0
votes

Welcome to stackoverflow.

You haven't posted what error you're getting, but I'm guessing your problem is module naming. In phoenix, modules used inside a scope block will get prefixed with the module name you give as an argument to that scope (HelloWeb in this case). So it should be enough to change the name of your plug module to HelloWeb.BackgroundJob.Plug. Alternatively you can not pass a scoping module to scope:

scope "/" do
  pipe_through :browser # Use the default browser stack

  get "/", HelloWeb.PageController, :index
  forward "/jobs", BackgroundJob.Plug, name: "Hello, Phoenix"
end

Note the change PageController -> HelloWeb.PageController in this case.

In terms of where to put it - any .ex file in lib will do, as elixir just compiles all of them. In fact you can just paste the code into your router.ex to test it out quickly. By convention the filename should follow the module name, so HelloWeb.BackgroundJob.Plug should live in lib/hello_web/background_job/plug.ex.