Reputation: 5472
So I'm doing some metaprogramming magic in Phoenix 1.3 router.ex file.
I need to use Demo.Repo to fetch some data from the database while I'm magically assembling some routes. How do I use this in router.ex?
When I run mix phx.routes
, I get an error:
repo Demo.Repo is not started, please ensure it is part of your supervision tree
Do I need to edit the application.ex file somehow to start it in a different supervision tree?
Here's what I have:
defmodule DemoWeb.Router do
use Utils
# other routes not included
car_resources("/cars", CarController, 1)
end
defmodule Utils do
@moduledoc """
Helper functions for autogenerating routes
"""
alias Demo.Repo
alias CarType
defmacro __using__(_options) do
# quote gets the representation of any expression
quote do
import unquote(__MODULE__) # import __MODULE__ into the user's code
@before_compile unquote(__MODULE__)
end
end
defmacro car_resources(path, controller, level) do
quote do
resources unquote(base_path(path, level), unquote(controller)
end
end
def base_path(path, level) do
# do stuff
end
def cars do
%{
1 => Repo.get_by!(CarType, name: "ford"),
2 => Repo.get_by!(CarType, name: "honda"),
}
end
end
Upvotes: 1
Views: 148
Reputation: 51429
This approach doesn't work because you are depending on the database during compilation time. So in order to define the routes, you need to compile CarType and then start the repository by calling start_link
on it.
While Elixir will be smart enough to figure those dependencies out, it shows a dangerous level of coupling. For example, if you add a new CarType, you need to recompile your code. It feels awkward having to deploy new code because a new type of cars was added to the database.
Instead I would nest the routes by the :car_type
and always fetch the car type dynamically:
scope "/cars/:car_type" do
resources "/cars", CarController
end
Then you fetch the :car_type
in a plug and store it in the connection.
Upvotes: 1
Reputation: 121010
Ecto.Repo
is a GenServer
in a nutshell, so one might go with explicit start/stop lifecycle:
def cars do
with {:ok, pid} <- Demo.Repo.start_link() do
cars = %{
1 => Repo.get_by!(CarType, name: "ford"),
2 => Repo.get_by!(CarType, name: "honda"),
}
Demo.Repo.stop(pid)
cars
else
error -> IO.inspect(error, label: "unable to connect")
end
end
While this would probably work, I would strongly suggest using some text file (or any other independent source) rather than a database. First of all, test database is recreated on each subsequent run and you’ll likely end up with empty routes file during test sessions. Also, reading the config from the thing that is configured by the same config smells like the chicken-egg issue.
Upvotes: 2