Root path for multiple controllers on rail routes

I have two resource controllers in which I am using slug to represent the id. (gem friendly_id).

I can show the path for one resource on the route, but not two at the same time. i.e.

root :to => 'home#index'
match '/:id' => "properties#show"
match '/:id' => "contents#show"


Basically, I need urls like

# Content
# Property


Whatever resource I make from above. Is there a way to do this?

Thanks if you can help.


source to share

5 answers

This is untested, but try using it constraint

on your route.

root :to => 'home#index'
match '/:id', :to => "properties#show",
              :constraints => lambda { |r| Property.find_by_id(r.params[:id]).present? }
match '/:id', :to => "contests#show",
              :constraints => lambda { |r| Contest.find_by_id(r.params[:id]).present? }


Alternatively, you can create a separate class that responds matches?

instead of defining lambda

proc. (I recommend putting these classes in separate files that will be automatically loaded into your Rails application.)

# app/constraints/property_constraint.rb
class PropertyConstraint
  def self.matches?(request)
    property = Property.find_by_id(request.params[:id])

# app/constraints/contest_constraint.rb
class ContestConstraint
  def self.matches?(request)
    contest = Contest.find_by_id(request.params[:id])

# config/routes.rb
root :to => 'home#index'
match '/:id', :to => "properties#show", :constraints => PropertyConstraint
match '/:id', :to => "contests#show", :constraints => ContestConstraint


Unfortunately, this leads to an additional DB request (once in routes and again in your controller). If anyone has a suggestion for minimizing this please share. :)



This Rails engine does what you want:

Slug Engine on Github

Basically, the author's approach was to install the Rails Engine inside its main application. This engine includes both a controller for working with existing slugs and a piece of middleware for filtering and refraining from non-working slugs.

He explains why he used this approach and other interrupted solutions in a fairly detailed and interesting blog post . This blog post and slug code source should be detailed enough for you to get your own code up and running, but this open source engine seems to be exactly what you are looking for if you want an affordable solution.



You can do this in middleware

  • Bullet detection in transit
  • If there is content with this bullet - change the request path to "contents /: id"
  • If there is a property with this slug - change the request path to "properties /: id"
  • in your routing set:

    match 'contents /: id' => "properties # show"

    match 'properties /: id' => "content # show"



You could write another controller that takes an ID from the router and checks if that ID belongs to properties or content and displays the appropriate view.

match '/:id' => "router#show"


The controller will do something like this:

def show
    @property = Property.find(params[:id])
    if @property then
        render 'property/show'
        @content = Content.find(params[:id])
        render 'content/show


Haven't tested this code, but this idea should work.



I would suggest that you do this in a more RESTful way, if possible. Basically, you have two different resources and you have to separate them:

match 'properties/:id' => "properties#show"
match 'contents/:id'   => "contents#show"


This will give you many advantages along the way. One instant benefit is that you can avoid collisions between identifiers for properties and content. (Note that friendly_id will not help you with collisions between patterns in your original schema.)



All Articles