Segment Constraints

Sometimes you may want to add some constraints on the dynamic segments in a route. This is where the constraints option that we saw earlier, comes into the picture. You can add restrictions on the segment keys by providing a constraint.

The following route matches only those URLs where slug is a word consisting of letters between a to z.

get '/posts/:slug', to: 'posts#show', constraints: { slug: /[a-z]+/ }

# or you can also use a shorthand
get '/posts/:slug', to: 'posts#show', slug: /[a-z]+/

Here's another example: you may want to restrict the id to three digits. For this, you can pass the :constraint option as follows:

get 'images/:id', to: 'images#show', constraints: { id: /\d{3}/ }

This route would match paths such as /images/293, but not /images/xyz.

The above route can be succinctly expressed by passing the segment key itself.

get 'photos/:id', to: 'photos#show', id: /\d{3}/

Custom Constraint Logic

For more powerful constraints, you can pass a lambda to :constraints which receives the request object.

get '/posts/:slug', to: 'posts#show', constraints: ->(req) { req.params[:slug].length < 10 }

Rails also allows you to extract the complex logic to a class that has a matches(request) method on it. Put that class inside the app/constraints directory, and Rails will pick it up without a hitch.

get '/posts/:slug', to: 'posts#show', constraints: SlugLength.new

# app/constraints/slug_length.rb
class SlugLength
  def matches?(request)
    request.params[:slug].length < 5
  end
end

Don't forget to return a boolean result from the method, depending on whether the URL matches the pattern.

Alright, that's enough about the segment keys. Next, we'll tackle the named routes, which give you sweet helper methods to generate the URLs on the fly.


If you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. You can also subscribe to my blog to receive future posts via email.