خط أنابيب الأصول في ريلز

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

يغطّي هذا الدليل أنبوب الأصول. ستتعلم بعد قراءة هذا الدليل:

  • ماهيّة أنبوب الأصول وماذا يفعل.
  • كيفيّة تنظيم أصول تطبيقك بشكل صحيح.
  • فوائد أنبوب الأصول.
  • كيفيّة إضافة معالج مسبق (pre-processor) إلى الأنبوب.
  • كيفيّة وضع الأصول مع جوهرة في حزمة.

ما هو أنبوب الأصول؟

يوفّر أنبوب الأصول إطارًا لسَلسَلة وتصغير أو ضغط أصول JavaScript و CSS. كما أنه يضيف القدرة على كتابة هذه الأصول بلغات أخرى ومعالجات مسبقة مثل CoffeeScript و Sass و ERB. يسمح للأصول في تطبيقك أن تُدمج تلقائيًّا مع الأصول من جواهر أخرى.

يُعرَّفُ استخدام خط الأصل بواسطة الجوهرة sprockets-rails، ويُفعّل افتراضيًّا. يمكنك تعطيله أثناء إنشاء تطبيق جديد عبر تمرير الخيار skip-sprockets--.

rails new appname --skip-sprockets

يضيف ريلز تلقائيًّا الجواهر coffee-rails و sass-rails و uglifier إلى الملف Gemfile التي يستخدمها Sprockets لضغط الأصول:

gem 'sass-rails'
gem 'uglifier'
gem 'coffee-rails'

استخدام الخيار skip-sprockets-- سيمنع ريلز من إضافتها إلى Gemfile لذا إن أردت تفعيل أنبوب الأصول فيما بعد سيتعيّن عليك إضافة تلك الجواهر إلى ملفك Gemfile. سيؤدي إنشاء تطبيق مع الخيار skip-sprockets-- إلى إنشاء ملف config/application.rb مختلف اختلافًا طفيفًا، مع عبارة طلب (require) إلى sprockets railtie موضوعة داخل تعليق. سيتعيّن عليك إزالة مُعامل (operator) التعليق على هذا السطر لتفعيل أنبوب الأصول لاحقًا:

# require "sprockets/railtie"

لضبط توابع ضغط الأصول، اضبط خيارات الإعداد المناسبة في production.rb - config.assets.css_compressor لملفك بـ CSS و config.assets.js_compressor من أجل شيفرتك JavaScript:

config.assets.css_compressor = :yui
config.assets.js_compressor = :uglifier

ملاحظة: تُستخدم الجوهرة sass-rails تلقائيًا لضغط ملفات CSS إذا ضُمّنت في Gemfile ولم يُضبط أي خيار config.assets.css_compressor.

الميزات الرئيسية

تكمن ميزة الأنابيب الأولى في تجميع الأصول ممّا يقلّل من عدد الطلبات التي يقدمها المتصفّح لعرض صفحة الويب. عدد الطلبات التي تقدر متصفّحّات الويب على إجرائها بالتوازي محدود لذا يمكن أن يعني تخفيض عدد الطلبات سرعة تحميل أعلى لتطبيقك.

يُجمّع Sprockets جميع ملفات JavaScript في ملف js. رئيسي واحد وجميع ملفّات CSS في ملف css. رئيسي واحد. يمكنك تخصيص هذه الاستراتيجية لتجميع الملفات بالطريقة التي تريدها كما سترى لاحقًا في هذا الدليل. يدرج ريلز عند الإنتاج بصمة SHA256 في كل اسم ملف بحيث يُخزّن الملف مؤقّتًا بواسطة متصفّح الويب. يمكنك إبطال ذاكرة التخزين المؤقّت عن طريق تغيير هذه البصمة التي تحدث تلقائيًا كلما غيّرت محتويات الملف.

الميزة الثانية لأنبوب الأصول هي اقتضاب (minification) الأصول أو ضغطها. وذلك عن طريق إزالة المسافات البيضاء والتعليقات بالنسبة إلى ملفّات CSS. بالنسبة إلى JavaScript يمكن تطبيق عمليّات أكثر تعقيدًا. يمكنك الاختيار بين مجموعة من الخيارات المضمنّة أو تحديد خياراتك الخاصّة.

الميزة الثالثة لأنبوب الأصول هي أنه يسمح بتشفير الأصول عبر لغة ذات مستوى أعلى، مع وصول التحويل المسبق حتّى مستوى الأصول الفعليّة. تتضمّن اللغات المدعومة Sass لـ CSS و CoffeeScript لـ JavaScript  و ERB بشكل افتراضي.

ما هي البصمات ولماذا يجب أن أهتم؟

البصمة هي تقنية تجعل اسم الملف مرتبطًا بمحتويات الملف. عند تغيير محتويات الملف يتغيّر اسم الملف أيضًا. يوفّر هذا الأسلوب بالنسبة للمحتوى الثابت أو الذي لم يُغيّر بشكل متكرر طريقة سهلة لمعرفة ما إذا كان إصداران من الملفّات متطابقين، حتى عبر الخوادم أو تواريخ النشر المختلفة.

يمكن عندما يكون اسم الملف فريدًا ومستندًا على محتواه تعيين ترويسات HTTP لتشجيع التخزين المؤقّت في كل مكان (سواء في شبكات توصيل المحتوى أو في مزودي خدمات الإنترنت أو في معدّات الشبكات أو في متصفحّات الويب) للاحتفاظ بنسخهم الخاصّة من المحتوى. عند تحديث المحتوى ستتغيّر البصمة. سيؤدي ذلك إلى مطالبة العملاء البعيدين بنسخة جديدة من المحتوى. يُعرف هذا عمومًا باسم خرق ذاكرة التخزين المؤقّت (cache busting).

التقنية التي يستخدمها Sprockets للبصمة هي إدخال قيمة hash للمحتوى في الاسم، عادةً في النهاية. خذ ملف CSS مسمّى global.css مثلًا:

global-908e25f4bf641868d8683022a5b62f54.css

هذه هي الاستراتيجية التي يعتمدها أنبوب الأصول ريلز.

كانت استراتيجية ريلز القديمة هي إلحاق سلسلة استعلام نصيّة (query string) تستند إلى التاريخ بكل أصل مرتبط بمساعد مضمّن. تبدو الشفرة المُنشئَة كما يلي في المصدر:

/stylesheets/global.css?1309495796

تملك استراتيجية سلسلة الاستعلام عدّة عيوب:

  1. لن تُخزّن كل ذاكرات التخزين المؤقّت المحتوى بشكل يعتمد عليه حين يختلف اسم الملف فقط بمعاملات الاستعلام. يوصي Steve Souders "... تجنّب سلاسل الاستعلام في الموارد القابلة للتخزين المؤقّت". وجد أنّه في هذه الحالة لن يُخزّنَ 5-20٪ من الطلبات مؤقّتًا. سلاسل الاستعلام النصيّة على وجه الخصوص لا تعمل على الإطلاق مع بعض شبكات توصيل المحتوى لإبطال ذاكرة التخزين المؤقّت.
  2. يمكن أن يتغيّر اسم الملف بين العقد في البيئات متعددة الخوادم. تستند سلسلة الاستعلام الافتراضية في الإصدار ‎2.x من ريلز على وقت تعديل الملفّات. عندما تُنشَر (deployed) الأصول إلى مجموعة، لا يوجد ضمان بأن الطوابع الزمنيّة ستكون نفسها مما يؤدي إلى استخدام قيم مختلفة بناءً على الخادم الذي يعالج الطلب.
  3. إبطال ذاكرات التخزين المؤقّت أكثر من اللازم. عندما تُطلق الأصول الثابتة مع كل إصدار جديد من التعليمات البرمجية، يتغيّر mtime (وقت التعديل الأخير) لجميع هذه الملفّات، مما يجبر كل العملاء البعيدين على جلبهم مرة أخرى، حتى عندما لا يتغيّر محتوى تلك الأصول.

