Рекомендации для контроллера: несколько методов или несколько обращений в Show

#ruby-on-rails #ruby #controller

#ruby-on-rails #ruby #контроллер

Вопрос:

Я часто создаю контроллеры, в которых мне хотелось бы использовать несколько методов (в дополнение к индексированию, редактированию, показу и т.д.). В большинстве случаев действия, которые я желаю, могут быть объединены в show, поскольку они являются простыми операциями GET, однако я не хочу вкладывать слишком много логики в какое-либо одно действие контроллера.

Вот краткий пример двух разных способов достижения одной и той же цели…

 class TwitterFriendController < ApplicationController
  ## lump everything into show?
  def show
    if params[:id] == "follow"
      users = current_user.following
    elsif params[:id] == "follow_me"
      users = current_user.users_who_follow_me
    elsif params[:id] == "following_follow_me"
      users = current_user.following_who_follow_me
    elsif params[:id] == "following_who_do_not_follow_me"
      users = current_user.following_who_do_not_follow_me
    ...
    end
    respond_with do |format|
      format.json do {...}
    end
  end

  ## or split everything out into separate methods, this requires
additional routing
  def following
    ...
  end

  def users_who_follow_me
    ...
  end

  def following_who_follow_me
    ...
  end

  def following_who_do_not_follow_me
    ...
  end
end
  

Все в show

  • тонна логики в одном методе
  • СУХОЙ? # для логики требуется много дополнительного кода
  • Меньше маршрутизации

Отдельные методы

  • Дополнительная маршрутизация
  • не работает
  • Простой поиск метода
  • Легче читать отдельные методы

Итак, снова реальный вопрос в том, какой из этих методов менее плох.

Ответ №1:

Я бы сделал что-то вроде:

 FOLLOW_WHITELIST = %w[ follow follow_me following_follow_me following_who_follow_me following_who_do_not_follow_me ]

def show
    if FOLLOW_WHITELIST.include? params[:id]
        users = current_user.send params[:id].to_sym
    end
    respond_with do |format|
        format.json do {...}
    end
end
  

Это вызовет любой метод, переданный в params[:id], при условии, что он находится в белом списке (чтобы предотвратить внедрение произвольного кода).

Если наличие отдельных маршрутов было для вас плюсом (более приятные URL-адреса?), вы также могли бы динамически генерировать методы и маршруты с помощью чего-то вроде этого:

 class TwitterFriendController < ApplicationController

    FOLLOW_ACTIONS = %w[ follow follow_me following_follow_me following_who_follow_me following_who_do_not_follow_me ]

    FOLLOW_ACTIONS.each do |action|
        define_method action do
            users = current_user.send action.to_sym
            respond_with do |format|
              format.json do {...}
            end
        end
    end

end
  

А затем в routes.rb:

 FOLLOW_ACTIONS.each do |action|
    match action.to_sym => "controller##{action}"
end
  

Комментарии:

1. очень элегантно. Однако, как только одному из этих методов потребуется дополнительная логика, которая не может или не должна быть включена в модель, вы практически потеряли все свои достижения. Я бы также не стал заходить так далеко, чтобы define_method для моих методов контроллера на рабочей машине, он сохраняет все локальные переменные в области видимости на момент определения и использует (немного) больше памяти. Если у кого-то есть какие-либо дополнительные мысли, мне интересно услышать, прежде чем я объявлю официальный «ответ»

2. Я не уверен, какого другого решения вы ждете. Как только одному из этих методов требуется дополнительная логика, использование другого метода для него больше не противоречит принципам «DRY».

3. Я остановился на шаблоне presenter и одном методе show. «Решения» нет, но я подумал, что получу несколько разных ответов. Спасибо за информацию, вот несколько моментов.