Примечание. В настоящее время я использую Rails 4.0.0, а ветвь Rails, которую я просматривал на Github, была 4-0-stable
. Код, который вы видите в этом посте, может отличаться от кода на вашем компьютере, если вы попытаетесь воспроизвести его в новейшей версии Rails.
Я люблю Rails. Я начал использовать его всего пару недель назад, но даже за этот короткий период времени я понимаю, почему это очень успешный фреймворк. Для меня самый простой способ описать это - магия программирования.
Запустите один двоичный файл командной строки, и у вас будет скелет проекта. Запустите команду scaffolding, и у вас есть работающее приложение. Ничего подобного я никогда не видел.
Хотя магия Rails чрезвычайно удобна, иногда она может быть слишком волшебной. Одним из первых соглашений Rails, вызвавших пик моего любопытства, было params
. Раньше я использовал Sinatra, поэтому знаком с params
. Однако мне никогда не приходило в голову, почему это всегда было доступно без необходимости передавать. Я не могу объяснить, почему этот кусочек магии мучил меня больше, чем другие, просто он просто сделал.
Когда мне нужно реализовать действие show
Контроллера, params
присутствует с id
. Когда я массово назначаю новый ActiveRecord
объект, на помощь приходит params
со всеми необходимыми мне данными. Он всегда доступен по моему вызову - без вопросов.
Многие из вас, возможно, прямо сейчас закатывают глаза и спрашивают: «Кому какое дело? Удобно и работает. Не беспокойся об этом ». Но я действительно переживаю по этому поводу. Я был одним из тех детей, которые нажимали все кнопки на новом пульте от телевизора, чтобы узнать, что все делает; к большому огорчению моих родителей. Я хотел знать, как все работает, чтобы использовать это в своих интересах. params
был моим пультом от телевизора - мне нужно было узнать, как он работает и откуда он берется.
Прежде чем я пойду дальше: это не будет сообщение о том, что делает params
, как его использовать или какие данные в нем передаются. Есть ряд руководств и сообщений в блогах, которые могут объяснить это намного лучше, чем я. Мой вопрос был более конкретным:
Почему params
доступен для действий в контроллерах Rails и как он туда попадает?
Если это похоже на то, что вы хотите узнать, продолжайте читать.
Стойка
Чтобы понять, как Rails интерпретирует параметры, вам нужно понять основы Rack и то, как Rails с ним взаимодействует. Согласно его сайту:
Стойка обеспечивает минимальный интерфейс между веб-серверами, поддерживающими платформы Ruby и Ruby.
Чтобы использовать Rack, предоставьте «приложение»: объект, который отвечает на метод вызова, принимает хэш среды в качестве параметра и возвращает массив с тремя элементами:
1. Код ответа HTTP.
2. Хеш заголовков.
3. Тело ответа, которое должно отвечать каждому
Rack - это, по сути, посредник между веб-серверами и вашим приложением Rails.
- Он принимает запрос от сервера, переводит его в переменную
env
(хэш Ruby), которую может понять Rails. - Rails берет эту
env
переменную, делает с ней то, что ей нужно, и возвращает простой массив обратно в Rack с кодом состояния, заголовками и телом ответа. - Rack берет этот массив, переводит его обратно в правильный HTTP-ответ и передает его браузеру для отображения.
Теперь, когда мы рассмотрели Rack, давайте перейдем к Rails.
Что такое «параметры»?
Чтобы лучше понять происхождение params
, нам нужно лучше понять, что это такое. Это хеш? Метод? Что-то другое?
Лучший способ выяснить это - добавить binding.pry
в действие контроллера и попытаться реконструировать ситуацию, отсоединив слои Rails. Я решил поместить binding.pry
в свой Posts#create
, так как именно там я использую params
наиболее активно.
Отправив запрос на тестовую форму, я был в.
Поиск в любопытстве выявил кое-что интересное:
params
- это метод классаActionController::StrongParameter
.- Хотя
params
выглядит хешем, на самом деле это экземпляр классаActionController::Parameters
.
Если вы не знакомы с ActionController::StrongParameters
, это класс, который предоставляет нам большую безопасность и ограничения по параметрам из params
, которые мы разрешаем при массовом назначении. Если вы когда-либо использовали этот синтаксис раньше, вы можете поблагодарить ActionController::StrongParameters
:
params.require(:post).permit!
Внутри исходного кода ActionController::StrongParameters
, вплоть до самого низа, вы найдете params
во всей его программной красоте.
Как видите, любопытство было правильным. params
- это метод получения класса ActionController::StrongParameters
, который возвращает переменную экземпляра @_params
, которая является экземпляром класса ActionController::Parameters
.
На первый взгляд может показаться, что это не так уж много, но это объясняет, почему params
доступен нам в наших контроллерах без предварительной ссылки. Быстрая проверка в консоли pry скажет вам, почему.
У нас есть доступ к params
, потому что наши контроллеры являются потомками ActionController::StrongParameters
и наследуют все его методы экземпляра!
Woohoo! Тайна разгадана!
Верно?
Не совсем.
Хотя это объясняет тайну вездесущего params
, но не объясняет, как оно туда попало. Если вы похожи на меня, вы хотите знать всю историю. Например, что это было за request.parameters
? Откуда взялось это?
Откуда приходят Params
Примечание: этот следующий раздел будет очень техническим по Rails. Если вы запутались, не волнуйтесь, вы не одиноки. Мне потребовалось несколько часов отладки и чтения кода Rails, чтобы понять, что делать дальше. Следующая информация является результатом этих часов.
После большего количества поисков в Rails я наткнулся на ActionController::Metal
. По сути, ActionController::Metal
является базовой версией ActionController::Base
и является одним из первых предков нашего приложения Rails.
Если вы посмотрите его исходный код, вы найдете несколько ссылок на request
, а также его собственный набор params
методов.
Здесь я должен упомянуть, что, чтобы лучше погрузиться в глубины Rails, я включил регистратор трассировки стека в дополнение к моему binding.pry
.
Причина, по которой я упоминаю это, заключается в том, что если вы выведите эту трассировку стека на консоль, вы увидите все методы, вызываемые, когда Rails получает переменную env
от Rack - а их МНОГО. Если вы хотите увидеть все, что происходит за кулисами, взгляните на это.
Хотя этот журнал впечатляет и чудовищен, есть только несколько строк, о которых нам нужно беспокоиться в отношении params
и ActionController::Metal
.
Эта трассировка стека говорит о том, что после того, как Rails был инициирован с переменной env
, эта переменная передается по всему Rails. В какой-то момент он попадает в метод класса ActionController::Metal::action
. Внутри этого метода ActionController::Metal
инициализирует новый экземпляр самого себя и вызывает #dispatch
, который устанавливает @_request
для нового экземпляра класса ActionDispatch::Request
.
ActionDispatch::Request
- это интерфейс, который Rails использует для взаимодействия с объектом HTTP-запроса (env
), исходящим от Rack. Один из методов, предоставляемых этим API, - #parameters
.
Вот откуда взялся request.parameters
!
Подвести итоги
Это была гора жаргона, которую я только что наложил на вас, так что позвольте мне сократить шаги вниз.
- Пользователь делает запрос к браузеру (GET / POST / и т. Д.).
- Браузер отправляет HTTP-запрос в Rack, где он переводит запрос в объект
env
и передает его в Rails. - Rails передает этот
env
объект внутри, пока он не достигнетActionController::Metal::action
. ActionController::Metal
использует этотenv
объект для создания экземпляраActionDispatch::Request
и сохранения его в переменной экземпляра@_request
, доступной через методrequest
getter.ActionDispatch::Request
анализирует и форматирует параметры исходного HTTP-запроса.ActionController::StrongParameters
использует метод#parameters
, предоставленный объектом экземпляраActionDispatch::Request
, для хранения отформатированных параметров в переменной экземпляра@_params
.- Данные в переменной экземпляра that
@_params
доступны нам и нашим контроллерам черезparams
.
Теперь это довольно сложно.
Надеюсь, вы нашли это подробное описание параметров Rails информативным и полезным. Если вы заметили какие-либо неточности в этом сообщении, сообщите мне, и я исправлю их. Спасибо за внимание!