تعمل البصمات على إصلاح هذه المشكلات عن طريق تجنب سلاسل الاستعلام وضمان توافق أسماء الملفّات استنادًا إلى محتواها.

تُفعّل البصمات بشكل افتراضي لكل من بيئات التطوير والإنتاج. يمكنك تفعيلها أو تعطيلها بإعداداتك من خلال الخيار config.assets.digest.

قراءة المزيد:

كيفية استخدام أنبوب الأصول

في الإصدارات السابقة من ريلز، كانت جميع الأصول موجودة في مجلّدات فرعية public مثل images و javascripts و  stylesheets. أصبح الآن الموقع المفضّل لهذه الأصول هو المجلّد app/assets مع أنبوب الأصول. تقدّم الملفّات في هذا المجلّد بواسطة برمجيّات Sprockets الوسيطة.

لا يزال من الممكن وضع الأصول في التسلسل الهرمي public. ستُقدّم أي أصول تحت public كملفّات ثابتة من طرف التطبيق أو خادم الويب عند ضبط config.public_file_server.enabled إلى القيمة true. عليك استخدام app/assets للملفّات التي يجب أن تخضع لبعض المعالجة المسبقة قبل تقديمها.

يُحوّل ريلز هذه الملفّات إلى public/assets بشكل افتراضي عند الإنتاج. تُقدّم بعد ذلك النسخ المُحوّلة مسبقًا كأصول ثابتة من طرف خادم الويب. لا تُقدّم الملفّات في app/assets أبدًا مباشرةً في الإنتاج.

الأصول الخاصة بوحدة تحكم (Controller Specific Assets)

عند توليد سقالة (scaffold) أو وحدة تحكّم، يُولّد ريلز أيضا ملف JavaScript (أو ملف CoffeeScript إذا وُجدت الجوهرة coffee-rails في Gemfile) وملف أوراق الأنماط الانسيابيّة (أو ملف SCSS إن وُجد sass-rails في Gemfile) لوحدة التحكّم تلك. علاوة على ذلك، ينشئ ريلز الملف scaffolds.css عند إنشاء سقالة (أو scaffolds.scss إن وُجد sass-rails في Gemfile).

إن أنشأت مثلًا ProjectsController، سيضيف ريلز أيضًا ملفًّا جديدًا في app/assets/javascripts/projects.coffee وآخر في app/assets/stylesheets/projects.scss. ستكون هذه الملفّات جاهزة للاستخدام من طرف تطبيقك إفتراضيًّا على الفور باستخدام الأمر require_tree. انظر للتعليمات والملفّات النصّيّة التي تحتوي على قائمة الملفات التي ستجرى عليها عملية ما (maniftest) لمزيد من التفاصيل حول require_tree.

يمكنك أيضًا اختيار تضمين أوراق أنماط خاصّة بوحدة التحكّم (controller specific stylesheets) وملفّات JavaScript فقط في وحدات تحكّمهم باستخدام ما يلي:

<%= javascript_include_tag params[:controller] %> or <%= stylesheet_link_tag
params[:controller] %>

تأكد لو فعلت هذا من أنك لا تستخدم التوجيه require_tree لأن ذلك سيؤدي لتضمين أصولك أكثر من مرة.

تحذير: عند استخدام تصريف الأصول المسبق (asset precompilation) ستحتاج للتأكّد من أن أصول وحدة تحكمّك ستُحوّل مسبقًا عند تحميلها بكل صفحة على حدة. لن تُحوّل الملفّات coffee. و scss. مسبقًا من تلقاء نفسها افتراضيًّا. راجع تحميل الأصول المسبق للحصول على مزيد من المعلومات حول كيفيّة عمل التحويل البرمجي المسبق.

ملاحظة: يجب أن يكون لديك تنفيذ (runtime) مدعوم من ExecJS من أجل استخدام CoffeeScript. إن كنت تستخدم macOS أو Windows، سيُثبّت تنفيذ JavaScript في نظام تشغيلك. راجع توثيق ExecJS لمعرفة كل تنفيذات JavaScript المدعومة.

يمكنك أيضًا تعطيل إنشاء ملفّات أصول خاصة بوحدة تحكّم عن طريق إضافة التالي إلى config/application.rb:

config.generators do |g|
  g.assets false
end

تنظيم الأصول

يمكن وضع أصول أنبوب داخل تطبيق في أحد المواقع الثلاثة: app/assets أو lib/assets أو vendor/assets.

  • app/assets مخصّص للأصول التي يملكها التطبيق مثل الصور المخصّصة أو ملفّات JavaScript أو أوراق الأنماط.
  • lib/assets مخصّص لشفرات مكتباتك التي لا تتناسب حقًا مع نطاق التطبيق أو تلك المكتبات المشتركة عبر التطبيقات.
  • vendor/assets مخصّص للأصول التي تملكها كيانات خارجية مثل شفرة لإضافات JavaScript وأطر عمل CSS. ضع بالحسبان وجوب إعادة كتابة شفرات الطرف الثالث ذات المراجع إلى ملفّات أخرى (الصور، أوراق الأنماط، إلخ) المعالجة من طرف أنبوب الأصول كي تستخدم المساعدين مثل asset_path.

تحذير: إن كنت تُحدّث من ريلز 3، رجاءً راع أن الأصول تحت lib/assets أو vendor/assets متوفرة للتضمين عبر بيانات التطبيق لكنها لم تعد جزءًا من مصفوفة التصريف المسبق precompile()‎. انظر قسم تصريف الأصول المسبق لمزيد من الإرشادات.

مسارات البحث

عند الرجوع إلى ملف من بيان أو مُساعد، يبحث Sprockets عنه في مواقع الأصول الافتراضية الثلاثة.

المواقع الافتراضية هي: المجلّدات images و javascripts و stylesheets تحت المجلّد app/assets، ولكن هذه المجلّدات الفرعية ليست حالة فريدة - سيُبحث في أي مسار تحت */assets.

هذه الملفّات مثلًا:

app/assets/javascripts/home.js
lib/assets/javascripts/moovinator.js
vendor/assets/javascripts/slider.js
vendor/assets/somepackage/phonebox.js

سيُرجع إليها في بيان بهذا الشكل:

//= require home
//= require moovinator
//= require slider
//= require phonebox

يمكن الوصول إلى الأصول الموجودة داخل المجلّدات الفرعيّة أيضًا.

app/assets/javascripts/sub/something.js

يشار إليها على النحو التالي:

//= require sub/something

يمكنك رؤية مسار البحث عبر فحص Rails.application.config.assets.paths في وحدة تحكّم ريلز.

عدا مسارات */assets القياسية يمكن إضافة مسارات إضافيّة (مؤهلّة بالكامل [fully qualified]) إلى خط الأنابيب في config/initializers/assets.rb. مثلًا:

Rails.application.config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")

تُجتاز المسارات بالترتيب الذي تظهر به في مسار البحث. هذا يعني افتراضيا أنّ الأسبقيّة للملفّات في app/assets وأنّها ستحجب المسارات المقابلة في lib و vendor.

من المهم ملاحظة وجوب إضافة الملفّات التي تريد الإشارة إليها خارج الملفّ النصّي الذي يحتوي على قائمة الملفات التي ستجرى عليها عملية ما (manifest) إلى المصفوفة precompile وإلّا لن تكون متوفّرة في بيئة الإنتاج.

