Статьи

Звездочки рассечены: стойки и промежуточное программное обеспечение

sprocketsmag

В предыдущей статье я объяснил, как теги ресурсов вставляются в 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 обслуживают ресурсы. Пожалуйста, дай мне знать, что ты думаешь!