Most Rails developers have written apps where you have a marketing homepage and a route that you should be redirected to after signing in. There may even be different pages to be redirected to after signing based on some sort of role system.
Who wants to remember that route across the app? No one.
Instead of writing a nasty helper to determine where to send a user after signing in, let’s have Rails handle this for us.
Enter Rails routing constraints.
It turns out Rack is a beautiful piece of software! With Rails routing
constraints, we can re-use the Rails’ root
to handle all this logic for us.
This means no more remembering where root_url
will take you or stuffing all
that logic into some giant switch statement!
For example, I recently wrote an app using Clearance and high_voltage
. When
visiting the root path without being signed in, I wanted to render high
voltage’s ‘homepage’ view. If the user was signed in, I wanted to display an
administrative page where they could manage their assets.
My routes file looks like this:
root to: "assets#index", constraints: Clearance::SignedInConstraint
root to: "high_voltage/pages#show", id: "homepage"
Two roots! My constraint to hitting assets#index is based off my class
Clearance::SignedInConstraint
, which I’ve thrown in lib/
.
# lib/clearance/signed_in_constraint.rb
module Clearance
class SignedInConstraint
def self.matches?(request)
request.env[:clearance].signed_in?
end
end
end
This looks to request.env[:clearance]
(introduced in Clearance 0.13), which
returns a Clearance::Session
instance, and allows us to call #signed_in?
.
With Rails constraints, if .matches?
returns true, the constraint is
fulfilled and the router allows assets#index to do its thing. When false, the
route ‘falls through’ and the next root kicks in.
Constraints are a great way to scope routes based on particular preconditions and may end up cleaning up a handful of nasty conditional logic in a view helper. Constraints can be great for things like subdomains as well, but I’d love to hear about some more inventive uses.