14

I would like to be able to map URLs to Controllers dynamically based on information in my database.

I'm looking to do something functionally equivalent to this (assuming a View model):

map.route '/:view_name',
    :controller => lambda { View.find_by_name(params[:view_name]).controller }

Others have suggested dynamically rebuilding the routes, but this won't work for me as there may be thousands of Views that map to the same Controller

Community
  • 1
  • 1
Daniel Beardsley
  • 19,907
  • 21
  • 66
  • 79
  • The controller need not be determined by a database record, I just want some way to evaluate which controller to use for a given route at Runtime, instead of Designtime. – Daniel Beardsley Apr 09 '10 at 18:13
  • Still never found a solution to this, though it seems like a simple task. Maybe a Rack-Application that re-writes URLs? – Daniel Beardsley Jan 16 '11 at 23:20

4 Answers4

13

This question is old, but I found it interesting. A fully working solution can be created in Rails 3 using router's capability to route to a Rack endpoint.

Create the following Rack class:

    class MyRouter
      def call(env)
        # Matched from routes, you can access all matched parameters
        view_name= env['action_dispatch.request.path_parameters'][:view_name]

        # Compute these the way you like, possibly using view_name
        controller= 'post' 
        my_action= 'show'

        controller_class= (controller + '_controller').camelize.constantize
        controller_class.action(my_action.to_sym).call(env)
      end
    end

In Routes

    match '/:view_name', :to => MyRouter.new, :via => :get

Hint picked up from http://guides.rubyonrails.org/routing.html#routing-to-rack-applications which says "For the curious, 'posts#index' actually expands out to PostsController.action(:index), which returns a valid Rack application."

A variant tested in Rails 3.2.13.

Deepak Kumar
  • 944
  • 9
  • 12
0

As suggested in the question Rails routing to handle multiple domains on single application, I guess you could use Rails Routing - Advanced Constraints to build what you need.

If you have a limited space of controllers (with unlimited views pointing to them), this should work. Just create a constraint for each controller that verifies if the current view matches them.

Assuming you have a space of 2 controllers (PostController and CommentController), you could add the following to your routes.rb:

match "*path" => "post#show", :constraints => PostConstraint.new
match "*path" => "comment#show", :constraints => CommentConstraint.new

Then, create lib/post_constraint.rb:

class PostConstraint     
  def matches?(request)
    'post' == Rails.cache.fetch("/view_controller_map/#{request.params[:view_name]}") { View.find_by_name(request.params[:view_name]).controller }
  end
end

Finally, create lib/comment_constraint.rb:

class CommentConstraint     
  def matches?(request)
    'comment' == Rails.cache.fetch("/view_controller_map/#{request.params[:view_name]}") { View.find_by_name(request.params[:view_name]).controller }
  end
end

You can do some improvements, like defining a super constraint class that fetches the cache, so you don't have to repeat code and don't risk fetching a wrong cache key name in one of the constraints.

Community
  • 1
  • 1
barbolo
  • 3,807
  • 1
  • 31
  • 31
0

So I think that you are asking that if you have a Views table and a View model for it where the table looks like

id | name | model
===================
1  | aaa  | Post
2  | bbb  | Post
3  | ccc  | Comment

You want a url of /aaa to point to Post.controller - is this right?

If not then what you suggest seems fine assuming it works.

You could send it to a catch all action and have the action look at the url, run the find_by_name and then call the correct controller from there.

def catch_all
  View.find_by_name('aaa').controller.action
end

Update

You can use redirect_to and even send the params. In the example below you I am sending the search parameters

def catch_all
  new_controller = View.find_by_name('aaa').controller
  redirect_to :controller => new_controller, :action => :index, 
      :search => params[:search] 
end
Will
  • 8,102
  • 5
  • 30
  • 32
  • How do you "call the correct controller"? Rails maintains instances of each controller and sets up Requests contexts, populates `params`, and lots of other stuff, how do I instruct rails to forward the request from within one controller to another one? – Daniel Beardsley Apr 08 '10 at 03:52
  • redirect_to will send a [302 HTTP](http://en.wikipedia.org/wiki/HTTP_302) response to the client, causing the browser to re-request the new URL. This isn't what we want. – Daniel Beardsley Apr 08 '10 at 05:58
  • You used to be able to use render_component in pre rails 2.3 but if you still want to do that then checkout the http://github.com/rails/render_component plugin – Will Apr 08 '10 at 11:40
  • "redirect_to" doesn't look like an answer for the question – barbolo Feb 04 '13 at 09:00
0

Here is a nice Rack Routing solution to SEO contributed by zetetic and Steve ross

Testing Rack Routing Using rSpec

It shows you how to write a custom dispatcher (where you can do a db lookup if needed) and with constraints, and testing as well.

Community
  • 1
  • 1
Agustin
  • 1,254
  • 13
  • 10