استخدام ملفات الفهرس

يستخدم Sprockets ملفّات تدعى index (مع الملحقات ذات الصلة) لغرض خاص.

إن كان لديك مكتبة jQuery تحتوي على العديد من الوحدات النمطية والتي تُخزّن في lib/assets/javascripts/library_name، يقوم الملف lib/assets/javascripts/library_name/index.js مقام الملفّ النصّي الذي يحتوي على قائمة الملفات التي ستجرى عليها عملية ما (maniftest) لجميع الملفّات الموجودة في هذه المكتبة. يمكن أن يتضمن هذا الملف قائمة بجميع الملفّات المطلوبة بالترتيب أو توجيهًا بسيطًا require_tree.

يمكن الوصول للمكتبة ككل في بيان التطبيق بهذا الشكل:

//= require library_name

يبسّط هذا الأسلوب الصيانة ويحافظ على ترتيب الأمور من خلال السماح بتجميع التعليمات البرمجية ذات الصلة قبل التضمين (inclusion) في مكان آخر.

روابط الترميز إلى الأصول

لا يضيف Sprockets أي توابع جديدة للوصول إلى أصولك - ما زلت تستخدم javascript_include_tag و stylesheet_link_tag المألوفتين:

<%= stylesheet_link_tag "application", media: "all" %>
<%= javascript_include_tag "application" %>

في حالة استخدام الجوهرة turbolinks المُضمّنة افتراضيًا في ريلز، ضمّن الخيار 'data-turbolinks-track' الذي يتسبب في تحقّق turbolinks من تحديث الأصل، وفي تلك الحالة يحمّله بالصفحة:

<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => "reload" %>
<%= javascript_include_tag "application", "data-turbolinks-track" => "reload" %>

في الواجهات (views) العاديّة، يمكنك الوصول للصور في مجلّد app/assets/images بهذا الشكل:

<%= image_tag "rails.png" %>

يُقدّم هذا الملف من طرف Sprockets بشرط أن يُفعّل الأنبوب داخل تطبيقك (و ولا يكون معطّلًا في سياق البيئة الحالي). إن وُجد الملف في public/assets/rails.png، سيقدّمه خادم الويب.

يُتعامل مع طلب ملف مع تجزئة SHA256 مثل بنفس الطريقة:

public/assets/rails-f90d8a84c707a8dc923fca1ca1895ae8ed0a09237f6992015fef1e11be77c023.png

كيفيّة توليد هذه التجزئات مُغطّى في قسم الإنتاج لاحقًا في هذا الدليل.

سينظر Sprockets أيضًا عبر المسارات المحدّدة في config.assets.paths والتي تتضمّن مسارات التطبيق القياسية وأي مسارات تضيفها محرّكات ريلز.

يمكن أيضًا تنظيم الصور في مجلّدات فرعية إن لزم الأمر ومن ثم الوصول إليها عبر تحديد اسم المجلّد في الوسم (tag):

<%= image_tag "icons/rails.png" %>

تحذير: إذا كنت تُصرّف أصولك مسبقًا (أي precompiling، انظر في قسم الإنتاج أدناه)، فسيرمي الربط بأصل غير موجود استثناءً في صفحة الاستدعاء. بما في ذلك الربط بسلسلة نصيّة فارغة. لهذا السبب كن حذرًا عند استخدام image_tag والمساعدين الآخرين مع البيانات التي يوفّرها المستخدم.

شيفرات CSS و ERB

يُقيّم أنبوب الأصول تلقائيًا ERB. ويعني هذا أنك إن أضفت ملحق erb (أي erb extension) إلى أصل CSS (على سبيل المثال application.css.erb)، ستكون المساعِدات مثل asset_path متاحة في قواعد CSS:

.class { background-image: url(<%= asset_path 'image.png' %>) }

يكتب هذا المسار إلى الأصل المعيّن المشار إليه. في هذا المثال ،من المنطقي امتلاك صورة في أحد مسارات تحميل الأصول مثل app/assets/images/image.png، والتي سيُشار إليها هنا. سيرجع إلى هذا المسار إذا كانت هذه الصورة متاحة بالفعل في public/assets كملف مبصوم (fingerprinted file).

إذا كنت تريد استخدام رابط بيانات URI - وهي طريقة لتضمين (embedding) بيانات الصورة مباشرةً في ملف CSS - يمكنك استخدام المساعد asset_data_uri.

#logo { background: url(<%= asset_data_uri 'logo.png' %>) }

يُدرج هذا URI بيانات منسقة بشكل صحيح في مصدر CSS.

لاحظ أن علامة الإغلاق لا يمكن أن تكون بالشكل ‎-%>‎.

شيفرات CSS و Sass

عند استخدام أنبوب الأصول، يجب إعادة كتابة المسارات إلى الأصول ويوفّر sass-rails المُساعدين url- و path- ( يكتبان بالشَرطة في Sass والشَرطة السفليّة في روبي) لأصناف الأصول التالية: الصورة والخط والفيديو والصوت وجافاسكربت وأوراق الأنماط الإنسيابيّة.

  • image-url("rails.png")‎ يعيد url(/assets/rails.png)‎
  • image-path("rails.png")‎ يعيد "‎/assets/rails.png"

يمكن أيضًا استخدام الشكل الأكثر شيوعًا:

  • asset-url("rails.png")‎ يعيد url(/assets/rails.png)‎
  • asset-path("rails.png")‎  يعيد "‎/assets/rails.png"

شيفرات JavaScript / CoffeeScript و ERB

يمكنك استخدام المساعد asset_path في شفرتك JavaScript إن أضفت امتداد erb إلى أصل JavaScript جاعلًا منه application.js.erb مثلًا:

$('#logo').attr({ src: "<%= asset_path('logo.png') %>" });

يكتب هذا  المسار إلى الأصل المعيّن المشار إليه.

تستطيع بالمثل استخدام المساعد asset_path في ملفّات CoffeeScript ذات امتداد erb (مثلًا application.coffee.erb):

$('#logo').attr src: "<%= asset_path('logo.png') %>"

ملفّات manifest والتوجيهات (Manifest Files and Directives)

يستخدم Sprockets ملفّات manifest لتحديد الأصول المراد تضمينها وعرضها. تحتوي ملفّات manifest على توجيهات - وهي تعليمات تخبر Sprockets بالملفّات التي يجب أن يتطلّبها لإنشاء ملف CSS أو JavaScript واحد. باستخدام هذه التوجيهات يُحمّل Sprockets الملفّات المحدّدة، ويعالجها إن لزم، ويجمعها في ملف واحد واحد ثم يضغطها (بناءً على قيمة Rails.application.config.assets.js_compressor). يمكن تقليل وقت تحميل الصفحات بشكل كبير بتقديم ملف واحد بدلاً من العديد لأن المتصفّح يرسل عددًا أقل من الطلبات. يُصغّر الضغط أيضًا من حجم الملف، ممّا يتيح للمتصفّح تنزيلها بشكل أسرع.

على سبيل المثال، يتضمن تطبيق ريلز جديد ملفًا افتراضيًا app/assets/javascripts/application.js يحتوي على السطور التالية:

// ...
//= require rails-ujs
//= require turbolinks
//= require_tree .

تبدأ أوامر Sprockets بـ ‎//=‎ في ملفّات JavaScript. في الحالة المذكورة أعلاه، يستخدم الملف التوجيهات require و require_tree. يُستخدم التوجيه require لإخبار Sprockets بالملفّات التي ترغب في طلبها. أنت تطلب هنا الملفّات rails-ujs.js و turbolinks.js المتاحة في مكان ما في مسار البحث لـ Sprockets. لا تحتاج إلى توفير الامتدادات بشكل صريح. يفترض Sprockets أنك تطلب ملف js. عندما تفعل ذلك داخل ملف js.

