Примечание. В настоящее время я использую 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 наиболее активно.

Отправив запрос на тестовую форму, я был в.

Поиск в любопытстве выявил кое-что интересное:

  1. params - это метод класса ActionController::StrongParameter.
  2. Хотя 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 информативным и полезным. Если вы заметили какие-либо неточности в этом сообщении, сообщите мне, и я исправлю их. Спасибо за внимание!