views:

60

answers:

4
+2  Q: 

Strange routing

How I can setup my rails app to respond to such urls:

http://mydomain.com/white-halogene-lamp

http://mydomain.com/children-lamps

http://mydomain.com/contact-form

The first one should link to my products controller, and show my product with this name

The second one should link to my categories controller, and show the category with this name

The third one should link to my sites controller, and show the site with this title.

All three models (product, category, site) have a method to_seo, giving the mentioned above urls (after the slash)

I know it's not the restful way, but please don't disuss here, it is wrong approach or not, that's not the question.

The question is how to accomplish this weird routing ?

I know we have catch all routes, but how I can tell rails to use different controllers on different urls in the catch all route ? Or have You an better idea ?

I would avoid redirects to other urls.

+1  A: 

It is a bit weird, especially if redirects are out of question, but one way to achieve this need might be to fill the route mapping from your tables - routes.rb is just another piece of ruby code, so if you can put

map.connect 'children-lamps', :controller => 'category', :action => 'show',  :id => 123

or whatever other way you would define it in routes.rb, then you can as well put

Category.each { |category|
  map.connect category.to_seo, :controller => 'category', :action => 'show', :id => category.id
}

but that would work only if your categories/sites are static, though.

Peteris
Wow - interesting solution. Kinda nasty, but cool at the same time :)
Taryn East
A: 

The best way is to add another layer, and run the routes like this:

http://yoursite.com/products/white-halogene-lamp

as this can be done with:

map.product_seo "prodcuts/:product", :controller => :products, :action => :show

then in Products#Show change @product=Product.find(params[:id]) to:

@product=Product.find_by_name(params[:product]) if params[:product] #This assumes name is the field you are searching on
@product=Product.find(params[:id]) unless params[:product]

that way the old routes still work but you get SEO urls aswell.

for the categories simply repeat and change as needed (url would be /categorys/children-lamps)

for the sites controller you really need the same thing, without the top level saying what you want you would have to define each route manually

Arcath
A: 

You can use route constraints for this.

class ProductExistsConstraint

  def matches?(request)
    Product.exists? :slug => request.path_parameters['slug']
  end

end

and similar classes for Category and Page. Then in the routes

match ":slug" => "products#show", :constraints => ProductExistsConstraint.new
match ":slug" => "categories#show", :constraints => CategoryExistsConstraint.new
match ":slug" => "page#show", :constraints => PageExistsConstraint.new

I didn't test this, but I think it's at least a pointer in the right direction. See also here: http://guides.rails.info/routing.html#advanced-constraints

Sven Koschnicke
A: 

I had to do something very similar. You can simply setup a generic handle action like this:

def handle
  @product = Product.find_by_permalink params[:permalink]
  if product
    return render_product
  end

  @category - Category.find_by_permalink
  ..
end

private

def render_product
  # something like this.
  render :file => 'products/show'
end
Oscar Del Ben