#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. «Решения» нет, но я подумал, что получу несколько разных ответов. Спасибо за информацию, вот несколько моментов.