views:

27

answers:

3

Hi all,

I am using Rails 2.3.2 but I'm sure this applies to newer versions as well. I would like to define a custom action in ApplicationController. However, I don't want to add a custom route to every single controller subclass which uses this action. Is there an easy way to do this?

My first inclination was to just route directly to the ApplicationController, since the method does not need to be overridden by any subclasses. But I don't think Rails lets you route to the ApplicationController anymore.

Somebody else suggested something like this:

map.connect ":controller/:action", :controller => my_regex, :action => my_regex

But I'm wondering if this has the potential of conflicting with or overriding other routes? Or if there's generally a better way? Thanks!

A: 

I don't think this is a case of modifying ApplicationController, but of monkey-patching the code in ActionDispatch::Routing to include the new actions you want. This seems like a pretty crazy thing to do in the scheme of things as there's no standard way to augment or extend the usual REST actions. I hope you've got a good reason for doing this.

In looking through the code you can see where the default actions are defined, and you might be able to introduce a new one. Rails 3 has a slightly different structure but the idea is the same:

    class ActionDispatch::Routing::Mapper::Resources::Resource
      ENHANCED_DEFAULT_ACTIONS = DEFAULT_ACTIONS + [ :myaction ]

      def self.default_actions
        ENHANCED_DEFAULT_ACTIONS
      end
    end

You'll have to modify ActionDispatch::Routing::Mapper::Resources#resources to behave differently, too, but you didn't specify if you're talking about a collection, new or member type of action so you'll have to just copy and modify the routine to behave as you want.

tadman
The reason I am doing this is because I want my application.js to be able to spit out Rails form tags. My application is heavy in AJAX and js, and I want centralized methods which give me Rails forms, inputs, etc. Basically, I'm trying to create a JavaScript FormBuilder.So, in my application.js, I would make an AJAX call to my custom action which would send down a js.erb file. This file would have a FormBuilder "class" with methods that give me the html generated by rails form builder tags. So when my application.js calls deleteWithAjax(), I can generate a Rails form with a Rails tag.
Samo
That sounds like a pretty interesting idea, and it could work well, but the question is why does it require diverging so much from the standard REST actions. What you really need is to define JavaScript responders for your methods that will process the forms properly. You could probably do this a lot more easily by intercepting and auto-handling the generation of the .js response if no template is found than by introducing new actions.
tadman
That's an interesting thought. Could you perhaps point me to an example of this? In all cases thus far, my response to an ajax call is either a js.erb template or a json object. I would like both the returned js.erb templates and the handlers for the json responses to have access to my js form builder. So the no-template-found scenario might not be adequate. Perhaps another alternative would be to make js_form_builder a resource which only responds to get, and my application.js file could call to that controller in order to load the form builder.
Samo
Creating a resource for it works pretty well. Now I have to tackle the issue of the correct way to route to the resource if I want to pass in a resource name and id for the javascript form builder to use :)
Samo
A: 

if you are declaring this controller as a resource such as map.resource then you will have to use the default actions or create your own by adding a member or collection to that resource.

 map.resources :post, :member => {:update_live_boolean => :post }, :collection => {:get_all_live_posts => :get}

Otherwise if you have the old routing format and are not using REST

  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'

Then all you need to do to link to a custom controller is provide the :controller' and:action/:id` variables when needed

<%= link_to "New Custom Controller", {:controller => "new_custom_controller", :action => "index"%>
Sam
A: 

This is the part of your post that concerns me:

I don't want to add a custom route to every single controller subclass which uses this action.

The rails community is rightly getting more and more careful about not over-defining needless routes. It's getting more common to see things like this:

map.resources :comments, :only => [:new, :create]

In the above example, only the new and create routes are generated. It's better security, and cleaner routing. While I'm not directly answering your question of how to make a new route available to every resource, I'm saying that rails best practices would discourage it. Add the custom route only to the resources that will actually use it.

Jaime Bellmyer
I understand your concern, but if I want every controller to use this custom action, then it seems rather redundant to explicitly route it for every controller, no?
Samo
Honestly, I don't think so. Because I can't think of a single real-world application where *every* resource would need it. Also, there are a finite number of routes in an application, usually only a couple dozen. Believe me, I really understand your desire to keep your code DRY - I'm the same way. And I wouldn't have answered with a "non-answer" unless I felt strongly about it. I do understand the annoyance of asking something, and having people say "just don't do it" :)
Jaime Bellmyer