I’ve been messing around with Rails for the past few months, and I just got through figuring this out so I thought I’d post this here.
Sometimes you want to have a dynamic segment in a route. For example: /:username, to allow your users to have vanity urls. You might use something like this:
However, you obviously don’t want routes like /login or /logout to be routed to your controller (in the above case, the profiles controller). In this case we would use segment constraints. Rails allows you to use either a regular expression as shown in this rails guide on segment constraints.
The other method is to use a Constraint class:
Reference the ProfileConstraint class by creating a new directory/file called app/constraints/profile_constraint.rb:
def self.matches?(request)
reserved_words = ['login', 'signup', 'signout', 'welcome', 'home']
!reserved_words.include?(request.path_parameters[:username])
end
end
Above, Rails looks for the matches? method in ProfileConstraint sends it a request object, which we then use to decide whether to match the route or not by comparing it against an array of reserved_words.
Another useful way to add on to the above would be to use scope. This allows you to extend your route like /:username/comments:
get '' => 'profiles#show'
get '/comments' => 'profile#comments'
end
And now you’ll have routes like so:
following GET /:username/comments(.:format) {:controller=>"profiles", :action=>"comments"}
Hope someone finds this useful as I did.
Tips for improving:
- Dynamically add model/controller classnames and routes to the reserved_words array.
One Comment
thanks for this. I was previously using get instead of scopes in my routes. well done brobotslacker.