Этот вопрос неоднократно задавали, и на него отвечали однострочными утверждениями, такими как «потому что это очевидное нарушение MVC». Честно говоря, я просто не понимаю. В самом деле, мне кажется, что размещение сеанса внутри контроллера просто артефакт, который ApplicationController сталкивается с сетевым уровнем через вызов стойки, а не мандат MVC. Позвольте мне объяснить мою проблему.
Запуская аутентификацию с нуля, я обнаружил, что мучаюсь и всплескиваю повсюду из-за отсутствия возможности сформулировать простые тесты (сессия также недоступна для среды тестирования). Моя схема аутентификации, как и почти все, что я видел в rails, хотела использовать хэш сеанса в качестве уровня сохраняемости, чтобы сохранить идентификатор для модели пользователя «текущего пользователя». Разве это не похоже НАМНОГО БОЛЬШЕ на модель, чем на артефакт контроллера?
Запахи кода очевидны, когда вы смотрите на «типичный» контроллер сеансов (этот из превосходных скринкастов Райана Бейтса). Отчаявшись перелопатить эту концепцию с отдыхом, мы видим нездоровый язык, такой как:
def create
user = User.find_by_email(params[:session][:email])
if user && user.authenticate(params[:session][:password])
session[:user_id] = user.id
redirect_to root_url, notice: "Logged in!"
else
flash.now.alert = "Email or password is invalid"
render "new"
end
end
Для меня это запах кода, явно перегруженный контроллер, который требует рефакторинга! Но мы не можем. Почему? Ах да, потому что вставлять в модель ссылки на сессию, используемую в качестве юриста настойчивости, является нарушением MVC. ВТФ? Разве это не говорит вам о том, что мы, кажется, хотим НАЗВАТЬ ЭТОТ РЕСУРС REST /sessions?
Чтобы понять, почему это просто глупо, посмотрите на свои представления входа в систему — HTML-код, написанный вручную, или использование API «_tags»? Если бы у нас была модель ActiveModel для выполнения этого кода, то код создания мог бы выглядеть как обычные строительные леса или, возможно, даже сведен к однострочнику «respond_with».
def create
recognition = Recognition.new(params[:user])
if recognition.save
redirect_to root_url, :notice => "Thank you for signing up!"
else
render "new"
end
end
Затем взгляните на закодированный вручную html для всех этих представлений! Если бы распознавание было моделью, сохраняемой сеансом (или каким-либо другим способом, который в любом случае не должен быть обязанностью уровня контроллера), то вы могли бы просто использовать построитель форм или simple_form для создания форм. Конечно, мы могли бы просто передать хэш сеанса методу распознавания класса «new_login», скажем, Recognition.on(session).new(params[:recognition])
, но это кажется более уродливым, чем должно быть. Возможно, это неотъемлемо, так как мы захотим использовать ссылку current_user позже на уровне приложения, возможно, Recognition.on(session).current_user
аналогично тому, как можно было бы использовать одноэлементный шаблон?
Просто попробуйте создать свой пакет аутентификации со строгим BDD и честно скажите мне, что вы не добавили эту часть? Если бы у нас была модель распознавания, все это сводилось бы к простому набору модульных тестов без хакерства. Теперь вместо этого у нас есть «единственный» вариант использования для интеграционного тестирования, волшебное вторжение в модули ActiveController и хаки для ускорения любого приемочного тестирования предиката logged_in_as.
Я думаю, что весь смысл ActiveModel заключался в том, чтобы облегчить такое переосмысление и рефакторинг. Не все модели используют базу данных "the". Почему бы не настаивать на "сессии"?
Я слишком долго пользуюсь devise и его аналогами, хороня эти запахи под предлогом «не связывайся с драгоценным камнем», чтобы мне не приходилось на них смотреть. Больше никогда! Я думаю, что с этого момента я буду отвергать фанатиков. Извините, для меня сеанс — это уровень сохраняемости, которым следует манипулировать на уровне модели MVC. Я утверждаю, но не уверен, что причина, по которой он живет в мире контроллеров, больше связана с уродливым или элегантным фактом, что контроллеры являются стоечными объектами, чем с какой-либо теоретической магией MVC.
Итак, еще раз, есть ли более элегантный способ доступа к сеансовому уровню, чем иметь для этого логику в контроллере?