التوجيه require_tree يأمر Sprockets تعاوديًّا (recursively) بأن يُضمّن كافة ملفّات JavaScript في المجلّد المحدّد في الإخراج. يجب تحديد هذه المسارات نسبة إلى ملف manifest. يمكنك أيضًا استخدام التوجيه require_directory الذي يتضمّن جميع ملفّات JavaScript في المجلّد المحدد فقط، بدون أي تعاوديّة (recursion).

تُعالج التوجيهات من الأعلى إلى الأسفل ولكن الترتيب الذي تُضمّن الملفّات به في require_tree غير محدّد. يجب ألا تعتمد على أي ترتيب معيّن بين هؤلاء. إن إحتجت إلى التأكد من أن يوضع JavaScript معين قبل غيره في الملف المُجمَّع (concatenated file)، تطلّب الملف اللازم أوّلًا في manifest. لاحظ أن عائلة توجيهات require تمنع الملفّات من تضمينها مرتين في الإخراج.

ينشئ ريلز أيضًا ملفًا افتراضيًا app/assets/stylesheets/application.css يحتوي على هذه السطور:

/* ...
*= require_self
*= require_tree .
*/

ينشئ ريلز كلا app/assets/javascripts/application.js و app/assets/stylesheets/application.css بغض النظر عن استخدام الخيار skip-sprockets-- عند إنشاء تطبيق ريلز جديد. هذا يمكنك من إضافة أنابيب الأصول (asset pipelining) بسهولة لاحقًا إن أردت.

تعمل التوجيهات التي تعمل في ملفّات JavaScript أيضًا في صفحات الأنماط (على الرغم من أنها تتضمن أوراق أنماط بدلًا من ملفّات JavaScript). يعمل التوجيه require_tree في بيان CSS بنفس طريقة بيان JavaScript، ويتطلب كل أوراق الأنماط من المجلّد الحالي.

يُستخدم require_self في هذا المثال. يضع شفرة CSS المضمنة داخل الملف (إن وجدت) في الموقع الدقيق للنداء require_self.

ملاحظة: إن أردت استخدام عدة ملفّات Sass، عليك استخدام القاعدة ‎@import بدلًا من توجيهات Sprockets. تتواجد ملفّات Sass داخل نطاقها الخاص عند استخدام توجيهات Sprockets ممّا يجعل المتغيّرات أو المخاليط (mixins) متوفّرة فقط داخل المستند الذي عُرّفت فيه.

يمكنك تعميم الملف (file globbing) أيضًا باستخدام "*" import@ و "*/**" import@ لإضافة الشجرة بأكملها وهو ما يعادل كيفيّة عمل require_tree. تحقق من توثيق sass-rails لمزيد من المعلومات والمحاذير المهمة.

يمكنك الحصول على أي عدد من الملفّات تريده. على سبيل المثال، يمكن أن يحتوي بيان admin.css و admin.js على ملفّات JS و CSS التي تُستخدم لقسم إدارة التطبيق.

تنطبق نفس الملاحظات حول الترتيب أعلاه. تحديدًا، يمكنك تحديد ملفّات منفردة وستُصرّف (compiled) بالترتيب المحدّد. قد تجمع مثلًا ثلاث ملفّات CSS معًا بهذه الطريقة:

/* ...
*= require reset
*= require layout
*= require chrome
*/

المعالجة المسبقة (preprocessing)

تحدّد امتدادات الملفّات المستخدمة في أصل ما المعالجة السابقة التي وقعت عليه. يُنشأ ملف CoffeeScript وملف SCSS بدلًا من ملف JavaScript و CSS عاديين عند إنشاء وحدة تحكّم أو سقالة (scaffold) باستخدام مجموعة جواهر ريلز (gemset Rails) الافتراضية. المثال المستخدم سابقًا كان وحدة تحكّم مُسمّاة "projects" ولّدت الملف app/assets/javascripts/projects.coffee والملف app/assets/stylesheets/projects.scss.

في وضع التطوير أو في حالة تعطيل أنبوب الأصل، تُعالج هذه الملفّات عندما تُطلب بواسطة المُعالجات التي يوفرّها coffee-script وجواهر sass ثم تُعاد إلى المتصفّح على هيئة JavaScript و CSS على التوالي. عندما يُفعّل أنبوب أصول، تُعالَج هذه الملفّات مسبقًا وتوضع في المجلّد public/assets لتقديمها إما من خلال تطبيق ريلز أو خادم الويب.

يمكن طلب طبقات إضافيّة من المعالجة المسبقة عن طريق إضافة ملحقات أخرى، حيث يُعالَج كل ملحق بطريقة من اليمين إلى اليسار. يجب استخدامهن بالترتيب الذي يجب تطبيق المعالجة حسبه. على سبيل المثال، تُعالَج ورقة أنماط باسم app/assets/stylesheets/projects.scss.erb أولاً كـ ERB ثم SCSS وأخيرًا تُقدّم كـ CSS. - وينطبق الشيء نفسه على ملف JavaScript - يُعالَج app/assets/javascripts/projects.coffee.erb كـ ERB، ثم CoffeeScript وتُقدّم بمثابة JavaScript.

وضع ترتيب هذه المعالجات المسبقة بالحسبان مهم. على سبيل المثال، إن استدعيت ملفك JavaScript المسمّى app/assets/javascripts/projects.erb.coffee، فسيُعالَج مع مترجم CoffeeScript أولاً والذي لن يفهم ERB وبالتالي ستواجه مشكلات.

في التطوير

تُقدّم الأصول في وضع التطوير كملفّات منفصلة بالترتيب المحدّد في ملف manifest.

الملف app/assets/javascripts/application.js الذي يحوي:

//= require core
//= require projects
//= require tickets

سيولد شيفرة HTML التالية:

<script src="/assets/core.js?body=1"></script>
<script src="/assets/projects.js?body=1"></script>
<script src="/assets/tickets.js?body=1"></script>

يتطلّب Sprockets المعامل body.

رفع خطأ عندما لا يعثر على الأصل

إذا كنت تستخدم sprockets-rails > = 3.2.0، تستطيع إعداد ما يحدث عند إجراء بحث عن أصول ولم يُعثر على أي شيء. سيظهر خطأ عند عدم العثور على أصل إذا أوقفت الأصول التراجعيّة (asset fallback).

config.assets.unknown_asset_fallback = false

إن فُعّلت الأصول التراجعيّة، سيُخرج المسار بدلًا من ذلك ولن يظهر خطأ عندما لا يمكن العثور على أصل. سلوك الأصول، مُفّعل افتراضيًا.

إطفاء القيم المشفرة المختصرة (Turning Digests Off)

يمكنك إيقاف تشغيل القيم المشفرة المختصرة (digests) من خلال تحديث config/environments/development.rb ليشمل الضبط:

config.assets.digest = false

ستُنشأ قيم مشفرة مختصرة لعناوين URL للأصول عندما تكون قيمة هذا الخيار true.

إيقاف تشغيل التنقيح

يمكنك إيقاف وضع التنقيح عبر تحديث config/environments/development.rb لتضمين:

config.assets.debug = false

يُجمّع Sprockets ويُشغّل المُعالجات التمهيديّة (preprocessors) الضروريّة على كافة الملفّات عند إيقاف وضع التنقيح. سيُنشئ manifest بدلًا منه:

<script src="/assets/application.js"></script>

