Как правильно запустить одно действие контроллера из другого действия контроллера без перенаправления HTTP? - PullRequest
8 голосов
/ 01 мая 2009

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

То, что у меня сейчас есть, выглядит примерно так:

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-запроса.

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

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

Ответы [ 6 ]

13 голосов
/ 01 мая 2009

Вместо вызова кода между действиями извлеките код в 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
8 голосов
/ 15 мая 2009

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

@my_other_controller = MyOtherController.new

Затем вызовите методы для этого:

@my_other_controller.some_method(params[:id])

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

1 голос
/ 16 мая 2009

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

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

Вариант 2 - Вставьте логику обратно в вашу модель. Pro's - держит контроллер в чистоте. Минусы - плохо работает, если у вас много сессонов, параметров или взаимодействий рендеринга / перенаправления.

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

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

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 голосов
/ 19 мая 2009

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

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

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

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

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

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

0 голосов
/ 19 мая 2009

Сделайте это:

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 голосов
/ 17 мая 2009

Если два контроллера пытаются сделать одно и то же, очень вероятно, что это должно быть в модели. Внимательно посмотрите на свой дизайн и - извините, я не знаю ваш уровень опыта работы с 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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...