В предыдущей статье я объяснил, как теги ресурсов вставляются в HTML с помощью Rails и Sprockets. Вставка тегов ресурсов в HTML — это первый шаг к обслуживанию ресурсов. В этом посте я продолжу путешествие, отвечая на оставшиеся вопросы:
- Что такое
/assets
и их назначение? - Для чего используется
ActionDispatch::Static
middleware?
Что такое /assets
?
По умолчанию приложения Rails, использующие Sprockets, добавляются /assets
на пути каждого ресурса. /assets
, однако, не только префикс для активов; это также конечная точка rack
.
По сути, при генерации ответа Sprockets добавляет каждый актив к /assets
, в результате чего каждый браузер запрашивает эти активы для отправки вызовов на http(s)://your_domain/assets/asset_name
. /assets/asset_name
— это имя ресурса, которое мы хотим, чтобы браузер запросил.
Всякий раз, когда выполняется вызов /assets/asset_name
, приложение Rails должно ответить, обслуживая этот актив. Как я уже упоминал, /assets
— это конечная точка rack
, поэтому она обрабатывается нашими маршрутами Rails? Каждая конечная точка или путь в приложении Rails определяется в config/routes.rb
, но при просмотре его содержимого вы не найдете какой-либо маршрут для /assets
. Тем не менее, наши активы обслуживаются. Посмотрим как.
rack
конечную точку rack
в любом существующем приложении Rails довольно просто. Существуют строгие правила, которым должна следовать каждая конечная точка rack
а именно:
- Он должен отвечать на метод
call
- Он должен возвращать массив с тремя частями информации: код состояния HTTP, заголовки ответа и сам ответ.
Например, поместите следующий код в наш пример приложения Rails (созданный в первой статье) в config/routes.rb
:
mount Proc.new {|env| [200, {'Content-Type' => 'text/html'}, ["SitePoint rocks!"]]} => "/sitepoint"
mount
определяет /sitepoint
как конечную точку rack
. Proc.new
вернет блок Ruby, который отвечает на call
метода. Блок возвращает массив, содержащий код состояния, заголовки состояния и ответ.
Запустите приложение Rails ( rails server
) и перейдите по http://localhost:3000/sitepoint
. Вы увидите SitePoint rocks!
отображается в браузере. Это было быстрое и грязное введение в конечные точки rack
в Rails. Давайте вернемся к Sprockets и посмотрим, как это работает.
Sprockets использует следующий код для монтирования конечной точки rack
в /assets
который определен здесь :
app.routes.prepend do mount app.assets => config.assets.prefix end
app.assets
— это Rails.application.assets
который является экземпляром Sprockets::Environment
, обсуждавшимся в предыдущей статье. config.assets.prefix
— это /assets
. Sprockets
добавляет /assets
к маршрутам Rails, потому что rack
будет соответствовать всем запросам в первом подходящем определении маршрута. Таким образом, предварительно, Sprockets
гарантирует, что /assets
имеет приоритет над другими конечными точками rack
.
app.assets
должен отвечать на call
, чтобы быть обработанным как конечная точка rack
. Глядя на код для Sprockets::Environment
в sprockets/lib/sprockets/environment.rb
, call
наследуется от Sprockets::Base
. Изучение sprockets/lib/sprockets/base.rb
далее включает в себя несколько модулей, одним из которых является Sprockets::Server
. Код записан в Sprockets::Server
и sprockets/lib/sprockets/server.rb
, здесь определен метод call
. Поэтому app.assets
получает метод call
из Sprockets::Server
, который вызывается каждый раз, когда есть запрос к /assets
. Этот метод call
возвращает соответствующий актив.
find_asset
, определенный в find_asset
sprockets/lib/sprockets/server.rb:42
, возвращает экземпляр Sprockets::BundledAsset
при условии, что это JavaScript или CSS. Если это изображение, то возвращается Sprockets::StaticAsset
. Когда браузер вызывает /assets/application.js
, find_asset
возвращает экземпляр Sprockets::ProcessedAsset
.
В line:62
идет вызов ok_response
. ok_response
определяется в line:187
и возвращает массив в соответствии со спецификацией rack
, помещая актив в ответ. Этот массив возвращается из ok_response
, который возвращается из call
.
Мы видели, что экземпляр asset
представлен как ответ для /assets/application.js
. Но достаточно ли этого для отправки обратно клиенту? Ответ «это зависит».
Согласно rack
, ответ, отправляемый из конечной точки rack
должен отвечать each
. И Sprockets::BundledAsset
и Sprockets::StaticAsset
наследуются от класса Sprockets::Asset
. sprockets/lib/sprockets/asset.rb:82
определяет each
, что означает, что наш экземпляр asset
отвечает на each
и совместим с rack
.
В each
методе to_s
передается в качестве аргумента в блок. В line:55
есть метод to_s
который возвращает источник asset
. Каждый экземпляр asset
имеет свой источник в переменной экземпляра source
которая используется для получения исходного кода для соответствующего актива. Мы делаем то же самое в методе to_s
, выбирая исходный код и возвращая его соответствующему вызову метода. Исходный код запрошенного ресурса затем передается клиенту HTTP нашим приложением Rails.
Вот как Sprockets обслуживает наши активы. Вышеописанная процедура одинакова для всех типов вызовов ресурсов, JavaScript, таблицы стилей, изображений, шрифтов и т. Д.
Что такое ActionDispatch::Static
?
ActionDispatch::Static
является промежуточным программным обеспечением в стеке промежуточного программного обеспечения Rails по умолчанию. Его главная цель — обслуживать статические активы. Статические активы — это активы, которые не требуют предварительной обработки перед их обслуживанием, такие как изображения и шрифты. Файлы JavaScript и таблицы стилей не считаются статическими ресурсами, поскольку перед их обработкой требуется некоторая предварительная обработка. Когда мы выполняем rake assets:precompile
, эти активы становятся статическими, потому что они предварительно обрабатываются и сохраняются в виде файлов, поэтому их можно обслуживать так же, как изображения. Давайте посмотрим, как работает это промежуточное программное обеспечение.
ActionDispatch::Static
промежуточное ПО настраивается и добавляется в стек промежуточного ПО здесь :
middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
ActionDispatch::Static
определяется в actionpack/lib/action_dispatch/middleware/static.rb:47
. В его конструкторе есть три параметра: app
, path
и cache_control
. Предоставление параметра app
является обязанностью rack
. Rails передает paths["public"].first
для второго аргумента. paths["public"].first
— это абсолютный путь к public
каталогу в приложении Rails. Если мы включили config.serve_static_assets
в соответствующем файле среды в config/environments
, то ActionDispatch::Static
будет первым промежуточным ПО в списке при rake middleware
. Это будет первая точка выполнения для нашего приложения Rails.
Если посмотреть на actionpack/lib/action_dispatch/middleware/static.rb:53
то есть метод call
, который используется в rack
. В этом методе в line:54
указывается case
для используемого HTTP method
. Если HTTP method
запроса — GET
или HEAD
, это промежуточное ПО оценивает путь запроса, чтобы определить, является ли это статическим ресурсом, который необходимо обслуживать. line:50
конструктора для ActionDispatch::Static
показывает следующее:
@file_handler = FileHandler.new(path, cache_control)
Класс FileHandler
определен в том же файле, чуть выше ActionDispatch::Static
. На line:57
, есть вызов, чтобы match?
в этом экземпляре @file_handler
. В line:12
вы можете увидеть определение match?
, Этот метод оценит предоставленный путь, чтобы увидеть, является ли он статическим активом. Если этот метод возвращает true
, ActionDispatch::Static
будет обслуживать ресурс, и управление не будет передаваться другим промежуточным программам в стеке. Если match?
возвращает false
, управление будет передано последующему промежуточному программному обеспечению.
line:15
match?
объединяет указанный путь с @root
. @root
— это абсолютный путь к public
каталогу нашего приложения Rails.
Например, запрос для /assets/application.js
объединяется с путем к общедоступному каталогу и становится /Users/imran/work/rails_asset_serve/public/assets/application.js
(ваш будет другим). Код в line:19
пытается определить, существует ли этот файл. Если это так, то этот файл будет обрабатываться ActionDispatch::Static
, в противном случае он будет обрабатываться Sprockets.
В среде development
нет ресурсов в общем каталоге, поэтому все они обрабатываются Sprockets. В production
, однако, статические ресурсы в public
каталоге существуют, потому что мы обычно компилируем активы с помощью rake assets:precompile
. Здесь match?
возвращает true
а ресурсы обслуживаются ActionDispatch::Static
.
В line:59
выполняется вызов для call
экземпляра @file_handler
. call
определяется в line:26
которая, в свою очередь, вызывает call
на @file_server
, экземпляре Rack::File
.
Rack::File
определяется в rack/lib/rack/file.rb
В методе call
on line:30
вызывается dup
который создает поверхностную копию текущего экземпляра. _call
(определено в line:36
) вызывается для этой вновь созданной копии, выполняя различные проверки и затем вызывая serving
line:53
.
При serving
, в line:73
, создается массив для ответа rack
, где self
представляется как объект ответа. В конце serving
этот массив response
возвращается.
Я спрашивал ранее, достаточно ли self
, чтобы отправить ответ клиенту? Ответ — yes
. Потому что self
отвечает на each
который определен в line:104
. В each
открывается файл для чтения и чтения фрагментами по 8 КБ в переданный блок, который передается в браузер нашим приложением Rails.
Вот как ActionDispatch::Static
обслуживает статические активы.
Вывод
Rails — невероятная кодовая база, написанная самыми яркими умами сообщества Ruby. Читая исходный код, вы не только улучшаете свои навыки, но и получаете возможность изучать новые вещи, которых вы не найдете в других местах. Например, я обнаружил, что с помощью ActionDispatch::Static
мы можем обслуживать части наших активов, используя заголовки Range, о которых я раньше не знал.
Я надеюсь, что эти статьи помогли вам узнать больше о том, как Rails и Sprockets обслуживают ресурсы. Пожалуйста, дай мне знать, что ты думаешь!