تُجمّع الأصول وتُخزّن مؤقّتًا في الطلب الأول بعد بدء تشغيل الخادم. يُعيّن Sprockets ترويسة HTTP وهي must-revalidate Cache-Control لتقليل حمل الطلبات على الطلبات اللاحقة والتي يحصل عليها المتصفّح على استجابة 304 (غير مُعدّل).

يستجيب الخادم بملف مُصرَّف (compiled) جديد إن تغيّرت أي من الملفّات في manifest بين الطلبات.

يمكن أيضًا تفعيل وضع التنقيح في توابع ريلز المساعدة:

<%= stylesheet_link_tag "application", debug: true %>
<%= javascript_include_tag "application", debug: true %>

الخيار debug: مجرّد تكرار إن كان وضع التنقيح مُشغّلًا بالفعل.

يمكنك أيضًا تفعيل الضغط في وضع التطوير كتحقّق من السلامة (sanity check) وتعطيله حسب الطلب وحسب الحاجة لتنقيح الأخطاء.

في الإنتاج

يستخدم Sprockets مخطط البصمات الموضّح أعلاه في بيئة الإنتاج. يفترض ريلز أن الأصول صُرّفت مسبقًا (precompiled) وستُقدّم كأصول ثابتة بواسطة خادم الويب الخاص بك.

تُولَّد SHA256 أثناء مرحلة التحويل المسبق من محتويات الملفّات المُصرّفة (compiled) وتضاف إلى أسماء الملفّات عند كتابتها على القرص. تُستخدم هذه الأسماء الحاملة لبصمات بواسطة مساعدي ريلز بدلًا من اسم ملف manifest.

على سبيل المثال، يولّد التالي:

<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application" %>

شيئًا كهذا:

<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js"></script>
<link href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" media="screen"
rel="stylesheet" />

ملاحظة: لا يُستخدم الخياران cache: و concat: مع أنبوب الأصول بعد الآن، احذفهما من javascript_include_tag و stylesheet_link_tag.

يتحكّم الخيار config.assets.digest (الذي يضبط إلى القيمة true إفتراضيًّا) في سلوك البصمة (fingerprinting behavior).

ملاحظة: في ظل الظروف العادية، لا يجب تغيير الخيار config.assets.digest الافتراضي. إن لم توجد أية قيم مشفرة مختصرة (digests) في أسماء الملفّات، وعُيّنت ترويسات للمستقبل البعيد، لن يعلم العملاء البعيدون أبدًا أن عليهم إعادة جلب الملفّات عند تغيير محتواها.

تصريف الأصول المسبق (Precompiling Assets)

يأتي ريلز مجهّزًا بمهمة لتصريف بيانات الأصول والملفّات الأخرى في الأنابيب.

تُكتب الأصول المُصرّفة بالموقع المحدّد في config.assets.prefix. هذا هو المجلّد assets/ افتراضيًّا.

يمكنك استدعاء هذه المهمة على الخادم أثناء النشر على الخادم الإنتاجي (deployment) لإنشاء إصدارات مُصرّفة من أصولك مباشرة على الخادم. انظر القسم التالي للحصول على معلومات حول التصريف محليًّا.

المهمة هي:

$ RAILS_ENV=production bin/rails assets:precompile

يتضمّن Capistrano (النسخة v2.15.1 وما فوق) وصفة للتعامل مع هذا الأسلوب في النشر على الخادم الإنتاجي. أضف السطر التالي إلى Capfile:

load 'deploy/assets'

يربط أعلاه المجلد المحدّد في config.assets.prefix بالمجلّد shared/assets. إن كنت تستخدم هذا المجلد المشترك بالفعل ستحتاج إلى كتابة مهمّة نشرك الخاصّة.

من المهم أن يُشارك هذا المجلّد بين عمليات النشر بحيث تظل الصفحات المخزّنة مؤقّتًا عن بعد ،التي تشير إلى الأصول القديمة المُصرّفة، تعمل طوال مدّة حياة الصفحة المخزّنة.

تتضمّن أداة المطابقة الافتراضية لترجمة الملفّات application.js و application.css وجميع ملفّات JS/CSS (وهذا يشمل جميع الأصول الصوريّة تلقائيًا) من المجلدات app/assets بما في ذلك جواهرك:

[ Proc.new { |filename, path| path =~ /app\/assets/ && !%w(.js .css).include?(File.extname(filename)) },
/application.(css|js)$/ ]

ملاحظة: يُطبّق المُطابق (والأعضاء الآخرين في المصفوفة precompile؛ انظر أدناه) على أسماء الملفّات المصرّفة النهائية. وهذا يعني أن أي شيء يُصّرف إلى JS/CSS مستبعد، وكذلك ملفّات JS/CSS الأوليّة (raw)؛ على سبيل المثال، ‎.coffee و scss. ليست مُضمّنة تلقائيًّا لأنها تصريف من JS/CSS.

يمكنك إن امتلكت بيانات أخرى أو أوراق أنماط منفردة وملفّات JavaScript تريد تضمينها إضافتها إلى المصفوفة precompile في config/initializers/assets.rb:

Rails.application.config.assets.precompile += %w( admin.js admin.css )

ملاحظة: حدّد دائمًا اسم الملف المُصرّف المتوقّع الذي ينتهي بـ js. أو css. حتى إن أردت إضافة ملفّات Sass أو CoffeeScript إلى المصفوفة precompile.

تُنشئ المهمّة أيضًا sprockets-manifest-md5hash.json. (حيث md5hash هي تجزئة MD5) الذي يحتوي على قائمة بجميع أصولك وبصماتها. تستخدمها توابع ريلز المساعدة لتجنّب تسليم طلبات التخطيط (mapping requests) مرة أخرى إلى Sprockets. يبدو ملف بيان اعتيادي كما يلي:

{"files":{"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js":{"logical_path":"application.js","mtime":"2016-12-23T20:12:03-05:00","size":412383,
"digest":"aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b","integrity":"sha256-ruS+cfEogDeueLmX3ziDMu39JGRxtTPc7aqPn+FWRCs="},
"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css":{"logical_path":"application.css","mtime":"2016-12-23T19:12:20-05:00","size":2994,
"digest":"86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18","integrity":"sha256-hqKStQcHk8N+LA5fOfc7s4dkTq6tp/lub8BAoCixbBg="},
"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico":{"logical_path":"favicon.ico","mtime":"2016-12-23T20:11:00-05:00","size":8629,
"digest":"8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda","integrity":"sha256-jSOHuNTTLOzZP6OQDfDp/4nQGqzYT1DngMF8n2s9Dto="},
"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png":{"logical_path":"my_image.png","mtime":"2016-12-23T20:10:54-05:00","size":23414,
"digest":"f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493","integrity":"sha256-9AKBVv1+ygNYTV8vwEcN8eDbxzaequY4sv8DP5iOxJM="}},
"assets":{"application.js":"application-aee4be71f1288037ae78b997df388332edfd246471b533dcedaa8f9fe156442b.js",
"application.css":"application-86a292b5070793c37e2c0e5f39f73bb387644eaeada7f96e6fc040a028b16c18.css",
"favicon.ico":"favicon-8d2387b8d4d32cecd93fa3900df0e9ff89d01aacd84f50e780c17c9f6b3d0eda.ico",
"my_image.png":"my_image-f4028156fd7eca03584d5f2fc0470df1e0dbc7369eaae638b2ff033f988ec493.png"}}

الموقع الافتراضي للملف manifest هو جذر الموقع المحدّد في config.assets.prefix (أو '/assets' بشكل افتراضي).

ملاحظة: إن افتُقدت ملفّات مصرفة مسبقًا (precompiled files) في الإنتاج، فستحصل على الاستثناء Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError الذي يشير إلى اسم الملف (الملفّات) المفقودة.

