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 :singleton option 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
 
29
Kudos
 
29
Kudos

Now read this

Versioning and Xcode

If you’ve ever used agvtool to manage versioning your project you know it’s a pain. Among other complaints, I’d rather not have version information buried in project files (or have to modify that project file to bump the version).... Continue →