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

Now read this

Deciphering Xcode’s index

At work we’re having to wait an inordinate amount of time for Xcode to finish indexing our rather large Swift project. I’ve consequently spent a lot of time over the past few weeks digging into the internals of indexing. This is more or... Continue →