ترويسة ذات تاريخ صلاحية بعيد الأمد (Far-future Expires Header)

توجد أصول مسبقة التصريف في نظام الملفّات وتُقدّم مباشرة من طرف خادم الويب الخاص بك. لا تحتوي هذه الأصول على ترويسات للمستقبل البعيد بشكل افتراضي. لذا للإستفادة من البصمات، سيكون عليك تحديث إعداد الخادم لإضافة هذه الترويسات.

بالنسبة إلى خادم Apache:

# Apache الوحدة  Expires* يتطلّب التوجيه
# `mod_expires` ستُفعّل

<Location /assets/>
 # Last-Modified غير منصوح به لما يوجد ETag إستعمال
 Header unset ETag
 FileETag None
 # أن ذاكرة التخزين المؤقتة تصلح لسنة واحدة RFC يقول
 ExpiresActive On
 ExpiresDefault "access plus 1 year"
</Location>

بالنسبة إلى خادم NGINX:

location ~ ^/assets/ {
  expires 1y;
  add_header Cache-Control public;
 
  add_header ETag "";
}

التصريف المسبق المحلي

هناك عدّة أسباب قد تُرغّبك في تصريف أصولك محليًّا. من بين هؤلاء:

  • قد لا تمتلك حق الوصول للكتابة بنظام ملفّات إنتاجك.
  • قد تنشر بأكثر من خادم واحد وتريد تجنب تكرار العمل.
  • ربما تنشر بكثرة نشرات لا تتضمّن تغييرات في الأصول.

يسمح لك التصريف المحلّي بحفظ (commit) الملفّات المُصرّفة في عنصر التحكم بالمصدر ونشرها كالمعتاد.

هناك ثلاثة تحذيرات:

  • يجب عدم تشغيل مهمّة نشر Capistrano التي تصرّف الأصول إستباقيًّا.
  • يجب التأكّد من توفّر أي ضواغط (compressors) أو مقلّصات (minifiers) ضروريّة في نظام تطويرك.
  • يجب تغيير إعداد التطبيق التالي:

في config/environments/development.rb ضع السطر التالي:

config.assets.prefix = "/dev-assets"

يؤدي تغيير البادئة prefix لاستخدام Sprockets عنوان URL آخر لعرض الأصول في وضع التطوير وتمرير كل الطلبات إلى Sprockets. لا تزال البادئة (prefix) مضبوطة إلى assets/ في بيئة الإنتاج. بدون هذا التغيير، سيُقدّم التطبيق الأصول المُصرّفة مسبقًا من assets/ في التطوير ولن ترى أي تغييرات محليّة حتى تعيد تصريف الأصول مرة أخرى.

في الممارسة العملية، يسمح لك هذا بالتصريف الاستباقي محليًّا ووضع تلك الملفّات في شجرة عملك وحفظ (commit) هذه الملفّات بالتحكم في المصدر (source control) عند الحاجة. يعمل وضع التطوير مثل المتوقّع.

التصريف المباشر (Live Compilation)

قد ترغب ببعض الحالات في استخدام التصريف المباشر (Live Compilation). يتعامل Sprockets مع جميع طلبات الأصول الموجودة في خط الأنابيب مباشرة في هذا الوضع.

لتفعيل مجموعة الخيارات هذه:

config.assets.compile = true

في الطلب الأول تُصرّف الأصول وتخزّن مؤقّتًا كما هو موضّح في التطوير أعلاه، وتتغيّر أسماء manifest المستخدمة في المساعدات لتشمل التجزئة SHA256.

تحدّد Sprockets ترويسة Cache-Control في طلبيات HTTP إلى max-age=31536000 . يُنبّه هذا جميع ذاكرات التخزين المؤقّت بين الخادم ومتصفّح العميل أن من الممكن تخزين هذا المحتوى (الملف المقدّم) مؤقّتًا لمدة عام واحد. النتيجة هي تقليل عدد طلبات هذا الأصل من خادمك؛ هناك فرصة جيّدة أن يكون الأصل بذاكرة التخزين المؤقّت للمتصفّح المحلّي أو ذاكرة تخزين مؤقّت وسيطة.

يستخدم هذا الوضع ذاكرة أكثر مع أداء أقل من الوضع الافتراضي ولا يُنصح به.

إن كنت بصدد نشر تطبيق إنتاج على نظام دون أي تنفيذ JavaScript سابق (pre-existing JavaScript runtimes)، قد ترغب في إضافة أحدهم إلى ملفك Gemfile:

group :production do
  gem 'mini_racer'
end

شبكات تسليم المحتوى CDNs

CDN تعني شبكة تسليم محتوى (Content Delivery Network)، وهي مصمّمة بشكل أساسي لتخزين الأصول حول العالم بحيث توجد نسخة مخبّأة قريبة جغرافيًا من المتصفّح عندما يطلب أصولًا. أفضل ممارسة هي استخدام CDN أمام تطبيقك إن كنت تقدّم الأصول مباشرة من خادمك ريلز في الإنتاج.

إن النمط الشائع لاستخدام CDN هو تعيين تطبيق إنتاجك كالخادم "الأصلي". يعني هذا أن المتصفّح سيأخذ الملف مباشرة من خادمك لو طلب أصلًا ما من CDN ولم يوجد بذاكرة التخزين المؤقّت ثم يخزّنه مؤقّتًا. إن شغّلت تطبيق ريلز على example.com مثلًا ولديك CDN مُعدّ على mycdnsubdomain.fictional-cdn.com، سيستعلم CDN خادمك مرة واحدة عند تقديم طلب إلى mycdnsubdomain.fictional- cdn.com/assets/smile.png في example.com/assets/smile.png ثمّ يخزّن الطلب. سيأخذ الطلب التالي إلى شبكة CDN لنفس العنوان URL الأصل من النسخة المخزّنة. لا يقرب الطلب خادم ريلز بتاتًا عندما يمكن لـ CDN أن تقدم الأصل مباشرةً. يكون الطلب أسرع نظرًا لأن الأصول من شبكة CDN أقرب جغرافيًا للمتصفّح، وبما أن الخادم لا يحتاج إلى قضاء وقت في تقديم الأصول، يستطيع التركيز على تقديم شفرات التطبيق بأسرع وقت ممكن.

إعداد CDN لتقديم أصول ثابتة

لإعداد CDN خاص بك، يجب تشغيل تطبيقك على الإنترنت على عنوان URL متاح للعموم، example.com مثلًا. بعد ذلك، ستحتاج للتسجيل بخدمة CDN من موفّر استضافة سحابيّة (cloud hosting provider). ثم تحتاج إلى إعداد "أصل" CDN للإشارة إلى موقعك example.com، لذا تحقّق من موفّرك للحصول على توثيقات حول إعداد خادم الأصل.

يجب أن تعطيك CDN التي خصّصتها عنوانًا فرعيًا مخصصًا لتطبيقك مثل mycdnsubdomain.fictional-cdn.com (انتبه إلى أن fictional-cdn.com ليس موفّر CDN صالح في وقت كتابة هذه السطور). الآن بعد أن أعددت خادمك CDN،  ستحتاج لإخبار المتصفحّات أن يستخدموه لأخذ الأصول بدلًا من خادمك ريلز مباشرةً. يمكنك ذلك عن طريق إعداد ريلز ليعيّن CDN كمضيف الأصول بدلًا من استخدام مسار نسبي. لتعيين مضيف الأصول في ريلز، يجب تعيين config.action_controller.asset_host في config/environments/production.rb:

config.action_controller.asset_host = 'mycdnsubdomain.fictional-cdn.com'

