What's the correct way to run one controller action from another controller action without an HTTP redirect?

后端 未结 6 494
无人及你
无人及你 2020-12-30 09:33

I\'d like to be able to dispatch from one controller action to another conditionally, based on a combination of query parameters and data in the database.

What I hav

相关标签:
6条回答
  • 2020-12-30 09:44

    Create an instance of the controller class:

    @my_other_controller = MyOtherController.new
    

    Then call methods on it:

    @my_other_controller.some_method(params[:id])
    

    I prefer the module idea, but this should do the trick.

    You can also pass parameters as a whole from another controller:

    @my_other_controller.params = params
    
    0 讨论(0)
  • 2020-12-30 09:45

    If two controllers are trying to do the same thing, there's a very good chance this should be in a model. Take a good look at your design and -- I'm sorry I don't know your experience level with MVC -- read up on thin controller techniques:

    http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model http://www.robbyonrails.com/articles/2007/06/19/put-your-controllers-on-a-diet-already http://andrzejonsoftware.blogspot.com/2008/07/mvc-how-to-write-controllers.html

    If the problem is that you need the other controller to do the render, then maybe the route should have pointed there to begin with, and still the skinny controller technique should save the day.

    0 讨论(0)
  • 2020-12-30 09:47

    I suspect you want option 3, but lets go through the some alternatives first

    Option 1 - Push the controller selection logic into a helper that inserts the right link into your view. Benifits - controllers remain clean, Cons - if decision logic depending on submitted values this approach won't work. If URL is being called by external websites then this won't work.

    Option 2 - Push the logic back into your model. Pro's - keeps controller clean. Cons - doesn't work well if you've got lots of sesson, params or render / redirect_to interaction.

    Option 3 - Stay within the same controller. I suspect you are trying to replace some existing functionality with some new functionality, but only in some cases. Pro's - Simple and have access to everything you need. Cons - only works if it makes sense to use the same controller i.e. you're working with the same entity such as user, place or company.

    Lets look an an example for option 3. My links controller has totally diferent behavour for admins than other users ...

    class LinksController < ApplicationController
      #...
    
      def new
        #Check params and db values to make a choice here
        admin? ? new_admin : new_user
      end
    
      #...
    
    private
    
      def new_admin
        #All of the good stuff - can use params, flash, etc 
        render :action => 'new_admin'    
      end
    
      def new_user
        #All of the good stuff - can use params, flash, etc 
        render :action => 'new_user' 
      end
    
    end
    
    0 讨论(0)
  • 2020-12-30 09:53

    Do this:

    class OldController < ApplicationController
      def old_controller_action
        if should_use_new_controller
          new_controller_action
        end
        # rest of old and busted
      end
    end
    

    and the new controller

    class NewController < OldController
      def new_controller_action
        # new hotness
      end
    end
    
    0 讨论(0)
  • 2020-12-30 10:08

    Instead of calling code across actions, extract the code to lib/ or something, and call that code from both controllers.

    # lib/foo.rb
    module Foo
      def self.bar
      # ...
      end
    end
    
    # posts_controller
    def index
      Foo.bar
    end
    
    # things_controller
    def index
      Foo.bar
    end
    
    0 讨论(0)
  • 2020-12-30 10:09

    If extracting the common code between controllers into a module doesn't work for you, I would use Rack middleware. I haven't seen code that uses ActiveRecord within middleware but I don't know of any reason why it shouldn't be possible since people have used Redis and the like.

    Otherwise I think your only option would be to restart processing of the request with something like (untested, pseudo example):

    env['REQUEST_URI'] = new_controller_uri_with_your_params
    call(env)
    

    This is similar to how integration tests are implemented. But I don't know if everything from call until you hit a controller is idempotent and safe to rerun like this. You could trace through the source and see. But even if it's ok now, it might break in any future version of rails or rack.

    Using middleware would avoid this by letting you intercept the request before it's been run. You should still be able to share code with your rails application by extracting it out into common modules included in both places.

    Honestly I think just doing the simple thing of factoring the common controller code is likely cleaner, but it's hard to know without the details of your situation so I thought I'd go ahead and suggest this.

    0 讨论(0)
提交回复
热议问题