Singular Resources in Phoenix
I recently stumbled across Elixir and Phoenix. The more I dig in, the more I’m drinking the Kool-Aid and loving it. Coming from Rails, things feel familiar, but there has definitely been some grinding of gears as I’ve ramped up.
One of the things I ran into today was how to make a “singular resource” for something like a user profile or account. My first instinct was to do something like this:
# web/router.ex
scope "/", MyApp do
    ...
    resource "/account", UserController
end
That didn’t work. It complained about not finding the function resource/2:
== Compilation error on file web/router.ex ==
** (CompileError) web/router.ex:31: undefined function resource/2
    (phoenix) expanding macro: Phoenix.Router.scope/3
    web/router.ex:17: MyApp.Router (module)
    (elixir) lib/kernel/parallel_compiler.ex:116: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
At this point, I reached for The Docs, but my awful tethered connection on BART refused to load the page. I consequently started throwing together a ham-fisted solution along the lines of “write all the routes out by hand:”
# web/router.ex
scope "/", MyApp do
    ...
    # Dear future self, I'm sorry. =/
    get "/account", UserController, :show
    get "/account/edit", UserController, :edit
    put "/account", UserController, :update
    ...
end
That was all fine and great until I printed out the routes and realized I should probably handle PATCH on :update in addition to PUT:
# web/router.ex
scope "/", MyApp do
    ...
    # Dear future self, I'm sorry. =/
    get "/account", UserController, :show
    get "/account/edit", UserController, :edit
    put "/account", UserController, :update
    patch "/account", UserController, :update
    ...
end
While I’m relatively sure that this should work, it just felt gross. Thankfully, by this point my phone felt like connecting to the network again, so I was able to pull up the docs. I quickly found resources/4 and noticed the singleton option. There’s a helpful blurb right afterward that perfectly matches what I was looking for:
Singleton resources
When a resource needs to be looked up without referencing an ID, because it contains only a single entry in the given context, the
:singletonoption can be used to generate a set of routes that are specific to such single resource:
[…]
Usage example:
resources "/account", AccountController, only: [:show], singleton: true
So there you go. All my artisanal hand-crafted routes can be replaced with a single call to resources:
# web/router.ex
scope "/", MyApp do
    ...
    resources "/account", UserController, singleton: true
    ...
end