ملاحظ: تحتاج فقط إلى توفير "المضيف"، هذا هو النطاق الفرعي ومجال الجذر، لا تحتاج إلى تحديد بروتوكول أو "مخطط" مثل //:http أو //:https. عند طلب صفحة ويب، سيطابق البروتوكول الموجود برابط الأصول المُولّد كيفيّة الوصول إلى صفحة الويب افتراضيًّا.

يمكنك أيضًا تعيين هذه القيمة عبر متغيّر بيئة لجعل تشغيل نسخة تدريج (staging copy) موقعك أسهل:

config.action_controller.asset_host = ENV['CDN_HOST']

ملاحظة: ستحتاج إلى ضبط CDN_HOST بخادمك إلى mycdnsubdomain .fictional-cdn.com.

بمجرد إعدادك لخادمك وشبكتك CDN عند تقديم صفحة ويب بها أصل:

<%= asset_path('smile.png') %>

بدلًا من إعادة مسار مثل assets/smile.png/ (تجاهلنا القيم المشفرة المختصرة [digests] من أجل سهولة القراءة)، سيتضمن العنوان URL المُولّد المسار الكامل لشبكتك CDN:

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png

إن امتلكت CDN نسخة من smile.png، فستقدّمها في المتصفّح ولن يعرف خادمك حتى أنها طُلبت. إن لم تمتلك CDN نسخة، ستحاول العثور عليها في "الأصل" example.com/assets/smile.png، ثم تُخزّنها للاستخدام في المستقبل.

إن رغبت في عرض بعض الأصول فقط من شبكتك CDN، يمكنك استخدام الخيار host: بمساعد أصولك، والذي يعيد تعريف القيمة المحدّدة في config.action_controller.asset_host.

<%= asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %>

تخصيص سلوك التخزين المؤقّت CDN

تعمل شبكة CDN من خلال التخزين المؤقّت للمحتوى. إن كان لـ CDN محتوى قديم أو سيء، فسيضرّ بدل أن يساعد تطبيقك. الغرض من هذا القسم هو وصف سلوك التخزين المؤقّت العام لمعظم شبكات CDN، قد يتصرّف مزوّد خدمتك بشكل مختلف قليلًا.

طلب التخزين المؤقت لطلب CDN

في حين توصف CDN بأنها جيّدة لتخزين الأصول المؤقّتلًا هي تخزّن في الواقع الطلب بأكمله. يتضمن ذلك جسد (body) الأصل وأي ترويسات. وأهمها هو Cache-Control الذي يخبر CDN (ومتصفحّات الويب) بكيفيّة تخزين المحتويات. أي لو طلب شخص ما أصلًا غير موجود assets/i-dont-exist.png/ وكان رد تطبيق ريلز الخطأ 404، من المرجح أن يخزّن CDN الصفحة 404 إن وجدت ترويسة Cache-Control صالحة.

تصحيح ترويسات CDN

استخدام curl طريقة من طرق التحقق من تخزين الترويسات بشكل صحيح في  شبكتك CDN. يمكنك طلب الترويسات من كل من خادمك وشبكتك CDN للتحقق من أنها تطابقهما:

$ curl -I http://www.example/assets/application-
d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK
Server: Cowboy
Date: Sun, 24 Aug 2014 20:27:50 GMT
Connection: keep-alive
Last-Modified: Thu, 08 May 2014 01:24:14 GMT
Content-Type: text/css
Cache-Control: public, max-age=2592000
Content-Length: 126560
Via: 1.1 vegur

مقابل نسخة CDN.

$ curl -I http://mycdnsubdomain.fictional-cdn.com/application-
d0e099e021c95eb0de3615fd1d8c4d83.css
HTTP/1.1 200 OK Server: Cowboy Last-
Modified: Thu, 08 May 2014 01:24:14 GMT Content-Type: text/css
Cache-Control:
public, max-age=2592000
Via: 1.1 vegur
Content-Length: 126560
Accept-Ranges:
bytes
Date: Sun, 24 Aug 2014 20:28:45 GMT
Via: 1.1 varnish
Age: 885814
Connection: keep-alive
X-Served-By: cache-dfw1828-DFW
X-Cache: HIT
X-Cache-Hits:
68
X-Timer: S1408912125.211638212,VS0,VE0

تحقق من توثيقات شبكتك CDN لأي معلومات إضافية قد تقدّمها مثل X-Cache أو لأي ترويسات إضافيّة قد يضيفونها.

شبكات CDN والترويسة Cache-Control

لتعتبر ترويسة التحكم في ذاكرة التخزين المؤقّت (cache control header) أحد مواصفات W3C التي توضّح كيفيّة تخزين الطلب مؤقّتًا. سيستخدم المتصفّح هذه المعلومات لتخزين المحتويات مؤقّتًا عند عدم استخدام أي CDN. هذا مفيد جدًا للأصول التي لم تُعدّل بحيث لا يحتاج المتصفّح لإعادة تنزيل CSS أو JavaScript لموقع الويب مع كل طلب. بشكل عام نريد أن يخبر خادمنا ريلز الشبكة CDN (والمتصفّح) بأن الأصل "عام" أي أن أي ذاكرة تخزين مؤقّت تستطيع تخزين الطلب. كما نرغب عمومًا أيضًا في ضبط المدّة max-age وهي مدّة تخزين الذاكرة التخزين المؤقّت للكائن قبل إبطالها. تُحسب قيمة max-age بالثواني مع حد أقصى يبلغ 31536000 وهو عام واحد. يمكنك فعل ذلك في تطبيقك ريلز عن طريق الإعداد التالي:

config.public_file_server.headers = {
  'Cache-Control' => 'public, max-age=31536000'
}

الآن عندما يقدّم تطبيقك أصلًا في الإنتاج، سيخزّن نظام CDN الأصل لمدة تصل إلى عام. نظرًا لأن معظم شبكات CDN تُخزّن ترويسات الطلبات مؤقّتًا، سيُمرّر Cache-Control إلى جميع المتصفحّات المستقبلية التي تبحث عن هذا الأصل ويعرف المتصفّح بعد ذلك أن بإمكانه تخزين هذا الأصل لفترة طويلة جدًا قبل الحاجة لإعادة طلبها.

شبكات CDN و إبطال ذاكرة التخزين المؤقّت المستند إلى URL

تخزّن معظم شبكات CDN محتويات الأصل مؤقّتًا استنادًا إلى عنوان URL الكامل. أي أن طلبًا إلى :

http://mycdnsubdomain.fictional-cdn.com/assets/smile-123.png

سيكون سيختلف تماما عن:

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png

إن أردت تعيين max-age للمستقبل البعيد (far future) في Cache-Control (وأنت فعلًا ترغب بذلك)، تأكّد عند تغيير أصولك من أن ذاكرة تخزينك باطلة (invalidated). عند تغيير الوجه المبتسم مثلًا في صورة من الأصفر إلى الأزرق، ستريد أن يحصل جميع زوار موقعك على الوجه الأزرق الجديد. عند استخدام CDN مع أنبوب الأصول ريلز، تُضبط القيمة config.assets.digest إلى true افتراضيًا بحيث يكون لكل أصل اسم ملف مختلف عند تغييره. بهذه الطريقة، لن تضطّر لإبطال أي عناصر في ذاكرة التخزين المؤقّت. باستخدام اسم أصل فريد مختلف بدلًا من ذلك، يحصل المستخدمون على آخر الأصول.

تخصيص الأنابيب

ضغط شيفرات CSS

أحد الخيارات لضغط CSS هو YUI. يوفّر الضاغط YUI CSS خدمة تقليص.

يفعّل السطر التالي ضغط YUI ويتطلّب جوهرة yui-compressor:

config.assets.css_compressor = :yui

