2009-04-30 2 views
8

Я хотел бы иметь возможность отправлять от одного действия контроллера другому, в зависимости от комбинации параметров запроса и данных в базе данных.Каков правильный способ запуска одного действия контроллера из другого действия контроллера без перенаправления HTTP?

То, что я сейчас что-то вроде:

class OldController < ApplicationController 
    def old_controller_action 
    if should_use_new_controller 
     new_params = params.dup 
     new_params[:controller] = "new_controller_action" 
     redirect_to new_params 
     return 
    end 
    # rest of old and busted 
    end 
end 

class NewController < ApplicationController 
    def new_controller_action 
    # new hotness 
    end 
end 

Это работает просто отлично, но он выдает HTTP редирект, который медленно. Я хотел бы иметь возможность делать то же самое, но в том же HTTP-запросе.

Есть ли чистый способ сделать это?

Редактировать: Щедрость пойдет к кому-то, кто может показать мне чистый способ сделать это, что оставляет контроллеры и их действия относительно нетронутыми (кроме самого кода переадресации).

ответ

13

Вместо вызова кода между действиями извлеките код в lib/или что-то еще и вызовите этот код с обоих контроллеров.

# 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

Да, я тоже об этом подумал, хотя это не очень впечатляющий вариант, с точки зрения организации кода. –

+1

Это на самом деле нормально, поскольку он создает на связном методе, называемом из двух мест, легко проверяемых –

7

Создать экземпляр класса контроллера:

@my_other_controller = MyOtherController.new 

вызывает методы на нем:

@my_other_controller.some_method(params[:id]) 

Я предпочитаю идею модуля, но это необходимо сделать трюк.

+0

Это не так хорошо работает. Трудно назвать метод «render» внутри этого нового экземпляра контроллера. Если вы вызываете рендер в исходном контроллере, вы потеряете все переменные, установленные в вызываемом контроллере. – holli

1

Я подозреваю, что вы хотите, вариант 3, но позволяет пройти через некоторые альтернативы первые

Вариант 1 - Нажмите на логику выбора контроллера в качестве помощника, который вставляет правильную ссылку в вашей точки зрения. Benifits - контроллеры остаются чистыми, Минусы - если логика принятия решения в зависимости от представленных значений, этот подход не будет работать. Если URL-адрес вызывается внешними веб-сайтами, это не сработает.

Вариант 2 - Вставьте логику обратно в вашу модель. Pro - держит контроллер чистым. Минусы - не работают, если у вас есть много sesson, params или render/redirect_to взаимодействия.

Вариант 3 - Оставайтесь в пределах одного контроллера. Я подозреваю, что вы пытаетесь заменить некоторые существующие функции некоторыми новыми функциями, но только в некоторых случаях. Pro - Простой и доступ ко всему, что вам нужно. Минусы - работает только в том случае, если имеет смысл использовать один и тот же контроллер, то есть вы работаете с одним и тем же лицом, например, с пользователем, местом или компанией.

Давайте рассмотрим в качестве примера для варианта 3. Мои ссылки контроллер имеет полностью behavour для с различного админов, чем другие пользователи ...

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

Если два контроллера пытаются сделать то же самое, что есть очень хороший Вероятно, это должно быть в модели.Всмотритесь в ваш дизайн и - Прости, что я не знаю свой уровень опыта с MVC - читать на тонких методов контроллера:

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

Если проблема что вам нужен другой контроллер, чтобы сделать рендер, тогда, возможно, маршрут должен был указывать туда для начала, и все же тонкая техника контроллера должна сохранить день.

-1

ли это:

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

и новый контроллер

class NewController < OldController 
    def new_controller_action 
    # new hotness 
    end 
end 
0

Если извлечение общего кода между контроллерами в модуль не работает для вас, я хотел бы использовать Rack Middleware. Я не видел код, который использует ActiveRecord в промежуточном программном обеспечении, но я не знаю, почему это было невозможно, поскольку люди использовали Redis и тому подобное.

В противном случае я думаю, что единственный вариант будет перезапустить обработку запроса с чем-то вроде (непроверенной, псевдо, например):

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

Это похоже на то, как реализуются интеграционные тесты. Но я не знаю, все ли от call до тех пор, пока вы не нажмете на контроллер, является идемпотентным и безопасным для повторного запуска. Вы можете проследить через источник и посмотреть. Но даже если теперь все в порядке, это может сломаться в любой будущей версии рельсов или стойки.

Использование промежуточного программного обеспечения позволило бы избежать этого, позволяя вам перехватить запрос до его запуска. Вы все равно сможете использовать код с помощью своего приложения rails, извлекая его из общих модулей, включенных в оба места.

Честно говоря, я думаю, что просто делать простые вещи, связанные с общим кодом контроллера, скорее всего, чище, но это трудно понять без деталей вашей ситуации, поэтому я подумал, что буду идти дальше и предлагать это.

Смежные вопросы