5

I'm trying to write a catch-all route in Rails 3, but I want to reserve some terms in it. I'm specifically following the example put forth in this post, in the answer by David Burrows: Dynamic routes with Rails 3

The syntax I am using is the following:

match '*path' => 'router#routing', :constraints => lambda{|req|  (req.env["REQUEST_PATH"] =~ /(users|my-stuff)/).nil? }

Now, that syntax works just fine - if a user visits a page with "user" or "my-stuff" in the path, it falls through the catch-all and goes to a specific place. If the user goes to any other URL, it goes to my routing logic.

My question is more about readability - is there a way I can match the route against something other than a regex? Is there a way to provide an array of terms to match against? Also, is there a way to match specific segments of the route, as opposed to the entire thing?

Obviously Rails has built-in routing, but this project has a requirement that for certain routes, the controller not be present in the URL. Hence, the catch-all.

Thanks for any help

Here's the updated routes file per the answer below:

class RouteConstraint
  RESERVED_ROUTES = ['users', 'my-stuff']

  def matches?(request)
    !RESERVED_ROUTES.map {|r| request.path.include?(r)}.empty?
  end
end

App::Application.routes.draw do
  resources :categories
  resources :sites

  match '*path' => 'router#routing', :constraints => RouteConstraint.new

  devise_for :users, :path_names =>{ :sign_in => 'login', :sign_out => 'logout', :registration => 'register' }
  root :to => "router#routing"
end
Community
  • 1
  • 1
Kevin Whitaker
  • 12,435
  • 12
  • 51
  • 89

2 Answers2

6

You can use a class to specify the constraints if you want something cleaner once you have multiple routes to try:

class MyConstraint
  BYPASSED_ROUTES = ['users', 'my-stuff']

  def matches?(request)
    BYPASSED_ROUTES.map {|r| request.path.include?(r)} .empty?
  end
end

TwitterClone::Application.routes.draw do
  match "*path" => "router#routing", :constraints => MyConstraint.new
end

This example is adapted from the rails routing guide.

Benoit Garret
  • 14,027
  • 4
  • 59
  • 64
  • This gets close, but now instead of bypassing the "hidden routes" listed, the logic tries to feed them to the routing controller. If I remove the bang or the .empty? call in the matches method, the logic works for reserved words, but not for everything else. Sorry, my grasp of lambdas is pretty limited. – Kevin Whitaker Oct 07 '11 at 14:29
  • Ah, I misunderstood your question then, my logic was the exact opposite of yours. Could you post a bit more of your routes files, to see how the catch-all fits in there? – Benoit Garret Oct 07 '11 at 14:37
  • Ok, I've added the current routes to my question above. – Kevin Whitaker Oct 07 '11 at 14:44
  • 1
    Catch-all routes are usually put at the very end, have you any particular reason for putting it in the middle? – Benoit Garret Oct 07 '11 at 15:07
  • Moving it to the end fixed the problem. I didn't have any reason to have it in the middle at all. – Kevin Whitaker Oct 07 '11 at 15:14
0

It's taking a lambda; you can use whatever criteria you want.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302