الخيار الآخر لضغط CSS إن امتلكت جوهرة sass-rails مثبّتة هو :

config.assets.css_compressor = :sass

ضغط شيفرات JavaScript

الخيارات الممكنة لضغط شيفرات JavaScript هي: closure: و uglifier: و yui:. وتتطلب استخدام الجواهر closure-compiler أو uglifier أو yui-compressor على التوالي.

يتضّمن الملف Gemfile الافتراضي uglifier. تغلف هذه الجوهرة UglifyJS (مكتوبة من أجل NodeJS) في روبي. تصغّر شفرتك عبر إزالة المساحة البيضاء والتعليقات، وتقصير أسماء المتغيّرات المحلية، وأداء غيرها من التحسينات الصغيرة مثل تغيير تصريحات if و else لعاملات ثلاثية (ternary operators) حيثما أمكن.

يستدعى السطر التالي uglifier لضغط JavaScript:

config.assets.js_compressor = :uglifier

ملاحظة: تحتاج إلى تنفيذ مدعوم من ExecJS كي تستخدم uglifier. لديك تنفيذ JavaScript مثبت في نظام تشغيلك إن كنت تستخدم macOS أو Windows.

تقديم نسخة مضغوطة من الأصول

ستُولّد نسخة مضغوطة (gzipped) من الأصول المُصرّفة افتراضيًّا إلى جانب نسخة غير مضغوطة (non-gzipped) من الأصول. تساعد الأصول المضغوطة (gzipped assets) في تقليل إرسال البيانات عبر السلك (wire). يمكنك إعداده عبر ضبط الراية gzip:

config.assets.gzip = false # تعطيل توليد الأصول المضغوطة

استخدام ضاغطك الخاص

تأخذ إعدادات تهيئة الضاغط لضغط شيفرات CSS و JavaScript أيضًا أي كائن. يجب أن يحتوي هذا الكائن على تابع يدعى compress يأخذ سلسلة كوسيطة وحيدة ويجب أن يعيد سلسلة نصيّة:

class Transformer
  def compress(string)
    do_something_returning_a_string(string)
  end
end

مرّر كائنًا جديدًا إلى خيار الإعداد في application.rb:

config.assets.css_compressor = Transformer.new

تغيير مسار الأصول

المسار العام الذي يستخدمه Sprockets افتراضيًّا هو assets/.

يمكن تغييره إلى مسار آخر:

config.assets.prefix = "/some_other_path"

يعد هذا التغيير خيارًا مفيدًا إن كنت بطور تحديث مشروع قديم لم يستخدم أنبوب الأصول لكن يستخدم بالفعل هذا المسار أو كنت ترغب في استخدام هذا المسار لمورد جديد.

ترويسات X-Sendfile

الترويسة X-Sendfile هي توجيه لخادم الويب لتجاهل الاستجابة من التطبيق، وتخديم ملف محدّد من القرص بدلًا من ذلك. هذا الخيار معطّل افتراضيًا، ولكن يمكن تفعيله إن دعمه الخادم. تُمرّر عند التفعيل مسؤوليّة خدمة الملف إلى خادم الويب وهو أسرع. ألق نظرة على send_file حول كيفيّة استخدام هذه الميزة.

يدعم الخادم Apache و NGINX هذا الخيار الذي يمكن تفعيله في config/environments/production.rb:

# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX

تحذير: إن كنت بصدد تحديث تطبيق موجود بالفعل وتنوي استخدام هذا الخيار، احرص على لصق خيار الإعداد هذا في production.rb فقط وأي بيئات أخرى تُعرّفها بسلوك الإنتاج (وليس application.rb).

ألق نظرة على مستندات خادم ويب إنتاجك: الخادم Apache، أو الخادم NGINX لمزيد من التفاصيل.

المخزن المؤقت للأصول

يخزّن Sprockets الأصول افتراضيًّا في tmp/cache/assets في بيئات التطوير والإنتاج. يمكن تغيير هذا الأسلوب كما يلي:

config.assets.configure do |env|
  env.cache = ActiveSupport::Cache.lookup_store(:memory_store,
                                                { size: 32.megabytes })
end

لتعطيل مخزن ذاكرة التخزين المؤقّت للأصول:

config.assets.configure do |env|
  env.cache = ActiveSupport::Cache.lookup_store(:null_store)
end

إضافة الأصول إلى جواهرك

يمكن أيضا أن تأتي الأصول أيضًا من مصادر خارجية في شكل جواهر.

وخير مثال على ذلك الجوهرة jquery-rails. تحتوي هذه الجواهر على صنف محرّك يرث من Rails::Engine. وبهذا يُبلّغ ريلز أن دليل هذه الجواهر قد يحتوي على أصول ويُضاف app/assets و lib/assets vendor/assets ومجلّدات vendor/assets خاصّة بهذا المحرك إلى مسار البحث في Sprockets.

جعل مكتبتك أو جوهرتك معالجين استباقيين (Pre-Processor)

تستخدم Sprockets المعالجات والمحوّلات والضاغطات والمصدّرات لتوسيع وظائف Sprockets. ألق نظرة على توسيع Sprockets لمعرفة المزيد. سجّلنا هنا معالجًا مسبقًا لإضافة تعليق إلى نهاية ملفّات text/css (ذات اللاحقة css. ).

module AddComment
  def self.call(input)
    { data: input[:data] + "/* Hello From my sprockets extension */" }
  end
end

والآن بعد أن صار لديك وحدة تعمل على تعديل بيانات الإدخال، حان الوقت لتسجيلها كمعالِج مسبق لنوع mime.

Sprockets.register_preprocessor 'text/css', AddComment

الترقية من نسخ ريلز القديمة

هناك بعض المشكلات عند الترقية من الإصدار 3.0 أو ‎2.x. أوّلها نقل الملفّات من /public إلى المواقع الجديدة. راجع قسم تنظيم الأصول المذكور أعلاه لإرشادات حول المواقع الصحيحة لأنواع الملفّات المختلفة.

الخطوة التالية هي تحديث ملفّات البيئة المختلفة مع الخيارات الافتراضيّة الصحيحة.

في application.rb:

# نسخة أصولك؛ غيره إن أردت لجميع أصولك أن تنتهي صلاحيتها
config.assets.version = '1.0'
 
# config.assets.prefix = "/assets" غير المسار الذي تُخدَّم الأصول منه

في development.rb:

# وسع الأسطر التي تحمل الأصول
config.assets.debug = true

وفي production.rb:

# (اختر الضاغط المراد استخدامه (إن كان هنالك أي واحد
config.assets.js_compressor = :uglifier
# config.assets.css_compressor = :yui
 
# لا تتراجع إلى أنبوب الأصول إن فُقِد أصل مصرف مسبقًا
config.assets.compile = false
 
# للأصول URL ولد قيم مشفرة مختصرة من أجل عناوين
config.assets.digest = true
 
# application.js و ،application.css) صرف الأصول الإضافية مسبقًا
# (CSS أو JS وجميع الشيفرات المضافة مسبقًا التي ليست
# config.assets.precompile += %w( admin.js admin.css )

لم يعد الإصدار 4 وما أعلى من ريلز يعيّن قيم الإعدادات الافتراضية لـ Sprockets في test.rb، لذلك يتطلّب test.rb الآن إعداد Sprockets. الإعدادات الافتراضية القديمة في بيئة الاختبار هي:

config.assets.compile = true
config.assets.compress = false
config.assets.debug = false
config.assets.digest = false

يجب أيضًا إضافة ما يلي إلى الملف Gemfile:

gem 'sass-rails',   "~> 3.2.3"
gem 'coffee-rails', "~> 3.2.1"
gem 'uglifier'

مصادر