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
 
44
Kudos
 
44
Kudos

Now read this

Generating SQLite Databases at Build Time with Xcode

Background # Some years ago I worked on v2.0 of the Fly Delta app for Delta Air Lines. As an initial part of that effort, we mapped out the data model of what we would need in order to support all of the features that were planned. When... Continue →