Metaprogramming in Phoenix router

Recently, when reading Phoenix routing guide I stumbled upon description of resources:

scope "/", HelloPhoenix do
  pipe_through :browser

  resources "/users", UserController
end

And having Rails background I thought that it would be nice to get the framework guess the controller name based on path.

  resources "/users"

Then I added matching function to deps/phoenix/lib/phoenix/router.ex

defmacro resources(path) do
  controller_name = path                             # "/users"
    |> String.replace_prefix("/", "")                # "users"
    |> String.split("_")                             # ["users"]
    |> Enum.map(fn(el) -> String.capitalize(el) end) # ["Users"]
    |> Enum.join()                                   # "Users"
  # create a fiction result of quote function
  controller = {:__aliases__, [], [String.to_atom(controller_name <> "Controller")]}
  add_resources path, controller, [], do: nil
end

After phoenix recompilation with mix deps.compile phoenix new router function magically started to work.

# web/router.ex
scope "/", HelloPhoenix do
  pipe_through :browser # Use the default browser stack

  resources "/users"
end

scope "/admin", HelloPhoenix.Admin, as: :admin do
  pipe_through :browser

  resources "/users"
end

$mix phoenix.routes

      users_path  GET     /users                 HelloPhoenix.UsersController :index
      users_path  GET     /users/:id/edit        HelloPhoenix.UsersController :edit
      users_path  GET     /users/new             HelloPhoenix.UsersController :new
      users_path  GET     /users/:id             HelloPhoenix.UsersController :show
      users_path  POST    /users                 HelloPhoenix.UsersController :create
      users_path  PATCH   /users/:id             HelloPhoenix.UsersController :update
                  PUT     /users/:id             HelloPhoenix.UsersController :update
      users_path  DELETE  /users/:id             HelloPhoenix.UsersController :delete
admin_users_path  GET     /admin/users           HelloPhoenix.Admin.UsersController :index
admin_users_path  GET     /admin/users/:id/edit  HelloPhoenix.Admin.UsersController :edit
admin_users_path  GET     /admin/users/new       HelloPhoenix.Admin.UsersController :new
admin_users_path  GET     /admin/users/:id       HelloPhoenix.Admin.UsersController :show
admin_users_path  POST    /admin/users           HelloPhoenix.Admin.UsersController :create
admin_users_path  PATCH   /admin/users/:id       HelloPhoenix.Admin.UsersController :update
                  PUT     /admin/users/:id       HelloPhoenix.Admin.UsersController :update
admin_users_path  DELETE  /admin/users/:id       HelloPhoenix.Admin.UsersController :delete

So the question is about ugly transformation that tries to mimic #constantize method in Rails. Is there a better way to guess the constant based on the given string?

1 Like