Rails: установить общую переменную экземпляра для нескольких действий контроллера

Как иметь несколько разных действий контроллера для установки общей переменной экземплярадля использования в шаблонах нопосле запуска действия.

Другими словами, я хочу, чтобы это работало в моем application_controller.

class ApplicationController < ActionController::Base
  after_filter :set_something_common

  def set_something_common
    # All controllers' actions have queried the DB and set @foo for me...
    @bar = some_calculation_on(@foo)
    # ... and all templates expect @bar to bet set.
  end
end

Этотне работает потому чтоafter_filter работает после рендеринга. Хорошо. Но каков правильный образец?

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

Ни одна из моих идей не кажется идеальной:

Вызовset_something_common() к основанию каждого действия, которое нуждается в этом.

Рефакторинг кода для каждого конкретного контроллера вcase_specific_code() и заставить их бежать по порядку:

before_filter :case_specific_code, :set_something_common

Подклассapplication_controller и переопределитьindex метод.

есть идеи? Благодарю.

Изменить: ответ Мэтью побудил меня уточнить:

Несколько контроллеров index () все выполняют разбиение на страницы, каждый из которых принимает параметры@offset а также@limit (через глобальныйbefore_filter) для просмотра срезов данных. Отлично. Теперь я хочу, чтобы общий метод вычислял URL RESTful для ссылки «следующий фрагмент». Я был рад видеть, чтоurl_for() генерирует URL, возвращающийся к тому же ресурсу, поэтому я попытался:

def set_something_common # really called set_next_url, truth be told
  @next_url = url_for(:offset => @offset + @limit, :limit => @limit)
end

Я попробую исправление обезьян Fixnum, чтобы я мог сделать что-то вроде@offset.next_url_for(self, @limit) из шаблона, но я не уверен, будет ли это работать. Если подумать, если я собираюсь изменить шаблоны, я могу также настроить помощника приложения. Я до сих пор не уверен, что это лучшее решение.

Обновление: Принятый ответ - «использовать помощника».

Спасибо за обновления от всех. Я усвоил урок, что помощники, такие как глобальные переменные, существуют по какой-то причине, и их не следует избегать, когда они явно полезны и лаконичны.

 Toby Hede02 июн. 2009 г., 00:54
Я думаю, что вы пытаетесь поместить в контроллер метод, который может быть более логично размещен внутри помощника.

Ответы на вопрос(3)

<%= do_some_calculations(@foo) %> внутри ваших шаблонов. Это прямой путь.

@foo который возвращает бар, таким образом, вы можете использовать@foo.bar в ваших взглядах.

<% @bar = @foo.bar %> # если вы действительно не хотите менять свои взгляды, но вы не слышали это от меня :)

 JasonSmith27 мая 2009 г., 13:51
Спасибо, Мэтью. Это отличная идея. Я не думал об этом, поскольку @foo - целое число, и мне нужно вычислить RESTful URL, но эй, я не понимаю, почему нет! Я уделю этому некоторое внимание и уточню вопрос. Я могу щедро пожертвовать этим ответом, и это приведет вас к главному утверждению!
Решение Вопроса

вы не хотите пытаться вставить код «между» действием контроллера и рендерингом шаблона. Почему? Потому что вы хотите, чтобы действие контроллера имело свободу выбора, какой ответ дать. Может возвращать XML, JSON, только заголовки, перенаправление, ничего и т. Д. Поэтому после выполнения фильтров после обработки ответа.

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

Вы упоминаете о рефакторинге кода каждого конкретного контроллера в фильтр before и последующем их запуске. Что наводит на мысль ...@foo то же самое в каждом случае? Если это так, то перед фильтром будет работать нормально:

before_filter :do_common_stuff
def do_common_stuff
  @foo = common_foo
  @bar = do_something_with @foo
end

Это абсолютно законный подход. Но если @foo изменится с контроллера на контроллер ... у вас есть еще несколько вариантов.

Вы можете разделить фильтры «до» на две половины и настроить одну для каждого контроллера.

# application_controller:
before_filter :get_foo, :do_something_common
def do_something_common
  @bar = do_something_with @foo
end

# baz_controller:
def get_foo
  @foo = pull_from_mouth
end

#baf_controller:
def get_foo
  @foo = pull_from_ear
end

Но вы знаете, если это простой случай, который не требует доступа к базе данных или сети или чего-то в этом роде ... чего в вашем случае нет ... не убивайте себя. И не парься. Брось это в помощника. Вот для чего они, чтобы помочь. Вы просто в любом случае просто переставляете некоторые данные представления в форму, которую легче использовать. Помощник мой голос. И вы можете просто назвать этоnext_url, :)

 Ian Terrell30 мая 2009 г., 18:34
Рад, что ты получаешь пищу для размышлений. Насколько распространен шаблон, в моей памяти за 3 года разработки Rails больше не выделялось ни одного случая, когда после действия, но перед рендерингом нужно было запускать идентичный код. Это всегда было так, что весь код, который был предоставлен, мог поместиться в фильтре before. Даже если ему нужен доступ к модели, которой он управляет (которая будет отличаться для каждого контроллера), если вы следуете соглашениям по именованию Rails, вы всегда можете сделать что-то вроде controller_name.singularize.camelize.constantize, чтобы получить к нему доступ. :)
 Ian Terrell29 мая 2009 г., 16:57
Использование before_filter в ApplicationController для этого только неудобно в той степени, что без какой-либо дополнительной настройки оно будет применяться к каждому действию в каждом контроллере. Я предпочитаю быть более скальпированным. Использование вспомогательного метода (строчный h, защищенный метод ApplicationController), который вызывается везде, где это необходимо, на самом деле тоже не страшно. Не все должно быть волшебным, вызывая такие методы, было сделано годами. Использование метода Helper (верхний регистр H, app / helpers / application_helper.rb) для генерации URL-адреса также является основным кандидатом, поскольку фактически он просто просматривает информацию.
 JasonSmith29 мая 2009 г., 12:42
Спасибо за подробный отзыв, Ян. Как вы можете видеть из моей обновленной ситуации, мой конкретный случай состоит в том, что метод индекса каждого контроллера устанавливает @offset и @limit, и я хочу, чтобы за этим стоял общий метод и устанавливал @next_url, чтобы мои шаблоны могли использовать все три. Таким образом, вы, вероятно, согласитесь, что неудобно перемещать очень типичный код метода индекса в фильтр before для того, чтобы соответствовать API. Мысли?
 JasonSmith30 мая 2009 г., 10:40
Еще раз спасибо за ваш ответ, Ян. На самом деле я выбрал вспомогательное решение h в нижнем регистре с планом рефакторинга на основе того, что я изучил здесь. Я согласен: не все должно быть волшебным; Однако я думал, что это обычная модель, чтобы повторить ту же задачупосле несколько действий, нодо рендеринг. В другом месте я читал, что «фильтр» - подходящее слово, и использование фильтров в качестве общих препроцессоров - это плохая форма. Это разумно. Я также рассматриваю возможность переноса либо метода index (), либо, возможно, render ().
 JasonSmith30 мая 2009 г., 10:43
Последняя мысль. Я перечитал ваше предложение о православном помощнике, и это может подойти. Мне просто нужно подтвердить, возвращает ли url_for () без аргумента RESTful URL для текущего ресурса, что и происходит, когда я вызываю его из контроллера. Если это так, то next_url может просто вернуть url_for (: next => @next + @skip,: skip => @skip)

Ваш ответ на вопрос