الفرق بين المراجعتين ل"Rails/autoloading and reloading constants"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
سطر 108: سطر 108:
 
* كائن الوحدة الذي يتبع الكلمة المفتاحية <code>module</code> يُضَاف عندما ينفذ جسمه، وينشر بعد ذلك.
 
* كائن الوحدة الذي يتبع الكلمة المفتاحية <code>module</code> يُضَاف عندما ينفذ جسمه، وينشر بعد ذلك.
 
* الصنف المنفرد (singleton class) الذي يُفتتَح مع <code>class << object</code> يضاف، ثم ينتشر لاحقًا.
 
* الصنف المنفرد (singleton class) الذي يُفتتَح مع <code>class << object</code> يضاف، ثم ينتشر لاحقًا.
* عندما يستدعى <code>instance_eval</code> مع سلسلة نصية تمثِّل الوسيط، يُضَاف الصنف المفرد للمتلقي إلى تداخل الشيفرة المقيمة. عندما يستدعى class_eval أو module_eval باستخدام سلسلة وسيطة، يُدفع جهاز الاستقبال إلى تداخل رمز eval'ed.
+
* عندما يستدعى <code>instance_eval</code> مع سلسلة نصية تمثِّل الوسيط، يُضَاف الصنف المفرد للمتلقي إلى تداخل الشيفرة المقيمة. عندما يستدعى <code>class_eval</code> أو <code>module_eval</code> باستخدام سلسلة وسيطة، يضاف المستقبل إلى تداخل الشيفرة المقيمة.
* التداخل في المستوى الأعلى من الشفرة التي ُفسرت بواسطة Kernel#load بأنه فارغ، إلا إذا تلقى اتصال التحميل قيمة حقيقية كوسيطة ثانية، وفي هذه الحالة تُدفع وحدة نمطية مجهولة أُنشئت حديثًا بواسطة Ruby.
+
* التداخل في المستوى الأعلى من الشيفرة التي فسرت بواسطة <code>[[Ruby/Kernel/load|Kernel.load]]</code> يُعدُّ فارغًا، إلا إذا تلقى استدعاء [[Ruby/Kernel/load|<code>load</code>]] قيمة صحيحة كوسيط ثانية، وفي هذه الحالة تضاف وحدة مجهولة أُنشئت حديثًا بواسطة روبي.
من المثير للاهتمام ملاحظة أن الكتل لا تقم بتعديل المكدس. وعلى وجه الخصوص، فإن الكتل التي قد مُررت إلى Class.new و Module.new لا تحصل على تعريف الفئة أو الوحدة النمطية التي دُفعت إلى تشعبها. هذا هو أحد الاختلافات بين تحديد الفئات والوحدات بطريقة أو بأخرى.
+
من المثير للاهتمام ملاحظة أن الكتل لا تعدل المكدس. وعلى وجه الخصوص، فإن الكتل التي قد مُرِّرت إلى <code>Class.new</code> و <code>Module.new</code> لا تجلب الصنف أو الوحدة التي يجري تعريفها والتي أضيفت إلى تشعبها. هذا هو أحد الاختلافات بين تحديد الأصناف والوحدات بطريقة أو بأخرى.
  
=== تعريفات الفئات Class والوحدات Module هي مهام ثابتة ===
+
=== تعريفات الصنف والوحدة هي مهام ثابتة ===
لنفترض أن المقتطف التالي ينشئ فئة (بدلاً من إعادة فتحها):
+
لنفترض أن الشيفرة البسيطة التالية تنشئ صنفًا (بدلًا من إعادة فتحه):
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
class C
 
class C
 
 
End
 
End
 
</syntaxhighlight>
 
</syntaxhighlight>
  
ينشئ Ruby الثابت C في الكائن ويخزن في هذا الثابت كائن فئة. اسم نسخة الفئة هي "C"، سلسلة، سميت بعد الثابت.
+
ينشئ روبي الثابت <code>C</code> في <code>Object</code> ويخزن في هذا الثابت على أنه كائن صنف. اسم نسخة الصنف هي "C"، سلسلة نصية، سميت بعد الثابت.
  
هذا هو،
+
إليك أيضًا الشيفرة التالية:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
class Project < ApplicationRecord
 
class Project < ApplicationRecord
 
 
End
 
End
 
</syntaxhighlight>
 
</syntaxhighlight>
  
ينفذ مهمة ثابتة تعادل
+
ينفذ مهمة ثابتة تعادل:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
Project = Class.new(ApplicationRecord)
 
Project = Class.new(ApplicationRecord)
 
</syntaxhighlight>
 
</syntaxhighlight>
  
بما في ذلك تحديد اسم الفئة كأثر جانبي:
+
بما في ذلك تحديد اسم الصنف على أنَّه تأثير جانبي:<syntaxhighlight lang="rails">
 
 
 
Project.name # => "Project"
 
Project.name # => "Project"
 +
</syntaxhighlight>المهمة الثابتة لها قاعدة خاصة لتحقيق ذلك: إذا كان الكائن الذي تعين هو صنف أو وحدة غير معروفة، تعين روبي اسم الكائن إلى اسم الثابت.
  
المهمة الثابتة لها قاعدة خاصة لتحقيق ذلك: إذا كان الكائن الذي تعين هو فئة أو وحدة نمطية غير معروفة، يقوم Ruby بتعيين اسم الكائن إلى اسم الثابت.
+
'''تنبيه''': من الآن فصاعدًا، ما يحدث للثابت والنسخة لا يهم. على سبيل المثال، يمكن حذف الثابت، أو يمكن تعيين كائن الصنف إلى ثابت مختلف، أو يمكن تخزينه بدون ثابت بعد الآن، ...إلخ. بمجرد تعيين الاسم، لا يتغير.
 
 
ملاحظة: من الآن فصاعدا، ما يحدث للثابت والنسخة لا يهم. على سبيل المثال، يمكن حذف الثابت، يمكن تعيين كائن الفئة إلى ثابت مختلف، يمكن تخزينه بدون ثابت بعد الآن، إلخ. بمجرد تعيين الاسم، لا يتغير.
 
  
بالمثل، إنشاء الوحدة النمطية باستخدام الكلمة الأساسية module كما في
+
بالمثل، يجري إنشاء وحدة باستخدام الكلمة المفتاحية <code>module</code> بالشكل التالي:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
module Admin
 
module Admin
 
 
End
 
End
 
</syntaxhighlight>
 
</syntaxhighlight>
  
تنفيذ مهمة ثابتة تعادل
+
تنفيذ مهمة ثابتة تعادل:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
Admin = Module.new
 
Admin = Module.new
 +
</syntaxhighlight>
  
بما في ذلك تحديد الاسم كأثر جانبي:
+
بما في ذلك تحديد الاسم على أنَّه تأثير جانبي:<syntaxhighlight lang="rails">
<syntaxhighlight lang="rails">
 
 
Admin.name # => "Admin"
 
Admin.name # => "Admin"
</syntaxhighlight>
+
</syntaxhighlight>'''تحذير''': سياق التنفيذ لكتلة مُررت إلى <code>Class.new</code> أو <code>Module.new</code> لا يكافئ تمامًا أحد تعريفات الجسم باستخدام الكلمتين المفتاحيتين <code>class</code> و <code>module</code>. لكن كل من التعابير ينتج عنها نفس المهمة الثابتة.
 
 
ملاحظة: لا يعتمد سياق التنفيذ لكتلة مُررت إلى Class.new أو Module.new تمامًا على أحد نصوص التعريفات باستخدام الكلمات الأساسية للفئة والوحدة النمطية. لكن كل من التعابير ينتج عنها نفس المهمة الثابتة.
 
  
وهكذا، عندما يقول أحدهم بشكل غير رسمي "صنف السلسلة" ، فهذا يعني بالفعل: كائن الفئة المخزن في الثابت المسمى "String" في كائن الفئة المخزن في ثابت الكائن. السلسلة هي ثابت روبي عادي وكل شيء يتعلق بالثوابت مثل خوارزميات الإحلال ينطبق عليها.
+
وهكذا، عندما يقول أحدهم بشكل غير رسمي "الصنف <code>String</code>" ، فهذا يعني بالفعل: كائن الصنف المخزن في الثابت المسمى "String" في كائن الصنف المخزن في الثابت <code>Object</code>. إن <code>String</code> هي ثابت روبي عادي وكل شيء يتعلق بالثوابت مثل خوارزميات الاستبيان ينطبق عليها.
  
وبالمثل ، في وحدة التحكم:
+
وبالمثل، في وحدة التحكم:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
class PostsController < ApplicationController
 
class PostsController < ApplicationController
 
+
  def index
 def index
+
    @posts = Post.all
 
+
  end
   @posts = Post.all
+
end
 
 
 end
 
 
 
End
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Post ليست بناء جملة لفئة. بدلاً من ذلك، Post هو ثابت روبي منتظم. إذا كان كل شيء جيد، يقيم الثابت إلى كائن يستجيب للجميع.
+
لا يعد <code>Post</code> صياغة لصنف. بدلًا من ذلك، <code>Post</code> هو ثابت روبي عادي. إذا كان كل شيء جيد، يقيم الثابت إلى كائن يستجيب إلى <code>all</code>.
  
هذا هو السبب في أننا نتحدث عن التحميل التلقائي للثابت، Rails لديها القدرة على تحميل الثوابت على الطيران.
+
هذا هو السبب في أننا نتحدث عن التحميل التلقائي للثابت (constant autoloading)، إذ ريلز لديها القدرة على تحميل الثوابت مباشرةً وتلقائيًّا.
  
 
=== تخزين الثوابت في الوحدات ===
 
=== تخزين الثوابت في الوحدات ===
الثوابت تنتمي إلى وحدات بالمعنى الحرفي للغاية. تحتوي الفئات والوحدات النمطية على جدول ثابت. فكر في الأمر كأنه جدول التجزئة.
+
الثوابت تنتمي إلى وحدات بالمعنى الحرفي. تحتوي الأصناف والوحدات على جدول للثوابت. فكر في الأمر كأنه [[Ruby/Hash|جدول Hash]].
  
دعونا نحلل مثال لفهم ما يعنيه ذلك حقاً. في حين أن الإساءات الشائعة للغة مثل "صنف السلسلة" ملائمة، فإن العرض سيكون دقيقًا هنا لأغراض تعليمية.
+
دعونا نحلل مثالًا لفهم ما يعنيه ذلك حقًا. في حين أن الإساءات الشائعة للغة مثل "الصنف <code>String</code>" صحيحة، فإن العرض سيكون دقيقًا هنا لأغراض تعليمية.
  
 
لنأخذ بعين الاعتبار تعريف الوحدة التالية:
 
لنأخذ بعين الاعتبار تعريف الوحدة التالية:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
module Colors
 
module Colors
 
+
  RED = '0xff0000'
 RED = '0xff0000'
+
end
 
 
End
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
أولاً، عند معالجة الكلمة الأساسية module، ينشئ المترجم مدخل جديد في الجدول الثابت لكائن الفئة المخزن في الثابت Object. يقترن المدخل بالاسم "Colors" إلى كائن الوحدة النمطية التي أُنشئت حديثًا. علاوة على ذلك، يعين المترجم اسم كائن الوحدة النمطية الجديد ليكون سلسلة "Colors".
+
أولاً، عند معالجة الكلمة المفتاحية <code>module</code>، ينشئ المترجم قيمة جديدة في جدول الثوابت لكائن الصنف المخزن في الثابت <code>Object</code>. يقترن المدخل بالاسم "Colors" إلى كائن الوحدة التي أُنشئت حديثًا. علاوة على ذلك، يعين المترجم اسم كائن الوحدة الجديد ليكون السلسلة النصية "Colors".
  
لاحقاً، عندما يُفسر نص تعريف الوحدة النمطية، يُنشأ إدخال جديد في الجدول الثابت لكائن الوحدة النمطية المخزن في الثابت Colors. يعين هذا المدخل الاسم "RED" إلى السلسلة "0xff0000".
+
لاحقًا، عندما يُفسر نص تعريف الوحدة، يُنشأ قيمة جديد في جدول الثوابت لكائن الوحدة المخزن في الثابت <code>Colors</code>. تعين تلك القيمة الاسم "RED" إلى السلسلة "0xff0000".
  
على وجه الخصوص، لا يرتبط Color :: RED كليا بأي ثابت RED آخر قد يعيش في أي فئة أو كائن وحدة أخرى. إذا كان هناك أي منها، سيكون لديهم مدخلات منفصلة في الجداول الثابتة الخاصة بها.
+
على وجه الخصوص، لا يرتبط <code>Color::RED</code> كليًّا بأي ثابت <code>RED</code> آخر قد يوجد في أي كائن صنف أو وحدة أخرى. إذا كان هناك أي منها، سيكون لديها قيم منفصلة في جداول الثوابت الخاصة بها.
  
يجب إيلاء اهتمام خاص في الفقرات السابقة للتمييز بين كائنات الفئة والوحدات النمطية والأسماء الثابتة وكائنات القيمة المرتبطة بها في الجداول الثابتة.
+
يجب إعطاء اهتمام خاص في الفقرات السابقة للتمييز بين كائنات الصنف والوحدات والأسماء الثابتة وكائنات القيمة المرتبطة بها في جداول الثوابت.
  
=== خوارزميات الإحلال ===
+
=== خوارزميات الاستبيان (Resolution Algorithms) ===
  
==== خوارزمية الإحلال للثوابت النسبية ====
+
==== خوارزمية الاستبيان للثوابت النسبية ====
في أي مكان في الكود، دعنا نعرّف cref ليكون العنصر الأول من التشعب إذا لم يكن فارغًا، أو Object خلاف ذلك.
+
في أي مكان في الشيفرة، دعنا نعرّف <code>cref</code> ليكون العنصر الأول من التشعب إذا لم يكن فارغًا، أو <code>Object</code> خلاف ذلك.
  
دون الحصول على الكثير من التفاصيل، فإن خوارزمية الإحلال لمراجع الثابت النسبي تسير على النحو التالي:
+
دون الحصول على الكثير من التفاصيل، فإن خوارزمية الاستبيان لمراجع الثابت النسبي تسير على النحو التالي:
* إذا لم يكن التشعب فارغًا، فسيُبحث عن الثابت في عناصره وترتيبه. يُتجاهل أسلاف تلك العناصر.
+
* إذا لم يكن التشعب فارغًا، فسيُبحث عن الثابت في عناصره بالترتيب. يُتجاهل أسلاف تلك العناصر.
* إذا لم يُعثرعليها، ثم يمشي خوارزمية سلسلة السلف من cref.
+
* إذا لم يُعثر عليه، ستصعد الخوارزمية عبر سلسلة أسلاف <code>cref</code>.
* إذا لم يُعثر على وحدة cref وهي وحدة نمطية، فسيٌبحث عن الثابت في Object.
+
* إذا لم يُعثر عليه وكانت cref وحدةً، فسيُبحَث عن الثابت في <code>Object</code>.
* إذا لم يُعثرعليها، يستدعى const_missing على cref. التنفيذ الافتراضي const_missing يثير NameError ولكن يمكن تجاوزه.
+
* إذا لم يُعثر عليه، يستدعى <code>const_missing</code> على <code>cref</code>. التنفيذ الافتراضي للتابع <code>const_missing</code> يرمي الاستثناء <code>[[Ruby/NameError|NameError]]</code> ولكن يمكن تجاوزه.
لا يحاكي التثبيت التلقائي لـ Rails هذه الخوارزمية ، ولكن نقطة البداية هي اسم الثابت الذي سيُحمل تلقائيًا، و cref. انظر المزيد في المراجع النسبية.
+
لا يحاكي التحميل التلقائي لريلز هذه الخوارزمية، ولكن نقطة البداية هي اسم الثابت الذي سيُحمل تلقائيًا، و <code>cref</code>. اطلع على قسم [[Rails/autoloading and reloading constants#.D8.A7.D9.84.D9.85.D8.B1.D8.A7.D8.AC.D8.B9 .D8.A7.D9.84.D9.86.D8.B3.D8.A8.D9.8A.D8.A9|المراجع النسبية]] للمزيد من التفاصيل.
  
==== خوارزمية الإحلال للثوابت المؤهلة ====
+
==== خوارزمية الاستبيان للثوابت المؤهلة ====
الثوابت المؤهلة تبدو كالتالي:
+
الثوابت المؤهلة (qualified constants) تبدو كالتالي:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
Billing::Invoice
 
Billing::Invoice
 
</syntaxhighlight>
 
</syntaxhighlight>
  
ملاحظة: Billing::Invoice تتكون من اثنين من الثوابت: Billing وهي نسبية وتُحل باستخدام خوارزمية القسم السابق.
+
يتكون <code>Billing::Invoice</code> من اثنين من الثوابت: <code>Billing</code> وهو نسبي ويُستبيَن باستخدام خوارزمية القسم السابق.
  
من شأن النقطتان البارزة أن تجعل الجزء الأول مطلقًا وليس نسبيًا: ::Billing::Invoice. من شأن ذلك أن يجبر Billing على الظهور كثابت عالي المستوى فقط.
+
'''تنبيه''': من شأن النقطتان البارزة أن تجعل الجزء الأول مطلقًا وليس نسبيًا: <code>::Billing::Invoice</code>. من شأن ذلك أن يجبر <code>Billing</code> على الظهور كثابت عالي المستوى فقط.
  
Invoice على الجانب الآخر مؤهلة من قِبل Billing وسنرى إحلالها التالي. لنحدد الأصل parent  ليكون هذا العنصر المؤهل للفئة أو الوحدة النمطية، أي Billing في المثال أعلاه. الخوارزمية للثوابت المؤهلة تسير مثل هذا:
+
<code>Invoice</code> على الجانب الآخر مؤهل من قبل <code>Billing</code> وسنرى استبيانه وقراره فيما يلي. لنحدد الأب ليكون هذا العنصر المؤهل للصنف أو الوحدة، أي <code>Billing</code> في المثال أعلاه. الخوارزمية للثوابت المؤهلة تسير بالشكل التالي:
* الثابت هو البحث في الأصل وأسلافه. في Ruby> = 2.5 ، يتخطى الكائن إذا كان موجودًا بين السلف. مازال يتحقق من Kernel و BasicObject.
+
* يُبحّث عن الثابت في الأب وأسلافه. في الإصدار 2.5 من ريلز وما بعده، يتخطى <code>Object</code> إذا كان موجودًا بين الأسلاف وستستمر عملية التحقق من <code>Kernel</code> و <code>BasicObject</code>.
* إذا فشل البحث، يستدعى const_missing في الأصل parent. التنفيذ الافتراضي const_missing يثير NameError ولكن يمكن تجاوزه.
+
* إذا فشل البحث، يستدعى <code>const_missing</code> في الأب. التنفيذ الافتراضي <code>const_missing</code> يرمي الاستثناء <code>[[Ruby/NameError|NameError]]</code> ولكن يمكن تجاوزه.
ملاحظة:  في Ruby <2.5 String :: Hash يقيّم إلى Hash والمترجم يصدر تحذيرًا:
+
'''تنبيه''': ما قبل الإصدار 2.5 من ريلز، يقيم <code>String::Hash</code> إلى <code>[[Ruby/Hash|Hash]]</code> والمترجم يصدر تحذيرًا: "toplevel constant Hash referenced by String::Hash" (ثابت <code>[[Ruby/Hash|Hash]]</code> ذو مستوى أعلى أشير إليه بواسطة <code>String::Hash</code>). بدءًا من الإصدار 2.5، يرمي <code>String::Hash</code> الاستثناء <code>[[Ruby/NameError|NameError]]</code> عند تخطي <code>Object</code>.
  
"تجزئة ثابت ذات مستوى أعلى يرجع إليها بواسطة String :: Hash". يبدأ بـ 2.5 ، String :: Hash يثير NameError لأنه يتخطي الكائن.
+
كما ترى، هذه الخوارزمية أبسط من تلك الثوابت النسبية. على وجه الخصوص، لا يلعب التشعب أي دور هنا، والوحدات ليست حالة خاصة؛ إذا لم تكن لديها هي أو أسلافها الثوابت، لن يُتحقق من <code>Object</code>.
  
كما ترى، هذه الخوارزمية أبسط من تلك الثوابت النسبية. على وجه الخصوص، لا يلعب التشعب أي دور هنا، والوحدات ليست ذات غلاف خاص، إذا لم تكن لديهم أو أسلافهم لديهم الثوابت، لا يحدد الكائن.
+
لا يحاكي التحميل التلقائي لريلز هذه الخوارزمية، ولكن نقطة البداية هي اسم الثابت الذي سيحمل تلقائيًا، والأب. اطلع على قسم [[Rails/autoloading and reloading constants#.D9.85.D8.B1.D8.A7.D8.AC.D8.B9 .D9.85.D8.A4.D9.87.D9.84.D8.A9|المراجع الؤهلة]] للمزيد من التفاصيل.
  
لا يحاكي التحميل التلقائي لRails هذه الخوارزمية، ولكن نقطة البداية هي اسم الثابت الذي سيحمل تلقائيًا، والأصل parent. انظر المزيد في المراجع المؤهلة.
+
== مفردات اللغة ==
  
== مفردات اللغه ==
+
=== مجالات أسماء الأب ===
 +
بالنظر إلى سلسلة نصية ذات مسار ثابت، نحدد مجال اسم الأب الخاص بها لتكون السلسلة التي تنتج عن إزالة الجزء الموجود في أقصى اليمين.
  
=== مجالات الأسماء الرئيسية Parent ===
+
على سبيل المثال ، مجال الاسم للأب من السلسلة "A::B::C" هو السلسلة "A::B"، ومجال الاسم للأب من "A::B" هو "A"، ومجال الاسم للأب من "A " هو "".
بالنظر إلى سلسلة ذات مسار ثابت، نحدد مساحة الاسم الأصل الخاصة بها لتكون السلسلة التي تنتج عن إزالة الجزء الموجود في أقصى اليمين.
 
  
على سبيل المثال ، مساحة الاسم الأصل من السلسلة "A :: B :: C" هي السلسلة "A :: B" ، مساحة الاسم الأصل من "A :: B" هي "A" ، ومساحة الاسم الأصل من "A " هو "".
+
تفسير مجال اسم الأب عند التفكير في الأصناف والوحدات أمر صعب رغم ذلك. لنفترض أن الوحدة <code>M</code> تحمل الاسم "A::B":
 
+
* قد لا يعكس مجال الاسم للأب، الذي هو "A"، التشعب في موضع محدد.
ترجمة مساحة اسم الأصل عند التفكير في الفئات والوحدات أمر صعب رغم ذلك. لنفترض أن وحدة M تحمل الإسم "A :: B":
+
* قد لا يكون الثابت <code>A</code> موجودًا بعد الآن، إذ يكون بعض التعليمات البرمجية قد أزالته من الكائن.
* قد لا تعكس مساحة الاسم الأصل، "A"، التشعب في موضع محدد.
+
* إذا كان <code>A</code> موجودًا، فقد لا يكون الصنف أو الوحدة التي كانت في الأصل في <code>A</code> موجودة بعد الآن. على سبيل المثال، إذا كانت هناك مهمة ثابتة أخرى بعد إزالة الثابت، فسيكون هناك كائن مختلف بشكل عام.
* قد لا يكون ثابت A موجودًا بعد الآن، فقد يكون بعض التعليمة البرمجية قد أزالته من الكائن.
+
* في مثل هذه الحالة، يمكن أن يحدث أن يعاد تعيين <code>A</code> إلى صنف جديدة أو وحدة تسمى أيضا "A"!
* إذا كان A موجودًا، فقد لا تكون الفئة أو الوحدة التي كانت في الأصل في A موجودة بعد الآن. على سبيل المثال، إذا كانت هناك مهمة ثابتة أخرى بعد إزالة الثابت، فسيكون هناك كائن مختلف بشكل عام.
+
* في السيناريوهات السابقة، لم يعد من الممكن الوصول إلى <code>M</code> خلال <code>A::B</code> ولكن كائن الوحدة نفسه يمكن أن يبقى على قيد الحياة في مكان ما وسيبقى اسمه "A::B".
* في مثل هذه الحالة ، يمكن أن يحدث أن يعاد تعيينه A فئة جديدة أو وحدة تسمى أيضا "A"!
+
تكمن فكرة وجود مجال اسم أب هي عند جوهر خوارزميات التحميل التلقائي والمساعدة على شرح وفهم دوافعها بشكل حدسي، ولكن كما ترى أن الاستعارة تتسرب بسهولة. إذا أخذنا في الاعتبار حالة حدية يجب التفكير فيها، فضع في حسبانك دائمًا أنه عبر "مجال اسم الأب"، الدليل يعني بالضبط اشتقاق سلسلة نصية محددة.
* في السيناريوهات السابقة لم يعد من الممكن الوصول إلى M خلال A :: B ولكن كائن الوحدة النمطية نفسه يمكن أن يبقى على قيد الحياة في مكان ما وسيبقى اسمه "A :: B".
 
تكمن فكرة وجود مساحة اسم رئيسية في جوهر خوارزميات التحميل التلقائي وتساعد على شرح وفهم دوافعهم بشكل حدسي، ولكن كما ترون أن الاستعارة تتسرب بسهولة. إذا أخذنا في الاعتبار حالة الحافة التي يجب التفكير فيها، فاخذ بعين الاعتبار دائمًا أن "مساحة الاسم الاصل" الدليل يعني بالضبط أن الاشتقاق سلسلة محددة.
 
  
 
=== آلية التحميل ===
 
=== آلية التحميل ===
تقوم Rails بالتحميل الآلي للملفات مع Kernel#load عندما تكون config.cache_classes خاطئة، الإفتراضي يكون في وضع التطوير، ومع Kernel#require خلاف ذلك، الافتراضي يكون في وضع الإنتاج.
+
تقوم ريلز بالتحميل الآلي للملفات مع <code>[[Ruby/Kernel/load|Kernel.load]]</code> عندما تكون قيمة الضبط <code>config.cache_classes</code> هي <code>false</code>، وهو الافتراضي في وضع التطوير، ومع <code>[[Ruby/Kernel/require|Kernel.require]]</code> خلاف ذلك، وهو الافتراضي في وضع الإنتاج.
  
Kernel#load يسمح لـ Rails بتنفيذ الملفات أكثر من مرة إذا كان إعادة التحميل للثابت مُفعل.
+
يسمح <code>[[Ruby/Kernel/load|Kernel.load]]</code> لريلز بتنفيذ الملفات أكثر من مرة إذا كانت [[Rails/autoloading and reloading constants#.D8.A5.D8.B9.D8.A7.D8.AF.D8.A9 .D8.A7.D9.84.D8.AA.D8.AD.D9.85.D9.8A.D9.84 .D9.84.D9.84.D8.AB.D9.88.D8.A7.D8.A8.D8.AA|إعادة التحميل]] للثابت مُفعلة.
  
يستخدم هذا الدليل كلمة "load" بحرية للإشارة إلى ترجمة ملف معين، لكن الآلية الفعلية يمكن أن تكون Kernel # load أو Kernel#require اعتمادًا على تلك العلامة.
+
يستخدم هذا الدليل الكلمة «تحميل» (load) بحرية للإشارة إلى ترجمة ملف معين، لكن الآلية الفعلية يمكن أن تكون عبر <code>[[Ruby/Kernel/load|Kernel.load]]</code> أو <code>[[Ruby/Kernel/require|Kernel.require]]</code> اعتمادًا على تلك الراية.
  
 
== إتاحة التحميل التلقائي ==
 
== إتاحة التحميل التلقائي ==
يكون Rails دائمًا قادرًا على التحميل التلقائي بشرط توفر بيئة التشغيل الخاصة به. على سبيل المثال التحميل التلقائي للأمر runner:
+
يكون ريلز دائمًا قادرًا على التحميل التلقائي بشرط توفر بيئة التشغيل الخاصة به. على سبيل المثال، الأمر <code>runner</code> التالية يعيد التحميل:
<syntaxhighlight lang="rails">
+
<syntaxhighlight lang="shell">
 
$ bin/rails runner 'p User.column_names'
 
$ bin/rails runner 'p User.column_names'
 
 
["id", "email", "created_at", "updated_at"]
 
["id", "email", "created_at", "updated_at"]
 
</syntaxhighlight>
 
</syntaxhighlight>
  
وحدة تحكم التحميل التلقائي، مجموعة الاختبار للتحميل التلقائي، وبالطبع تطبيق التحميل التلقائي.
+
وحدة تحكم تتحمل تلقائيًّا، ومجموعة الاختبار تتحمل تلقائيًّا، وبالطبع التطبيق يتحمل تلقائيًّا أيضًا.
  
بشكل افتراضي، يقوم Rails حريصة بتحميل ملفات التطبيق عند تشغيله في وضع الإنتاج، لذلك لا يحدث معظم التحميل التلقائي في التطوير. ولكن قد لا يزال يثار التحميل التلقائي أثناء تحميل حريصة.
+
بشكل افتراضي، تحمل ريلز بشكل حثيث (eager loads) ملفات التطبيق عند تشغيله في وضع الإنتاج، لذلك لا يحدث معظم التحميل التلقائي هنا الذي يجري في التطوير. ولكن قد لا يزال يُطلَق التحميل التلقائي أثناء التحميل الحثيث.
  
على سبيل المثال ، معطى
+
على سبيل المثال، إعطاء:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
class BeachHouse < House
 
class BeachHouse < House
 
 
End
 
End
 
</syntaxhighlight>
 
</syntaxhighlight>
  
إذا كان House لا يزال غير معروف عندما app/models/beach_house.rb يكون حريص لتحميلها، Rails تحملها تلقائياً.
+
إذا كان <code>House</code> لا يزال غير معروف عندما يُحمَّل app/models/beach_house.rb بشكل حثيث، تحمله تلقائيًا.
  
== autoload_paths و eager_load_paths ==
+
== <code>autoload_paths</code> و <code>eager_load_paths</code> ==
كما تعلم، عندما يتطلب الأمر الحصول على اسم ملف نسبي:
+
كما تعلم، عندما تُعطَى <code>require</code> اسم ملف نسبي:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
require 'erb'
 
require 'erb'
 
</syntaxhighlight>
 
</syntaxhighlight>
  
يبحث Ruby عن الملف في قوائم الدلائل في $ LOAD_PATH. هذا هو، يتكرر Ruby على كافة الدلائل الخاصة به ويتحقق من كل واحد منها من وجود ملف يسمى "erb.rb" أو "erb.so" أو "erb.o" أو "erb.dll". إذا وجد أي منها، يحملها المترجم وينهي عملية البحث. وإلا، فإنه يحاول مرة أخرى في الدليل التالي من القائمة. إذا استنفدت القائمة، فسيرفع LoadError.
+
تبحث [[Ruby|روبي]] عن الملف في المجلدات المدرجة في ‎<code>$LOAD_PATH</code>. هذا هو، تكرر روبي على كافة المجلدات الخاصة به وتتحقق من كل واحد منها من وجود ملف يسمى "erb.rb" أو "erb.so" أو "erb.o" أو "erb.dll". إذا وجدت أي منها، يحملها المترجم وينهي عملية البحث. وإلا، فإنه يحاول مرة أخرى في المجلد التالي من القائمة. إذا نفدت القائمة، فسيُمرمَى الاستثناء <code>[[Ruby/LoadError|LoadError]]</code>.
 
 
سنغطي كيفية عمل التحميل التلقائي للثابت بمزيد من التفاصيل في وقت لاحق،
 
  
لكن الفكرة هي أنه عندما يكون ثابتًا مثل Post مكتوبًا ومفتقدًا، إذا كان هناك ملف post.rb على سبيل المثال في app/models، سيجده Rails، ويقيمه، ويحدد Post على أنه أثر جانبي.
+
سنغطي كيفية عمل التحميل التلقائي للثابت بمزيد من التفاصيل في وقت لاحق، لكن الفكرة هي أنه عندما يكون ثابتًا مثل <code>Post</code> مكتوبًا ومفقودًا، فإذا كان هناك ملفًا باسم post.rb على سبيل المثال في app/models، فسيجده ريلز، ويقيمه، ويحدد <code>Post</code> على أنه تأثير جانبي.
  
حسنًا ، لدى Rails مجموعة من الأدلة المشابهة لـ $ LOAD_PATH للبحث عنها post.rb. يطلق على هذه المجموعة اسم autoload_paths وتتضمن افتراضيًا ما يلي:
+
حسنًا، لدى ريلز مجموعة من المجلدات المشابهة لـ ‎<code>$LOAD_PATH</code> للبحث فيها عن post.rb. يطلق على هذه المجموعة الاسم <code>autoload_paths</code> وتتضمن افتراضيًا ما يلي:
* جميع الأدلة الفرعية للــ app في التطبيق والمحركات موجودة في وقت التمهيد. على سبيل المثال، app/controllers. لا تحتاج إلى أن تكون الافتراضية، فكل الدلائل المخصصة مثل app/workers تنتمي تلقائيًا إلى autoload_paths.
+
* جميع المجلدات الفرعية للــ app في التطبيق والمحركات الموجودة في وقت الإقلاع. على سبيل المثال، app/controllers. لا تحتاج إلى أن تكون الافتراضية، فكل المجلدات المخصصة مثل app/workers تنتمي تلقائيًا إلى <code>autoload_paths</code>.
* أي دلائل موجودة ذات المستوى الثاني تسمى app/*/concerns  في التطبيق والمحركات.
+
* أي مجلدات موجودة ذات مستوى ثان تسمى app/*/concerns  في التطبيق والمحركات.
* الدليل test/mailers/previews.
+
* المجلد test/mailers/previews.
eager_load_paths هو في البداية مسارات التطبيق أعلاه.
+
إن <code>eager_load_paths</code> هو في البداية مسارات التطبيق (app) أعلاه.
  
تعتمد كيفية التحميل التلقائي للملفات على إعدادات تهيئة eager_load و cache_classes التي تختلف عادةً في أوضاع التطوير والإنتاج والاختبار:
+
تعتمد كيفية التحميل التلقائي للملفات على إعدادات تهيئة <code>eager_load</code> و <code>cache_classes</code> التي تختلف عادةً في أوضاع التطوير والإنتاج والاختبار:
* في التطوير، تريد بدء تشغيل أسرع مع تحميل متزايد من رمز التطبيق. لذا، يجب تعيين eager_load على "false" ، وسيثبت rails تلقائيًا الملفات حسب الحاجة (انظر Autoloading Algorithms أدناه) - ثم إعادة تحميلها عند تغييرها (راجع إعادة التحميل المستمر أدناه).
+
* في التطوير، تريد بدء التشغيل بسرعة مع تحميل تدريجي لشيفرة التطبيق. لذا، يجب يجب تعطيل التحميل الحثيث عبر تعيين <code>eager_load</code> إلى القيمة "false"، وسيحمل ريلز تلقائيًا الملفات حسب الحاجة (انظر قسم [[Rails/autoloading and reloading constants#.D8.AE.D9.88.D8.A7.D8.B1.D8.B2.D9.85.D9.8A.D8.A7.D8.AA .D8.A7.D9.84.D8.AA.D8.AD.D9.85.D9.8A.D9.84 .D8.A7.D9.84.D8.AA.D9.84.D9.82.D8.A7.D8.A6.D9.8A|خوارزميات التحميل التلقائي]] أدناه) ثم يعيد تحميلها عند تغييرها (راجع قسم [[Rails/autoloading and reloading constants#.D8.A5.D8.B9.D8.A7.D8.AF.D8.A9 .D8.A7.D9.84.D8.AA.D8.AD.D9.85.D9.8A.D9.84 .D9.84.D9.84.D8.AB.D9.88.D8.A7.D8.A8.D8.AA|إعادة التحميل للثوابت]] أدناه).
* في الإنتاج، ومع ذلك تريد التناسق وسلامة الصفحات ويمكن أن تعيش مع وقت تمهيد أطول.  لذا، يتعين eager_load على true، وبعد ذلك أثناء التمهيد (قبل أن يكون التطبيق جاهزًا لاستقبال الطلبات) تحمل rails كافة الملفات الموجودة في eager_load_paths ثم إيقاف التحميل التلقائي (NB: قد تكون هناك حاجة إلى التحميل التلقائي أثناء التحميل السريع). لا يعد التحميل التلقائي بعد التشغيل أمرًا جيدًا، نظرًا لأن التحميل التلقائي يمكن أن يتسبب في حدوث مشكلات في سلامة الصفحات.
+
* في الإنتاج، ومع ذلك تريد التناسق وسلامة الصفحات ويمكن قبول وقت أطول للإقلاع. لذا، يتعين <code>eager_load</code> إلى القيمة <code>true</code>، وبعد ذلك أثناء الإقلاع (قبل أن يكون التطبيق جاهزًا لاستقبال الطلبات) تحمل ريلز كافة الملفات الموجودة في <code>eager_load_paths</code> ثم توقف التحميل التلقائي (ملحوظة: قد تكون هناك حاجة إلى التحميل التلقائي أثناء التحميل الحثيث). لا يعد التحميل التلقائي بعد التشغيل أمرًا جيدًا، نظرًا لأن التحميل التلقائي يمكن أن يتسبب في حدوث مشكلات في سلامة الصفحات.
* في الاختبار، لسرعة التنفيذ (من الاختبارات الفردية) eager_load يكون false، لذلك rails يتبع سلوك التطوير.
+
* في الاختبار، لسرعة التنفيذ (من الاختبارات الفردية) <code>eager_load</code> يكون <code>false</code>، لذلك يتبع ريلز سلوك التطوير نفسه.
ما هو موضح أعلاه هي الإعدادات الافتراضية مع تطبيق rails أنشأ حديثًا. هناك طرق متعددة يمكن تهيئتها بشكل مختلف (راجع تكوين تطبيقات Rails). ولكن استخدام autoload_paths من تلقاء نفسها في الماضي (المطورين قبل الإصدار 5) قد يهيئ autoload_paths للإضافة في مواقع إضافية (على سبيل المثال lib الذي اعتاد أن يكون قائمة مسار autoload منذ سنوات ، ولكن لم يعد). ومع ذلك، فإن هذا غير محبط الآن لمعظم الأغراض، حيث من المحتمل أن يؤدي إلى أخطاء في الإنتاج فقط. من الممكن إضافة مواقع جديدة إلى كل من config.eager_load_paths و config.autoload_paths ولكن استخدامها على مسؤوليتك الخاصة.
+
ما هو موضح أعلاه هي الإعدادات الافتراضية مع تطبيق ريلز أنشأ حديثًا. هناك طرق متعددة يمكن تهيئتها بشكل مختلف (راجع توثيق [[Rails/configuring|ضبط تطبيقات ريلز]]). ولكن استخدام <code>autoload_paths</code> من تلقاء نفسها في الماضي (قبل الإصدار 5) قد يضبط المطورون <code>autoload_paths</code> للإضافة مواقع إضافية (على سبيل المثال <code>lib</code> الذي اعتاد أن يكون قائمة مسار <code>autoload</code> منذ سنوات، ولكن لم يعد كذلك). ومع ذلك، فإن هذا غير محبط الآن لمعظم الأغراض، حيث من المحتمل أن يؤدي إلى أخطاء في الإنتاج فقط. من الممكن إضافة مواقع جديدة إلى كل من <code>config.eager_load_paths</code> و <code>config.autoload_paths</code> ولكن استخدم ذلك على مسؤوليتك الخاصة.
  
انظر أيضًا (Autoloading في بيئة الاختبار).
+
انظر أيضًا قسم [[Rails/autoloading and reloading constants#.D8.A7.D9.84.D8.AA.D8.AD.D9.85.D9.8A.D9.84 .D8.A7.D9.84.D8.AA.D9.84.D9.82.D8.A7.D8.A6.D9.8A .D9.81.D9.8A .D8.A8.D9.8A.D8.A6.D8.A9 .D8.A7.D9.84.D8.A7.D8.AE.D8.AA.D8.A8.D8.A7.D8.B1|التحميل التلقائي في بيئة الاختبار]].
  
config.autoload_paths غير قابلة للتغيير من ملفات التكوين الخاصة بالبيئة.
+
الضبط <code>config.autoload_paths</code> غير قابل للتغيير من ملفات الضبط الخاصة بالبيئة.
  
يمكن فحص قيمة autoload_paths. في تطبيق أنشأ للتو،  ويحرر:
+
يمكن فحص قيمة <code>autoload_paths</code>. في تطبيق أنشأ للتو:
<syntaxhighlight lang="rails">
+
<syntaxhighlight lang="shell">
 
$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
 
$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
 
 
.../app/assets
 
.../app/assets
 
 
.../app/channels
 
.../app/channels
 
 
.../app/controllers
 
.../app/controllers
 
 
.../app/controllers/concerns
 
.../app/controllers/concerns
 
 
.../app/helpers
 
.../app/helpers
 
 
.../app/jobs
 
.../app/jobs
 
 
.../app/mailers
 
.../app/mailers
 
 
.../app/models
 
.../app/models
 
 
.../app/models/concerns
 
.../app/models/concerns
 
 
.../activestorage/app/assets
 
.../activestorage/app/assets
 
 
.../activestorage/app/controllers
 
.../activestorage/app/controllers
 
 
.../activestorage/app/javascript
 
.../activestorage/app/javascript
 
 
.../activestorage/app/jobs
 
.../activestorage/app/jobs
 
 
.../activestorage/app/models
 
.../activestorage/app/models
 
 
.../actioncable/app/assets
 
.../actioncable/app/assets
 
 
.../actionview/app/assets
 
.../actionview/app/assets
 
 
.../test/mailers/previews
 
.../test/mailers/previews
 
</syntaxhighlight>
 
</syntaxhighlight>
  
ملاحظة: يحسب autoload_paths ويخزن مؤقتًا أثناء عملية التهيئة. يجب إعادة تشغيل التطبيق لإظهار أي تغييرات في بنية الدليل.
+
'''تنبيه''': يحسب <code>autoload_paths</code> ويخزن مؤقتًا أثناء عملية التهيئة. يجب إعادة تشغيل التطبيق لتطبيق أي تغييرات في بنية المجلد.
  
 
== خوارزميات التحميل التلقائي ==
 
== خوارزميات التحميل التلقائي ==
سطر 365: سطر 329:
  
 
==== الثوابت بعد الكلمات الأساسية class و module ====
 
==== الثوابت بعد الكلمات الأساسية class و module ====
ينفّذ روبي بحثًا عن الثابت الذي يتبع الكلمة الأساسية class او module لأنه يحتاج إلى معرفة ما إذا كان سينشأ الفئة أو الوحدة النمطية أو سيُعيد فتحها.
+
ينفّذ روبي بحثًا عن الثابت الذي يتبع الكلمة المفتاحية class او module لأنه يحتاج إلى معرفة ما إذا كان سينشأ الصنف أو الوحدة النمطية أو سيُعيد فتحها.
  
 
إذا لم يُعرف الثابت عند هذه النقطة لا يعتبر ثابت مفقود، لا يُثار التحميل التلقائي.
 
إذا لم يُعرف الثابت عند هذه النقطة لا يعتبر ثابت مفقود، لا يُثار التحميل التلقائي.
سطر 383: سطر 347:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
ملاحظة: لا يتطلب Rails قيمة الثوابت التي تُحمل تلقائيًا لتكون فئة أو كائن وحدة نمطية. على سبيل المثال، إذا كان تطبيق الملف / models / max_clients.rb يعرّف MAX_CLIENTS = 100 autoloading MAX_CLIENTS يعمل بشكل جيد.
+
ملاحظة: لا يتطلب Rails قيمة الثوابت التي تُحمل تلقائيًا لتكون صنف أو كائن وحدة نمطية. على سبيل المثال، إذا كان تطبيق الملف / models / max_clients.rb يعرّف MAX_CLIENTS = 100 autoloading MAX_CLIENTS يعمل بشكل جيد.
  
 
==== مجالات الأسماء ====
 
==== مجالات الأسماء ====
سطر 452: سطر 416:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
إذا فشلت كل هذه المحاولات، فسيبدأ Rails عملية البحث مرة أخرى في مساحة الاسم الأصل. في هذه الحالة، يبقى المستوى الأعلى فقط:
+
إذا فشلت كل هذه المحاولات، فسيبدأ Rails عملية البحث مرة أخرى في مجال الاسم الأصل. في هذه الحالة، يبقى المستوى الأعلى فقط:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
app/assets/post.rb
 
app/assets/post.rb
سطر 487: سطر 451:
 
إذا كان هناك User رفيع المستوى روبي سوف يحلها في المثال السابق، لكن لن يكون في الأخير. بشكل عام، لا تحاكي Rails خوارزميات روبي لإحلال الثوابت، ولكنها في هذه الحالة تحاول استخدام الكشف عن الأشياء التالية:
 
إذا كان هناك User رفيع المستوى روبي سوف يحلها في المثال السابق، لكن لن يكون في الأخير. بشكل عام، لا تحاكي Rails خوارزميات روبي لإحلال الثوابت، ولكنها في هذه الحالة تحاول استخدام الكشف عن الأشياء التالية:
  
" إذا كان أي من مجالات الأسماء الأصل للفئة أو الوحدة النمطية يحتوي على ثابت مفقود، يفترض Rails أن المرجع هو نسبي. خلاف ذلك مؤهل. "
+
" إذا كان أي من مجالات الأسماء الأصل للصنف أو الوحدة النمطية يحتوي على ثابت مفقود، يفترض Rails أن المرجع هو نسبي. خلاف ذلك مؤهل. "
  
 
على سبيل المثال، إذا كان هذا الرمز يشغل التحميل التلقائي:
 
على سبيل المثال، إذا كان هذا الرمز يشغل التحميل التلقائي:
سطر 512: سطر 476:
  
 
=== الوحدات التلقائية ===
 
=== الوحدات التلقائية ===
عندما تعمل وحدة نمطية مثل مساحة اسم، لا يتطلب Rails التطبيق لتعريف ملف له، الدليل المتطابق لمساحة الاسم كافٍ.
+
عندما تعمل وحدة نمطية مثل مجال اسم، لا يتطلب Rails التطبيق لتعريف ملف له، الدليل المتطابق لمجال الاسم كافٍ.
  
 
لنفترض أن التطبيق يحتوي على مكتب خلفي يُخزن وحدات التحكم فيه في app/controllers/admin.
 
لنفترض أن التطبيق يحتوي على مكتب خلفي يُخزن وحدات التحكم فيه في app/controllers/admin.
سطر 521: سطر 485:
  
 
=== الإجراء العام ===
 
=== الإجراء العام ===
تم ذكر فقدان المراجع النسبية في cref حيث سُجلت، وتم ذكر فقدان المراجع المؤهلة في الأصل (راجع خوارزمية الإحلال للثوابت النسبية في بداية هذا الدليل لتعريف cref، و خوارزمية الإحلال للثوابت المؤهلة لتعريف الأصل).
+
تم ذكر فقدان المراجع النسبية في cref حيث سُجلت، وتم ذكر فقدان المراجع المؤهلة في الأصل (راجع خوارزمية الاستبيان للثوابت النسبية في بداية هذا الدليل لتعريف cref، و خوارزمية الاستبيان للثوابت المؤهلة لتعريف الأصل).
  
 
يكون الإجراء للتحميل التلقائي للثابت  C في الموقف التعسفي كما يلي:
 
يكون الإجراء للتحميل التلقائي للثابت  C في الموقف التعسفي كما يلي:
سطر 628: سطر 592:
 
عندما يكون config.cache_classes false، فإن Rails قادرة على إعادة تحميل الثوابت ذاتية التحميل.
 
عندما يكون config.cache_classes false، فإن Rails قادرة على إعادة تحميل الثوابت ذاتية التحميل.
  
على سبيل المثال، إذا كنت في جلسة عمل وحدة التحكم وقمت بتحرير بعض الملفات من وراء الكواليس، فيمكن إعادة تحميل الكود مع reload! أمر:
+
على سبيل المثال، إذا كنت في جلسة عمل وحدة التحكم وقمت بتحرير بعض الملفات من وراء الكواليس، فيمكن إعادة تحميل الشيفرة مع reload! أمر:
  
 
> reload!
 
> reload!
سطر 639: سطر 603:
 
إذا تغير أي شيء هناك، فهناك برنامج وسيط يكتشفها ويعيد تحميل الشفرة.
 
إذا تغير أي شيء هناك، فهناك برنامج وسيط يكتشفها ويعيد تحميل الشفرة.
  
Autoloading بتتبع التحميل التلقائي الثوابت. تُنفذ إعادة التحميل عن طريق إزالتها جميعًا من الفئات والوحدات الخاصة بها باستخدام Module # remove_const. بهذه الطريقة، عندما يُشغل الكود، ستكون هذه الثوابت غير معروفة مرة أخرى، ويُعاد تحميل الملفات عند الطلب.
+
Autoloading بتتبع التحميل التلقائي الثوابت. تُنفذ إعادة التحميل عن طريق إزالتها جميعًا من الأصناف والوحدات الخاصة بها باستخدام Module # remove_const. بهذه الطريقة، عندما يُشغل الشيفرة، ستكون هذه الثوابت غير معروفة مرة أخرى، ويُعاد تحميل الملفات عند الطلب.
  
 
هذه عملية كل شيء أو لا شيء، Rails لا يحاول إعادة تحميل سوى ما تغيرت لأن التبعيات بين الطبقات يجعل ذلك صعب حقا. بدلا من ذلك، يُمسح كل شيء.
 
هذه عملية كل شيء أو لا شيء، Rails لا يحاول إعادة تحميل سوى ما تغيرت لأن التبعيات بين الطبقات يجعل ذلك صعب حقا. بدلا من ذلك، يُمسح كل شيء.
سطر 690: سطر 654:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
لحل User روبي يتحقق من Admin في الحالة السابقة، لكنه لا يوجد في الأخير لأنه لا ينتمي إلى التداخل (راجع التشعب و خوارزميات الإحلال).
+
لحل User روبي يتحقق من Admin في الحالة السابقة، لكنه لا يوجد في الأخير لأنه لا ينتمي إلى التداخل (راجع التشعب و خوارزميات الاستبيان).
  
 
لسوء الحظ، لا تعرف التحميل التلقائي ل Rails التشعب في المكان الذي كان فيه الثابت مفقودًا، وبالتالي لا يكون قادرًا على التصرف كما يفعل روبي. على وجه الخصوص، سيحصل على التحميل التلقائي Admin::User في كلتا الحالتين.
 
لسوء الحظ، لا تعرف التحميل التلقائي ل Rails التشعب في المكان الذي كان فيه الثابت مفقودًا، وبالتالي لا يكون قادرًا على التصرف كما يفعل روبي. على وجه الخصوص، سيحصل على التحميل التلقائي Admin::User في كلتا الحالتين.
سطر 712: سطر 676:
  
 
=== التحميل التلقائي و STI ===
 
=== التحميل التلقائي و STI ===
وراثة جدول فردي (STI) هي إحدى ميزات السجل النشط التي تمكّن من تخزين تسلسل هرمي للنماذج في جدول واحد. واجهة برمجة التطبيقات (API) لهذه النماذج تدرك التسلسل الهرمي وتلخص بعض الاحتياجات العامة. على سبيل المثال ، بالنظر إلى هذه الفئات:
+
وراثة جدول فردي (STI) هي إحدى ميزات السجل النشط التي تمكّن من تخزين تسلسل هرمي للنماذج في جدول واحد. واجهة برمجة التطبيقات (API) لهذه النماذج تدرك التسلسل الهرمي وتلخص بعض الاحتياجات العامة. على سبيل المثال ، بالنظر إلى هذه الأصناف:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
<nowiki>#</nowiki> app/models/polygon.rb
 
<nowiki>#</nowiki> app/models/polygon.rb
سطر 739: سطر 703:
 
والتحميل التلقائي أنواع حسب الحاجة. على سبيل المثال، إذا كان Polygon.first مستطيلاً ولم يُحمل Rectangle بعد، السجل النشط يُحمله تلقائيًا وينشئ السجل بشكل صحيح.
 
والتحميل التلقائي أنواع حسب الحاجة. على سبيل المثال، إذا كان Polygon.first مستطيلاً ولم يُحمل Rectangle بعد، السجل النشط يُحمله تلقائيًا وينشئ السجل بشكل صحيح.
  
كل شيء جيد، ولكن إذا أردنا العمل على بعض الفئات الفرعية، بدلاً من تنفيذ استعلامات تستند إلى فئة الجذر، فستصبح الأشياء مثيرة للاهتمام.
+
كل شيء جيد، ولكن إذا أردنا العمل على بعض الأصناف الفرعية، بدلاً من تنفيذ استعلامات تستند إلى صنف الجذر، فستصبح الأشياء مثيرة للاهتمام.
  
أثناء العمل مع Polygon، لا تحتاج إلى أن تكون على دراية بجميع أحفادها، لأن أي شيء في الجدول يكون مضلعًا بحكم التعريف، ولكن عند العمل مع الفئات الفرعية يجب أن يكون السجل النشط قادراً على تعداد الأنواع التي يبحث عنها. دعونا نرى مثال.
+
أثناء العمل مع Polygon، لا تحتاج إلى أن تكون على دراية بجميع أحفادها، لأن أي شيء في الجدول يكون مضلعًا بحكم التعريف، ولكن عند العمل مع الأصناف الفرعية يجب أن يكون السجل النشط قادراً على تعداد الأنواع التي يبحث عنها. دعونا نرى مثال.
  
 
يُحمل Rectangle.all المستطيلات فقط عن طريق إضافة قيد نوع إلى الاستعلام:
 
يُحمل Rectangle.all المستطيلات فقط عن طريق إضافة قيد نوع إلى الاستعلام:
سطر 750: سطر 714:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
دعونا نقدم الآن فئة فرعية من Rectangle:
+
دعونا نقدم الآن صنف فرعية من Rectangle:
  
 
<nowiki>#</nowiki> app/models/square.rb
 
<nowiki>#</nowiki> app/models/square.rb
سطر 764: سطر 728:
 
WHERE "polygons"."type" IN ("Rectangle", "Square")
 
WHERE "polygons"."type" IN ("Rectangle", "Square")
  
ولكن هناك تحذير هنا: كيف يعرف السجل النشط أن فئة Square موجودة على الإطلاق؟
+
ولكن هناك تحذير هنا: كيف يعرف السجل النشط أن صنف Square موجودة على الإطلاق؟
  
حتى إذا كان ملف app/models/square.rb موجودًا ويحدد فئة Square، إذا لم يستخدم أي رمز بعد ذلك، فإن Rectangle.all يصدر الاستعلام.
+
حتى إذا كان ملف app/models/square.rb موجودًا ويحدد صنف Square، إذا لم يستخدم أي رمز بعد ذلك، فإن Rectangle.all يصدر الاستعلام.
  
 
SELECT "polygons".* FROM "polygons"
 
SELECT "polygons".* FROM "polygons"
سطر 774: سطر 738:
 
هذا ليس خطأ، ويشمل الاستعلام جميع الأحفاد المعروفة من Rectangle.
 
هذا ليس خطأ، ويشمل الاستعلام جميع الأحفاد المعروفة من Rectangle.
  
طريقة للتأكد من أن هذا يعمل بشكل صحيح بغض النظر عن ترتيب التنفيذ هو يدوياً تحميل الفئات الفرعية المباشرة في أسفل الملف الذي يعرّف كل فئة متوسطة:
+
طريقة للتأكد من أن هذا يعمل بشكل صحيح بغض النظر عن ترتيب التنفيذ هو يدوياً تحميل الأصناف الفرعية المباشرة في أسفل الملف الذي يعرّف كل صنف متوسطة:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
<nowiki>#</nowiki> app/models/rectangle.rb
 
<nowiki>#</nowiki> app/models/rectangle.rb
سطر 785: سطر 749:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
يجب أن يحدث هذا لكل فئة وسيطة (غير جذر وغير ورقة). لا يقوم نطاق الجذر بتوسيع نطاق البحث حسب النوع، وبالتالي لا يلزم بالضرورة معرفة كل أحفاده.
+
يجب أن يحدث هذا لكل صنف وسيطة (غير جذر وغير ورقة). لا يقوم نطاق الجذر بتوسيع نطاق البحث حسب النوع، وبالتالي لا يلزم بالضرورة معرفة كل أحفاده.
  
 
=== التحميل التلقائي والطلب ===
 
=== التحميل التلقائي والطلب ===
سطر 823: سطر 787:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
قد يكون الغرض من هذا الإعداد أن يستخدم التطبيق الفئة التي تتوافق مع البيئة عبر AUTH_SERVICE. في وضع التطوير يحصل على التحميل التلقائي MockedAuthService عند تشغيل المُهيئ. لنفترض أننا نقوم ببعض الطلبات، ونغير تنفيذها، ونضغط على التطبيق مرة أخرى. لدهشتنا لا تنعكس التغييرات. لماذا ا؟
+
قد يكون الغرض من هذا الإعداد أن يستخدم التطبيق الصنف التي تتوافق مع البيئة عبر AUTH_SERVICE. في وضع التطوير يحصل على التحميل التلقائي MockedAuthService عند تشغيل المُهيئ. لنفترض أننا نقوم ببعض الطلبات، ونغير تنفيذها، ونضغط على التطبيق مرة أخرى. لدهشتنا لا تنعكس التغييرات. لماذا ا؟
  
كما رأينا سابقاً، يزيل Rails التحميل التلقائي للثوابت، ولكن يخزن AUTH_SERVICE كائن الفئة الأصلي. تالفة، غير قابلة للوصول باستخدام الثابت الأصلي  ولكن وظيفية تماما.
+
كما رأينا سابقاً، يزيل Rails التحميل التلقائي للثوابت، ولكن يخزن AUTH_SERVICE كائن الصنف الأصلي. تالفة، غير قابلة للوصول باستخدام الثابت الأصلي  ولكن وظيفية تماما.
  
يلخص الكود التالي الحالة:
+
يلخص الشيفرة التالي الحالة:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
class C
 
class C
سطر 1٬015: سطر 979:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
يمكن ملاحظة هذا الإحلال الثابت المذهل مع أي فئة مؤهلة:
+
يمكن ملاحظة هذا الاستبيان الثابت المذهل مع أي صنف مؤهلة:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
2.1.5 :001 > String::Array
 
2.1.5 :001 > String::Array
سطر 1٬024: سطر 988:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
ملاحظة: للعثور على هذا gotcha  يجب أن تكون مساحة الاسم المؤهلة فئة، الكائن ليس سلف من الوحدات.
+
ملاحظة: للعثور على هذا gotcha  يجب أن تكون مجال الاسم المؤهلة صنف، الكائن ليس سلف من الوحدات.
  
=== التحميل التلقائي داخل فئات Singleton ===
+
=== التحميل التلقائي داخل أصناف Singleton ===
لنفترض أن لدينا تعريفات الفئة هذه:
+
لنفترض أن لدينا تعريفات الصنف هذه:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
<nowiki>#</nowiki> app/models/hotel/services.rb
 
<nowiki>#</nowiki> app/models/hotel/services.rb
سطر 1٬056: سطر 1٬020:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
إذا كان Hotel::Services معروفة في وقت تحميل app/models/hotel/geo_location.rb، تُحل الـ Services بواسطة Ruby لأن الفندق ينتمي إلى التشعب عندما تُفتح فئة singleton من Hotel :: GeoLocation.
+
إذا كان Hotel::Services معروفة في وقت تحميل app/models/hotel/geo_location.rb، تُحل الـ Services بواسطة Ruby لأن الفندق ينتمي إلى التشعب عندما تُفتح صنف singleton من Hotel :: GeoLocation.
  
 
ولكن إذا كان Hotel::Services غير معروفة، تكون Rails غير قادرة على التحميل التلقائي، ويرفع التطبيق NameError.
 
ولكن إذا كان Hotel::Services غير معروفة، تكون Rails غير قادرة على التحميل التلقائي، ويرفع التطبيق NameError.
  
السبب في ذلك هو أن تشغيل التحميل التلقائي لفئة singleton، هو مجهول، وكما رأينا من قبل، Rails فقط تتحقق من مساحة الاسم الأعلى في حالة الحافة.
+
السبب في ذلك هو أن تشغيل التحميل التلقائي لصنف singleton، هو مجهول، وكما رأينا من قبل، Rails فقط تتحقق من مجال الاسم الأعلى في حالة الحافة.
  
 
الحل السهل لهذا التحذير هو تأهيل الثابت:
 
الحل السهل لهذا التحذير هو تأهيل الثابت:
سطر 1٬101: سطر 1٬065:
 
End
 
End
  
نظرًا لأن Rails يتحقق من مساحة الاسم ذات المستوى الأعلى، يحصل User على التحميل التلقائي بشكل جيد في المرة الأولى التي يستدعى فيها التابع User. تحصل على الاستثناء فقط إذا كان الثابت User معروفًا عند هذه النقطة، خاصة في ثاني استدعاء للـ Use:
+
نظرًا لأن Rails يتحقق من مجال الاسم ذات المستوى الأعلى، يحصل User على التحميل التلقائي بشكل جيد في المرة الأولى التي يستدعى فيها التابع User. تحصل على الاستثناء فقط إذا كان الثابت User معروفًا عند هذه النقطة، خاصة في ثاني استدعاء للـ Use:
 
<syntaxhighlight lang="rails">
 
<syntaxhighlight lang="rails">
 
c = C.new
 
c = C.new
سطر 1٬110: سطر 1٬074:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
لأنه يكتشف أن مساحة الاسم الأصل تحتوي على الثابت (انظر مراجع مؤهلة).
+
لأنه يكتشف أن مجال الاسم الأصل تحتوي على الثابت (انظر مراجع مؤهلة).
  
 
كما هو الحال مع روبي الصافي، داخل جسم السلف المباشر من BasicObject، استخدم دائمًا المسارات الثابتة المطلقة:
 
كما هو الحال مع روبي الصافي، داخل جسم السلف المباشر من BasicObject، استخدم دائمًا المسارات الثابتة المطلقة:

مراجعة 10:08، 12 مارس 2019

يوثق هذا الدليل طريقة عمل التحميل التلقائي وإعادة تحميل الثوابت. بعد قراءة هذا الدليل، ستتعلم:

  • الجوانب الرئيسية لثوابت لغة روبي.
  • ماهية autoload_paths وكيفية عمل التحميل الحثيث (eager loading) في الإنتاج.
  • كيفية عمل التحميل التلقائي للثابت.
  • ماهية need_dependency.
  • كيفية عمل إعادة التحميل للثابت.
  • حلول للتحميل التلقائي المشترك.

المقدمة

تسمح لغة ريلز بكتابة تطبيقات كما لو حملت الشيفرة الخاص بها مسبقًا.

في أصناف برنامج روبي عادي، تحتاج إلى تحميل جميع اعتماديته (dependencies):

require 'application_controller'
require 'post'
 
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

سرعان ما ترى غريزتنا الروبية بعض التكرار هنا: إذا عُرِّفَت الأصناف في ملفات مطابقة لاسمها، ألا يمكن أن يتم التحميل التلقائي بطريقة ما؟ يمكن حفظ مسح الملف (scanning the file) من أجل الاعتماديات، وهو أمر غير مُجدٍ.

علاوة على ذلك، يحمل Kernel.require الملفات مرة واحدة، ولكن التطوير يكون أكثر سلاسة إذا تحدثت الشيفرة عند تغييرها دون إعادة تشغيل الخادم. سيكون من الجيد أن تكون قادرًا على استخدام Kernel.load في التطوير، و Kernel.require في الإنتاج.

في الواقع، تتوفر هذه الميزات بواسطة ريلز، إذ يكفي أن نكتب فقط:

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

يوثق هذا الدليل كيفية عمل ذلك.

إنه خارج نطاق هذا الدليل توثيق ثوابت لغة روبي، لكننا مع ذلك سنسلط الضوء على بعض المواضيع الرئيسية. حقا استيعاب المقاطع التالية له دور فعال لفهم التحميل التلقائي وإعادة التحميل للثوابت.

منشط الثوابت

بينما الثوابت ليست ذات أهمية في معظم لغات البرمجة، إلا أنها موضوع ثري في لغة روبي.

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

التشعب

يمكن تشعيب تعريفات صنف ووحدة لإنشاء مجالات أسماء:

module XML
  class SAXParser
    # (1)
  end
end

إن التشعب (nesting) في أي مكان معطى هو مجموعة الأصناف المتداخلة المضمنة (enclosing nested class) وكائنات الوحدة الخارجية. يمكن فحص التشعب في أي مكان مع Module.nesting. على سبيل المثال، في المثال السابق، يكون التداخل في (1) هو:

[XML::SAXParser, XML]

من المهم أن نفهم أن التشعب يتكون من كائنات الصنف والوحدة، ولا علاقة له بالثوابت المستخدمة في الوصول إليهم، كما أنه لا علاقة له بأسمائها.

على سبيل المثال، في حين أن هذا التعريف هو مماثل لهذا التعريف السابق:

class XML::SAXParser
  # (2)
end

التشعب في (2) مختلف:

[XML::SAXParser]

لا تنتمي XML إليها.

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

أكثر من ذلك، فهي مستقلة تمامًا. خذ على سبيل المثال:

module X
  module Y
  end
end
 
module A
  module B
  end
end
 
module X::Y
  module A::B
    # (3)
  end
end

يتكون التشعب في (3) من كائنات وحدتين:

[A::B, X::Y]

لذلك، لا تنتهي فقط في A، والتي لا تنتمي حتى إلى التشعب، ولكنها تحتوي أيضًا على X::Y، وهي مستقلة عن A::B.

التشعب عبارة عن حزمة داخلية يحتفظ بها المترجم، وتُعدل وفقًا للقواعد التالية:

  • كائن الصنف الذي يتبع الكلمة المفتاحية class يُضَاف عندما ينفذ جسمه، وينتشر (pop) بعد ذلك.
  • كائن الوحدة الذي يتبع الكلمة المفتاحية module يُضَاف عندما ينفذ جسمه، وينشر بعد ذلك.
  • الصنف المنفرد (singleton class) الذي يُفتتَح مع class << object يضاف، ثم ينتشر لاحقًا.
  • عندما يستدعى instance_eval مع سلسلة نصية تمثِّل الوسيط، يُضَاف الصنف المفرد للمتلقي إلى تداخل الشيفرة المقيمة. عندما يستدعى class_eval أو module_eval باستخدام سلسلة وسيطة، يضاف المستقبل إلى تداخل الشيفرة المقيمة.
  • التداخل في المستوى الأعلى من الشيفرة التي فسرت بواسطة Kernel.load يُعدُّ فارغًا، إلا إذا تلقى استدعاء load قيمة صحيحة كوسيط ثانية، وفي هذه الحالة تضاف وحدة مجهولة أُنشئت حديثًا بواسطة روبي.

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

تعريفات الصنف والوحدة هي مهام ثابتة

لنفترض أن الشيفرة البسيطة التالية تنشئ صنفًا (بدلًا من إعادة فتحه):

class C
End

ينشئ روبي الثابت C في Object ويخزن في هذا الثابت على أنه كائن صنف. اسم نسخة الصنف هي "C"، سلسلة نصية، سميت بعد الثابت.

إليك أيضًا الشيفرة التالية:

class Project < ApplicationRecord
End

ينفذ مهمة ثابتة تعادل:

Project = Class.new(ApplicationRecord)

بما في ذلك تحديد اسم الصنف على أنَّه تأثير جانبي:

Project.name # => "Project"

المهمة الثابتة لها قاعدة خاصة لتحقيق ذلك: إذا كان الكائن الذي تعين هو صنف أو وحدة غير معروفة، تعين روبي اسم الكائن إلى اسم الثابت.

تنبيه: من الآن فصاعدًا، ما يحدث للثابت والنسخة لا يهم. على سبيل المثال، يمكن حذف الثابت، أو يمكن تعيين كائن الصنف إلى ثابت مختلف، أو يمكن تخزينه بدون ثابت بعد الآن، ...إلخ. بمجرد تعيين الاسم، لا يتغير.

بالمثل، يجري إنشاء وحدة باستخدام الكلمة المفتاحية module بالشكل التالي:

module Admin
End

تنفيذ مهمة ثابتة تعادل:

Admin = Module.new

بما في ذلك تحديد الاسم على أنَّه تأثير جانبي:

Admin.name # => "Admin"

تحذير: سياق التنفيذ لكتلة مُررت إلى Class.new أو Module.new لا يكافئ تمامًا أحد تعريفات الجسم باستخدام الكلمتين المفتاحيتين class و module. لكن كل من التعابير ينتج عنها نفس المهمة الثابتة.

وهكذا، عندما يقول أحدهم بشكل غير رسمي "الصنف String" ، فهذا يعني بالفعل: كائن الصنف المخزن في الثابت المسمى "String" في كائن الصنف المخزن في الثابت Object. إن String هي ثابت روبي عادي وكل شيء يتعلق بالثوابت مثل خوارزميات الاستبيان ينطبق عليها.

وبالمثل، في وحدة التحكم:

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

لا يعد Post صياغة لصنف. بدلًا من ذلك، Post هو ثابت روبي عادي. إذا كان كل شيء جيد، يقيم الثابت إلى كائن يستجيب إلى all.

هذا هو السبب في أننا نتحدث عن التحميل التلقائي للثابت (constant autoloading)، إذ ريلز لديها القدرة على تحميل الثوابت مباشرةً وتلقائيًّا.

تخزين الثوابت في الوحدات

الثوابت تنتمي إلى وحدات بالمعنى الحرفي. تحتوي الأصناف والوحدات على جدول للثوابت. فكر في الأمر كأنه جدول Hash.

دعونا نحلل مثالًا لفهم ما يعنيه ذلك حقًا. في حين أن الإساءات الشائعة للغة مثل "الصنف String" صحيحة، فإن العرض سيكون دقيقًا هنا لأغراض تعليمية.

لنأخذ بعين الاعتبار تعريف الوحدة التالية:

module Colors
  RED = '0xff0000'
end

أولاً، عند معالجة الكلمة المفتاحية module، ينشئ المترجم قيمة جديدة في جدول الثوابت لكائن الصنف المخزن في الثابت Object. يقترن المدخل بالاسم "Colors" إلى كائن الوحدة التي أُنشئت حديثًا. علاوة على ذلك، يعين المترجم اسم كائن الوحدة الجديد ليكون السلسلة النصية "Colors".

لاحقًا، عندما يُفسر نص تعريف الوحدة، يُنشأ قيمة جديد في جدول الثوابت لكائن الوحدة المخزن في الثابت Colors. تعين تلك القيمة الاسم "RED" إلى السلسلة "0xff0000".

على وجه الخصوص، لا يرتبط Color::RED كليًّا بأي ثابت RED آخر قد يوجد في أي كائن صنف أو وحدة أخرى. إذا كان هناك أي منها، سيكون لديها قيم منفصلة في جداول الثوابت الخاصة بها.

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

خوارزميات الاستبيان (Resolution Algorithms)

خوارزمية الاستبيان للثوابت النسبية

في أي مكان في الشيفرة، دعنا نعرّف cref ليكون العنصر الأول من التشعب إذا لم يكن فارغًا، أو Object خلاف ذلك.

دون الحصول على الكثير من التفاصيل، فإن خوارزمية الاستبيان لمراجع الثابت النسبي تسير على النحو التالي:

  • إذا لم يكن التشعب فارغًا، فسيُبحث عن الثابت في عناصره بالترتيب. يُتجاهل أسلاف تلك العناصر.
  • إذا لم يُعثر عليه، ستصعد الخوارزمية عبر سلسلة أسلاف cref.
  • إذا لم يُعثر عليه وكانت cref وحدةً، فسيُبحَث عن الثابت في Object.
  • إذا لم يُعثر عليه، يستدعى const_missing على cref. التنفيذ الافتراضي للتابع const_missing يرمي الاستثناء NameError ولكن يمكن تجاوزه.

لا يحاكي التحميل التلقائي لريلز هذه الخوارزمية، ولكن نقطة البداية هي اسم الثابت الذي سيُحمل تلقائيًا، و cref. اطلع على قسم المراجع النسبية للمزيد من التفاصيل.

خوارزمية الاستبيان للثوابت المؤهلة

الثوابت المؤهلة (qualified constants) تبدو كالتالي:

Billing::Invoice

يتكون Billing::Invoice من اثنين من الثوابت: Billing وهو نسبي ويُستبيَن باستخدام خوارزمية القسم السابق.

تنبيه: من شأن النقطتان البارزة أن تجعل الجزء الأول مطلقًا وليس نسبيًا: ::Billing::Invoice. من شأن ذلك أن يجبر Billing على الظهور كثابت عالي المستوى فقط.

Invoice على الجانب الآخر مؤهل من قبل Billing وسنرى استبيانه وقراره فيما يلي. لنحدد الأب ليكون هذا العنصر المؤهل للصنف أو الوحدة، أي Billing في المثال أعلاه. الخوارزمية للثوابت المؤهلة تسير بالشكل التالي:

  • يُبحّث عن الثابت في الأب وأسلافه. في الإصدار 2.5 من ريلز وما بعده، يتخطى Object إذا كان موجودًا بين الأسلاف وستستمر عملية التحقق من Kernel و BasicObject.
  • إذا فشل البحث، يستدعى const_missing في الأب. التنفيذ الافتراضي const_missing يرمي الاستثناء NameError ولكن يمكن تجاوزه.

تنبيه: ما قبل الإصدار 2.5 من ريلز، يقيم String::Hash إلى Hash والمترجم يصدر تحذيرًا: "toplevel constant Hash referenced by String::Hash" (ثابت Hash ذو مستوى أعلى أشير إليه بواسطة String::Hash). بدءًا من الإصدار 2.5، يرمي String::Hash الاستثناء NameError عند تخطي Object.

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

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

مفردات اللغة

مجالات أسماء الأب

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

على سبيل المثال ، مجال الاسم للأب من السلسلة "A::B::C" هو السلسلة "A::B"، ومجال الاسم للأب من "A::B" هو "A"، ومجال الاسم للأب من "A " هو "".

تفسير مجال اسم الأب عند التفكير في الأصناف والوحدات أمر صعب رغم ذلك. لنفترض أن الوحدة M تحمل الاسم "A::B":

  • قد لا يعكس مجال الاسم للأب، الذي هو "A"، التشعب في موضع محدد.
  • قد لا يكون الثابت A موجودًا بعد الآن، إذ يكون بعض التعليمات البرمجية قد أزالته من الكائن.
  • إذا كان A موجودًا، فقد لا يكون الصنف أو الوحدة التي كانت في الأصل في A موجودة بعد الآن. على سبيل المثال، إذا كانت هناك مهمة ثابتة أخرى بعد إزالة الثابت، فسيكون هناك كائن مختلف بشكل عام.
  • في مثل هذه الحالة، يمكن أن يحدث أن يعاد تعيين A إلى صنف جديدة أو وحدة تسمى أيضا "A"!
  • في السيناريوهات السابقة، لم يعد من الممكن الوصول إلى M خلال A::B ولكن كائن الوحدة نفسه يمكن أن يبقى على قيد الحياة في مكان ما وسيبقى اسمه "A::B".

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

آلية التحميل

تقوم ريلز بالتحميل الآلي للملفات مع Kernel.load عندما تكون قيمة الضبط config.cache_classes هي false، وهو الافتراضي في وضع التطوير، ومع Kernel.require خلاف ذلك، وهو الافتراضي في وضع الإنتاج.

يسمح Kernel.load لريلز بتنفيذ الملفات أكثر من مرة إذا كانت إعادة التحميل للثابت مُفعلة.

يستخدم هذا الدليل الكلمة «تحميل» (load) بحرية للإشارة إلى ترجمة ملف معين، لكن الآلية الفعلية يمكن أن تكون عبر Kernel.load أو Kernel.require اعتمادًا على تلك الراية.

إتاحة التحميل التلقائي

يكون ريلز دائمًا قادرًا على التحميل التلقائي بشرط توفر بيئة التشغيل الخاصة به. على سبيل المثال، الأمر runner التالية يعيد التحميل:

$ bin/rails runner 'p User.column_names'
["id", "email", "created_at", "updated_at"]

وحدة تحكم تتحمل تلقائيًّا، ومجموعة الاختبار تتحمل تلقائيًّا، وبالطبع التطبيق يتحمل تلقائيًّا أيضًا.

بشكل افتراضي، تحمل ريلز بشكل حثيث (eager loads) ملفات التطبيق عند تشغيله في وضع الإنتاج، لذلك لا يحدث معظم التحميل التلقائي هنا الذي يجري في التطوير. ولكن قد لا يزال يُطلَق التحميل التلقائي أثناء التحميل الحثيث.

على سبيل المثال، إعطاء:

class BeachHouse < House
End

إذا كان House لا يزال غير معروف عندما يُحمَّل app/models/beach_house.rb بشكل حثيث، تحمله تلقائيًا.

autoload_paths و eager_load_paths

كما تعلم، عندما تُعطَى require اسم ملف نسبي:

require 'erb'

تبحث روبي عن الملف في المجلدات المدرجة في ‎$LOAD_PATH. هذا هو، تكرر روبي على كافة المجلدات الخاصة به وتتحقق من كل واحد منها من وجود ملف يسمى "erb.rb" أو "erb.so" أو "erb.o" أو "erb.dll". إذا وجدت أي منها، يحملها المترجم وينهي عملية البحث. وإلا، فإنه يحاول مرة أخرى في المجلد التالي من القائمة. إذا نفدت القائمة، فسيُمرمَى الاستثناء LoadError.

سنغطي كيفية عمل التحميل التلقائي للثابت بمزيد من التفاصيل في وقت لاحق، لكن الفكرة هي أنه عندما يكون ثابتًا مثل Post مكتوبًا ومفقودًا، فإذا كان هناك ملفًا باسم post.rb على سبيل المثال في app/models، فسيجده ريلز، ويقيمه، ويحدد Post على أنه تأثير جانبي.

حسنًا، لدى ريلز مجموعة من المجلدات المشابهة لـ ‎$LOAD_PATH للبحث فيها عن post.rb. يطلق على هذه المجموعة الاسم autoload_paths وتتضمن افتراضيًا ما يلي:

  • جميع المجلدات الفرعية للــ app في التطبيق والمحركات الموجودة في وقت الإقلاع. على سبيل المثال، app/controllers. لا تحتاج إلى أن تكون الافتراضية، فكل المجلدات المخصصة مثل app/workers تنتمي تلقائيًا إلى autoload_paths.
  • أي مجلدات موجودة ذات مستوى ثان تسمى app/*/concerns  في التطبيق والمحركات.
  • المجلد test/mailers/previews.

إن eager_load_paths هو في البداية مسارات التطبيق (app) أعلاه.

تعتمد كيفية التحميل التلقائي للملفات على إعدادات تهيئة eager_load و cache_classes التي تختلف عادةً في أوضاع التطوير والإنتاج والاختبار:

  • في التطوير، تريد بدء التشغيل بسرعة مع تحميل تدريجي لشيفرة التطبيق. لذا، يجب يجب تعطيل التحميل الحثيث عبر تعيين eager_load إلى القيمة "false"، وسيحمل ريلز تلقائيًا الملفات حسب الحاجة (انظر قسم خوارزميات التحميل التلقائي أدناه) ثم يعيد تحميلها عند تغييرها (راجع قسم إعادة التحميل للثوابت أدناه).
  • في الإنتاج، ومع ذلك تريد التناسق وسلامة الصفحات ويمكن قبول وقت أطول للإقلاع. لذا، يتعين eager_load إلى القيمة true، وبعد ذلك أثناء الإقلاع (قبل أن يكون التطبيق جاهزًا لاستقبال الطلبات) تحمل ريلز كافة الملفات الموجودة في eager_load_paths ثم توقف التحميل التلقائي (ملحوظة: قد تكون هناك حاجة إلى التحميل التلقائي أثناء التحميل الحثيث). لا يعد التحميل التلقائي بعد التشغيل أمرًا جيدًا، نظرًا لأن التحميل التلقائي يمكن أن يتسبب في حدوث مشكلات في سلامة الصفحات.
  • في الاختبار، لسرعة التنفيذ (من الاختبارات الفردية) eager_load يكون false، لذلك يتبع ريلز سلوك التطوير نفسه.

ما هو موضح أعلاه هي الإعدادات الافتراضية مع تطبيق ريلز أنشأ حديثًا. هناك طرق متعددة يمكن تهيئتها بشكل مختلف (راجع توثيق ضبط تطبيقات ريلز). ولكن استخدام autoload_paths من تلقاء نفسها في الماضي (قبل الإصدار 5) قد يضبط المطورون autoload_paths للإضافة مواقع إضافية (على سبيل المثال lib الذي اعتاد أن يكون قائمة مسار autoload منذ سنوات، ولكن لم يعد كذلك). ومع ذلك، فإن هذا غير محبط الآن لمعظم الأغراض، حيث من المحتمل أن يؤدي إلى أخطاء في الإنتاج فقط. من الممكن إضافة مواقع جديدة إلى كل من config.eager_load_paths و config.autoload_paths ولكن استخدم ذلك على مسؤوليتك الخاصة.

انظر أيضًا قسم التحميل التلقائي في بيئة الاختبار.

الضبط config.autoload_paths غير قابل للتغيير من ملفات الضبط الخاصة بالبيئة.

يمكن فحص قيمة autoload_paths. في تطبيق أنشأ للتو:

$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
.../app/assets
.../app/channels
.../app/controllers
.../app/controllers/concerns
.../app/helpers
.../app/jobs
.../app/mailers
.../app/models
.../app/models/concerns
.../activestorage/app/assets
.../activestorage/app/controllers
.../activestorage/app/javascript
.../activestorage/app/jobs
.../activestorage/app/models
.../actioncable/app/assets
.../actionview/app/assets
.../test/mailers/previews

تنبيه: يحسب autoload_paths ويخزن مؤقتًا أثناء عملية التهيئة. يجب إعادة تشغيل التطبيق لتطبيق أي تغييرات في بنية المجلد.

خوارزميات التحميل التلقائي

المراجع النسبية

قد يظهر مرجع ثابت نسبي في عدة أماكن، على سبيل المثال، في

class PostsController < ApplicationController

 def index

   @posts = Post.all

 end

End

كل المراجع الثلاثة الثابتة نسبية.

الثوابت بعد الكلمات الأساسية class و module

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

إذا لم يُعرف الثابت عند هذه النقطة لا يعتبر ثابت مفقود، لا يُثار التحميل التلقائي.

لذا، في المثال السابق، إذا لم يحدد الوظيفة PostController عند ترجمة الملف، لن يُشغل Rails autoloading، سيعمل Ruby على تعريف وحدة التحكم فقط.

ثوابت المستوى الأعلى

على العكس من ذلك، إذا كان ApplicationController غير معروف، فسيعتبر الثابت مفقودًا وسيقوم Rails بمحاولة تحميل تلقائية.

لتحميل ApplicationController، يتكرر Rails على autoload_paths. أولاً تحقق من وجود app/assets/application_controller.rb. إذا لم يحدث ذلك، وهو ما يحدث عادةً، فسيستمر في البحث عن app/controllers/application_controller.rb.

إذا كان الملف يعرّف التطبيق الثابت، فإن كل شيء على ما يرام، وإلا فسيرفع LoadError:

unable to autoload constant ApplicationController, expected

<full path to application_controller.rb> to define it (LoadError)

ملاحظة: لا يتطلب Rails قيمة الثوابت التي تُحمل تلقائيًا لتكون صنف أو كائن وحدة نمطية. على سبيل المثال، إذا كان تطبيق الملف / models / max_clients.rb يعرّف MAX_CLIENTS = 100 autoloading MAX_CLIENTS يعمل بشكل جيد.

مجالات الأسماء

يبدو التحميل التلقائي لـ ApplicationController مباشرة تحت دلائل autoload_paths لأن التداخل في تلك البقعة فارغ.

يختلف وضع Post، ويكون التشعب في هذا الخط هو [PostsController] ويأتي تشغيل الدعم لمساحات الإسم.

الفكرة الأساسية هي أن تعطى

module Admin

 class BaseController < ApplicationController

   @@all_roles = Role.all

 end

End

للتحميل التلقائي لـ Role الذي سيفحص إذا تعرف في مجالات الأسماء الحالية أو المحلية، في وقت واحد. لذا، من الناحية المفاهيمية، نرغب في محاولة التحميل التلقائي لأي منها

Admin::BaseController::Role

Admin::Role

Role

بهذا الترتيب. هذه هي الفكرة. للقيام بذلك، يبحث Rails في autoload_paths على التوالي لأسماء الملفات مثل هذه:

admin/base_controller/role.rb

admin/role.rb

Role.rb

بعض عمليات البحث عن الأدلة الإضافية التي سنغطيها قريبًا.

'Constant::Name'.underscore يعطي المسار النسبي بدون امتداد لاسم الملف حيث من المتوقع أن يُحدد Constant :: Name.

دعنا نرى كيف يحمل Rails تلقائيًا الثابت Post في postsController أعلاه بافتراض أن التطبيق يحتوي على نموذج Post محدد في app/models/post.rb.

أولاً يتحقق من posts_controller / post.rb في autoload_paths:

app/assets/posts_controller/post.rb

app/controllers/posts_controller/post.rb

app/helpers/posts_controller/post.rb

...

test/mailers/previews/posts_controller/post.rb

نظرًا لاستنفاد البحث دون نجاح، يجري بحث مشابه عن الدليل، سنرى السبب في القسم التالي:

app/assets/posts_controller/post

app/controllers/posts_controller/post

app/helpers/posts_controller/post

...

test/mailers/previews/posts_controller/post

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

app/assets/post.rb

app/controllers/post.rb

app/helpers/post.rb

app/mailers/post.rb

app/models/post.rb

إذا عُثر على ملف مطابق في app/models/post.rb. يتوقف البحث هناك ويُحمل الملف. إذا كان الملف يعرف بالفعل Post اذا كل شيء على ما يرام، وإلا يرفع LoadError.

مراجع مؤهلة

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

على سبيل المثال، انظر

module Admin

 User

End

و

Admin::User

إذا كان User  مفقودًا، ففي كلتا الحالتين يعرف Rails أن ثابتًا يسمى "User" مفقود في وحدة تسمى "Admin".

إذا كان هناك User رفيع المستوى روبي سوف يحلها في المثال السابق، لكن لن يكون في الأخير. بشكل عام، لا تحاكي Rails خوارزميات روبي لإحلال الثوابت، ولكنها في هذه الحالة تحاول استخدام الكشف عن الأشياء التالية:

" إذا كان أي من مجالات الأسماء الأصل للصنف أو الوحدة النمطية يحتوي على ثابت مفقود، يفترض Rails أن المرجع هو نسبي. خلاف ذلك مؤهل. "

على سبيل المثال، إذا كان هذا الرمز يشغل التحميل التلقائي:

Admin::User

والثابت User موجود بالفعل في Object، ليس من الممكن أن الوضع:

module Admin

 User

End

لأنه بخلاف ذلك، كان روبي قد حل User ولم يكن قد شُغل أي تحميل ذاتي في المقام الأول. وبالتالي، يفترض Rails مرجع مؤهلاً ويعتبر ملف admin / user.rb والدليل admin/user أن تكون الخيارات صالحة فقط.

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

ومع ذلك، يحدث التحميل التلقائي عند الطلب. إذا لم يُحمل User ذي المستوى الأعلى عن طريق الصدفة، يفترض Rails مرجعًا نسبيًا بموجب العقد.

إن تسمية التضارب من هذا النوع أمر نادر في الممارسة، ولكن إذا حدث ذلك، فإن required_dependency يوفر حلاً من خلال التأكد من أن الثابت المطلوب لتحفيز الكشف عن مجريات الأمور محدد في المكان المتضارب.

الوحدات التلقائية

عندما تعمل وحدة نمطية مثل مجال اسم، لا يتطلب Rails التطبيق لتعريف ملف له، الدليل المتطابق لمجال الاسم كافٍ.

لنفترض أن التطبيق يحتوي على مكتب خلفي يُخزن وحدات التحكم فيه في app/controllers/admin.

إذا لم تُحمل وحدة Admin حتى الآن عند إصابة Admin::UsersController، فإن Rails يحتاج أولاً إلى تحميل الثابت Admin أولاً.

إذا كان autoload_paths يحتوي على ملف يسمى admin.rb Rails سيُحمل هذا الملف، ولكن إذا لم يكن هناك مثل هذا الملف وعُثر على دليل يسمى admin، يُنشأ Rails وحدة فارغة ويُعينه إلى الثابت Admin أثناء الطيران.

الإجراء العام

تم ذكر فقدان المراجع النسبية في cref حيث سُجلت، وتم ذكر فقدان المراجع المؤهلة في الأصل (راجع خوارزمية الاستبيان للثوابت النسبية في بداية هذا الدليل لتعريف cref، و خوارزمية الاستبيان للثوابت المؤهلة لتعريف الأصل).

يكون الإجراء للتحميل التلقائي للثابت  C في الموقف التعسفي كما يلي:

if the class or module in which C is missing is Object

 let ns = <nowiki>''</nowiki>

else

 let M = the class or module in which C is missing

 if M is anonymous

   let ns = <nowiki>''</nowiki>

 else

   let ns = M.name

 end

end

loop do

 # ابحث عن ملف عادي.

 for dir in autoload_paths

   if the file "#{dir}/#{ns.underscore}/c.rb" exists

     load/require "#{dir}/#{ns.underscore}/c.rb"

     if C is now defined

       return

     else

       raise LoadError

     end

   end

 end

 # ابحث عن وحدة تلقائية.

 for dir in autoload_paths

   if the directory "#{dir}/#{ns.underscore}/c" exists

     if ns is an empty string

       let C = Module.new in Object and return

     else

       let C = Module.new in ns.constantize and return

     end

   end

 end

 if ns is empty

   #وصلنا إلى المستوى الأعلى دون العثور على الثابت.

   raise NameError

 else

   if C exists in any of the parent namespaces

     # الثوابت المؤهلة الإرشادية.

     raise NameError

   else

     # حاول مرة أخرى في مساحة الاسم الأصل.

     let ns = the parent namespace of ns and retry

   end

 end

End

Require_dependency

يُشغل التحميل التلقائي عند الطلب ولذلك قد يكون التعليمات البرمجية التي تستخدم ثابت معين محددة بالفعل أو قد يؤدي إلى التحميل التلقائي. يعتمد ذلك على مسار التنفيذ وقد يختلف بين عمليات التنفيذ.

ومع ذلك، هناك أوقات تريد فيها التأكد من معرفة ثابت معين عندما يصل التنفيذ إلى رمز ما. يوفر require_dependency طريقة لتحميل ملف باستخدام آلية التحميل الحالية، وتتبع الثوابت المحددة في هذا الملف كما لو تُحملت تلقائيًا لإعادة تحميلها حسب الحاجة.

نادراً ما نحتاج إلى require_dependency، ولكن نرى بعض حالات الاستخدام في Autoloading و STI وعندما لا يُشغل الثوابت.

ملاحظة: على عكس autoloading، لا تتوقع require_dependency أن يحدد الملف أي ثابت معين. قد يكون استغلال هذا السلوك ممارسة سيئة بالرغم من ذلك، يجب أن تتطابق الملفات والمسارات الثابتة.

إعادة التحميل للثوابت

عندما يكون config.cache_classes false، فإن Rails قادرة على إعادة تحميل الثوابت ذاتية التحميل.

على سبيل المثال، إذا كنت في جلسة عمل وحدة التحكم وقمت بتحرير بعض الملفات من وراء الكواليس، فيمكن إعادة تحميل الشيفرة مع reload! أمر:

> reload!

عند تشغيل التطبيق، يُعاد تحميل الشفرة عندما يتغير شيء ذي صلة بهذا المنطق. من أجل القيام بذلك، تراقب Rails عدد من الأشياء:

  • config/routes.rb.
  • مواقع محلية.
  • ملفات روبي تحت autoload_paths.
  • db / schema.rb و db / structure.sql.

إذا تغير أي شيء هناك، فهناك برنامج وسيط يكتشفها ويعيد تحميل الشفرة.

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

هذه عملية كل شيء أو لا شيء، Rails لا يحاول إعادة تحميل سوى ما تغيرت لأن التبعيات بين الطبقات يجعل ذلك صعب حقا. بدلا من ذلك، يُمسح كل شيء.

Module#autoload غير متضمنة

Module#autoload يوفر طريقة كسولة لتحميل الثوابت التي تتكامل تمامًا مع خوارزميات البحث لثوابت روبي، وواجهة برمجة التطبيقات الثابتة الديناميكية، إلخ. إنها شفافة تمامًا.

تستخدم Rails الداخلية استخدام واسع النطاق لتأجيل أكبر قدر ممكن من العمل من عملية التمهيد. ولكن التحميل التلقائي للثوابت لايُنفذ في Rails مع Module#autoload.

أحد التطبيقات الممكنة استناداً إلى Module#autoload سيكون السير إلى شجرة التطبيق وإصدار استدعاءات autoload تُعين أسماء الملفات الموجودة إلى اسمها الثابت التقليدي.

هناك عدد من الأسباب التي تمنع Rails من استخدام هذا التنفيذ.

على سبيل المثال، لا يستطيع Module#autoload  إلا تحميل الملفات باستخدام require، لذا لن تكون عملية إعادة التحميل ممكنة. ليس ذلك فحسب، بل يستخدم require الداخلي الذي لا Kernel#require.

ثم، فإنه لا يوفر أية طريقة لإزالة التعريفات في حالة حذف ملف. في حالة إزالة الثابت باستخدام Module#remove_const لا يُشغل autoload مرة أخرى. كما أنه لا يدعم الأسماء المؤهلة، لذلك يجب ترجمة الملفات ذات مجالات الأسماء أثناء سير الشجرة لتثبيت استدعاءات autoload الخاصة، لكن هذه الملفات قد تحتوي على مراجع ثابتة لم تُكون بعد.

سيكون التنفيذ الذي يعتمد على Module#autoload رائعًا لكن، كما ترى، على الأقل اعتبارًا من اليوم غير ممكن. تُنفذ التحميل التلقائي في Rails مع  Module#const_missing وهذا هو السبب في أنه يحتوي على العقد الخاص به، موثقة في هذا الدليل.

Gotchas المشترك

التشعب والثوابت المؤهلة

دعونا ننظر:

module Admin

 class UsersController < ApplicationController

   def index

     @users = User.all

   end

 end

End

و

class Admin::UsersController < ApplicationController

 def index

   @users = User.all

 end

End

لحل User روبي يتحقق من Admin في الحالة السابقة، لكنه لا يوجد في الأخير لأنه لا ينتمي إلى التداخل (راجع التشعب و خوارزميات الاستبيان).

لسوء الحظ، لا تعرف التحميل التلقائي ل Rails التشعب في المكان الذي كان فيه الثابت مفقودًا، وبالتالي لا يكون قادرًا على التصرف كما يفعل روبي. على وجه الخصوص، سيحصل على التحميل التلقائي Admin::User في كلتا الحالتين.

على الرغم من أن الثوابت المؤهلة التي تحتوي على كلمات أساسية class و module قد تعمل تقنيًا مع التحميل التلقائي في بعض الحالات، فمن الأفضل استخدام الثوابت النسبية بدلاً من ذلك:

module Admin

 class UsersController < ApplicationController

   def index

     @users = User.all

   end

 end

End

التحميل التلقائي و STI

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

<nowiki>#</nowiki> app/models/polygon.rb

class Polygon < ApplicationRecord

end

<nowiki>#</nowiki> app/models/triangle.rb

class Triangle < Polygon

end

<nowiki>#</nowiki> app/models/rectangle.rb

class Rectangle < Polygon

End

ينشئ Triangle.create صفًا يمثل المثلث، وينشئ Rectangle.create صف يمثل مستطيلاً. إذا كان المعرف هو الرقم التعريفي لسجل موجود، فإن (Polygon.find (id ترجع كائن من النوع الصحيح.

التوابع التي تعمل على مجموعات هي أيضا على بينة من التسلسل الهرمي. على سبيل المثال، ترجع Polygon.all كافة سجلات الجدول، لأن كافة المستطيلات والمثلثات عبارة عن مضلعات. يعتني السجل النشط بإعادة نسخ من فئتها المتطابقة في مجموعة النتائج.

والتحميل التلقائي أنواع حسب الحاجة. على سبيل المثال، إذا كان Polygon.first مستطيلاً ولم يُحمل Rectangle بعد، السجل النشط يُحمله تلقائيًا وينشئ السجل بشكل صحيح.

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

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

يُحمل Rectangle.all المستطيلات فقط عن طريق إضافة قيد نوع إلى الاستعلام:

SELECT "polygons".* FROM "polygons"

WHERE "polygons"."type" IN ("Rectangle")

دعونا نقدم الآن صنف فرعية من Rectangle:

# app/models/square.rb

class Square < Rectangle

End

Rectangle.all يجب الآن إرجاع المستطيلات والمربعات:

SELECT "polygons".* FROM "polygons"

WHERE "polygons"."type" IN ("Rectangle", "Square")

ولكن هناك تحذير هنا: كيف يعرف السجل النشط أن صنف Square موجودة على الإطلاق؟

حتى إذا كان ملف app/models/square.rb موجودًا ويحدد صنف Square، إذا لم يستخدم أي رمز بعد ذلك، فإن Rectangle.all يصدر الاستعلام.

SELECT "polygons".* FROM "polygons"

WHERE "polygons"."type" IN ("Rectangle")

هذا ليس خطأ، ويشمل الاستعلام جميع الأحفاد المعروفة من Rectangle.

طريقة للتأكد من أن هذا يعمل بشكل صحيح بغض النظر عن ترتيب التنفيذ هو يدوياً تحميل الأصناف الفرعية المباشرة في أسفل الملف الذي يعرّف كل صنف متوسطة:

<nowiki>#</nowiki> app/models/rectangle.rb

class Rectangle < Polygon

end

require_dependency 'square'

يجب أن يحدث هذا لكل صنف وسيطة (غير جذر وغير ورقة). لا يقوم نطاق الجذر بتوسيع نطاق البحث حسب النوع، وبالتالي لا يلزم بالضرورة معرفة كل أحفاده.

التحميل التلقائي والطلب

لا يجب مطلقًا طلب الملفات التي تحدد الثوابت التي ستُحمل تلقائيًا:

require 'user' # DO NOT DO THIS

class UsersController < ApplicationController

 ...

End

هناك نوعان من gotchas محتملة هنا في وضع التطوير:

1- إذا تحمل User تلقائيًا قبل الوصول إلى require، فسيشغل app/models/user.rb مرة أخرى نظرًا لأن التحميل لا يُحدّث $ LOADED_FEATURES.

2- إذا كانت require  قيد التشغيل أولًا، فإن Rails لا يضع علامة على User كتحميل تلقائي لثابت ولا يُعاد تحميل التغييرات على app/models/user.rb.

فقط اتبع التدفق واستخدم التحميل التلقائي للثابت دائما، أبدا لا تخلط بين التحميل التلقائي و require. كحل أخير، إذا كان بعض الملفات يحتاج إلى تحميل ملف معين استخدم require_dependency للعب لطيف مع التحميل التلقائي للثابت. نادرًا ما يكون هذا الخيار مطلوبًا في الممارسة.

وبالطبع، فإن استخدام ما يتطلب من ملفات حُملت تلقائيًا لتحميل مكتبات عادية تابعة لجهات خارجية أمر جيد، و Rails قادرة على تمييز ثوابتها، ولا يُوضع علامة عليها بأنها تحميل تلقائي.

التحميل التلقائي و المهيأت   

خذ بعين الاعتبار هذه المهمة في config / initializers / set_auth_service.rb:

AUTH_SERVICE = if Rails.env.production?

 RealAuthService

else

 MockedAuthService

End

قد يكون الغرض من هذا الإعداد أن يستخدم التطبيق الصنف التي تتوافق مع البيئة عبر AUTH_SERVICE. في وضع التطوير يحصل على التحميل التلقائي MockedAuthService عند تشغيل المُهيئ. لنفترض أننا نقوم ببعض الطلبات، ونغير تنفيذها، ونضغط على التطبيق مرة أخرى. لدهشتنا لا تنعكس التغييرات. لماذا ا؟

كما رأينا سابقاً، يزيل Rails التحميل التلقائي للثوابت، ولكن يخزن AUTH_SERVICE كائن الصنف الأصلي. تالفة، غير قابلة للوصول باستخدام الثابت الأصلي  ولكن وظيفية تماما.

يلخص الشيفرة التالي الحالة:

class C

 def quack

   'quack!'

 end

end

X = C

Object.instance_eval { remove_const(:C) }

X.new.quack # => quack!

X.name      # => C

C           # => uninitialized constant C (NameError)

وبسبب ذلك، ليست فكرة جيدة التحميل التلقائي للثوابت عند تهيئة التطبيق.

في الحالة أعلاه، يمكننا تنفيذ نقطة وصول ديناميكية:

<nowiki>#</nowiki> app/models/auth_service.rb

class AuthService

 if Rails.env.production?

   def self.instance

     RealAuthService

   end

 else

   def self.instance

     MockedAuthService

   end

 end

End

واستخدم التطبيق AuthService.instance بدلاً من ذلك. ستُحمل AuthService عند الطلب وتكون autoload-friendly.

required_dependency و المُهيآت

كما رأينا من قبل، need_dependency تحميل الملفات بطريقة autoloading-friendly. ومع ذلك، عادةً ما لا يكون مثل هذا الاستدعاء منطقيًا في المُهيئ.

يمكن للمرء أن يفكر في إجراء بعض استدعاءات relay_dependency في المُهيئ للتأكد من تحميل ثوابت معينة مقدما، على سبيل المثال كمحاولة لمعالجة gotcha مع STIs.

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

الإستدعاء إلى require_dependency يجب أن تكون مكتوبة بشكل استراتيجي في مواقع التحميل التلقائي.

عندما لا تكون الثوابت مفقودة

المراجع النسبية

دعونا ننظر في جهاز محاكاة الطيران. التطبيق يحتوي على نموذج رحلة افتراضي:

<nowiki>#</nowiki> app/models/flight_model.rb

class FlightModel

End

يمكن تجاوزها من قبل كل طائرة، على سبيل المثال:

<nowiki>#</nowiki> app/models/bell_x1/flight_model.rb

module BellX1

 class FlightModel < FlightModel

 end

end

<nowiki>#</nowiki> app/models/bell_x1/aircraft.rb

module BellX1

 class Aircraft

   def initialize

     @flight_model = FlightModel.new

   end

 end

End

يريد المُهيئ إنشاء BellX1 :: FlightModel وتداخل يحتوي على BellX1، التي تبدو جيدة. ولكن إذا تحمل نموذج الطيران الافتراضي ولم يُثبت نموذج Bell-X1، فسيكون بمقدور المترجم حل ملف FlightModel ذي المستوى الأعلى وبالتالي لا يُشغل ميزة التحميل التلقائي لـ BellX1 :: FlightModel.

يعتمد هذا الرمز على مسار التنفيذ.

يمكن في الغالب حل هذا النوع من الغموض باستخدام ثوابت مؤهلة:

module BellX1

 class Plane

   def flight_model

     @flight_model ||= BellX1::FlightModel.new

   end

 end

End

أيضا، require_dependency هو الحل:

require_dependency 'bell_x1/flight_model'

module BellX1

 class Plane

   def flight_model

     @flight_model ||= FlightModel.new

   end

 end

End

مراجع مؤهلة

ملاحظة: هذه gotcha ممكن فقط في روبي <2.5.

معطى

<nowiki>#</nowiki> app/models/hotel.rb

class Hotel

end

<nowiki>#</nowiki> app/models/image.rb

class Image

end

<nowiki>#</nowiki> app/models/hotel/image.rb

class Hotel

 class Image < Image

 end

End

التعبير Hotel :: Image غامض لأنه يعتمد على مسار التنفيذ.

كما رأينا من قبل، يبحث روبي عن الثابت في Hotel وأسلافه. إذا تحمل app/models/image.rb ولكن app/models/hotel/image.rb لا، لم يعثر روبي على Image في Hotel، لكنه في Object:

$ bin/rails r 'Image; p Hotel::Image' 2>/dev/null

Image # NOT Hotel::Image!

يجب أن يكون رمز تقييم Hotel :: Image للتأكد من تحميل app/models/hotel/image.rb، ربما مع require_dependency.

في هذه الحالات، يصدر المترجم تحذيرًا على الرغم من:

warning: toplevel constant Image referenced by Hotel::Image

يمكن ملاحظة هذا الاستبيان الثابت المذهل مع أي صنف مؤهلة:

2.1.5 :001 > String::Array

(irb):1: warning: toplevel constant Array referenced by String::Array

=> Array

ملاحظة: للعثور على هذا gotcha  يجب أن تكون مجال الاسم المؤهلة صنف، الكائن ليس سلف من الوحدات.

التحميل التلقائي داخل أصناف Singleton

لنفترض أن لدينا تعريفات الصنف هذه:

<nowiki>#</nowiki> app/models/hotel/services.rb

module Hotel

 class Services

 end

end

<nowiki>#</nowiki> app/models/hotel/geo_location.rb

module Hotel

 class GeoLocation

   class << self

     Services

   end

 end

End

إذا كان Hotel::Services معروفة في وقت تحميل app/models/hotel/geo_location.rb، تُحل الـ Services بواسطة Ruby لأن الفندق ينتمي إلى التشعب عندما تُفتح صنف singleton من Hotel :: GeoLocation.

ولكن إذا كان Hotel::Services غير معروفة، تكون Rails غير قادرة على التحميل التلقائي، ويرفع التطبيق NameError.

السبب في ذلك هو أن تشغيل التحميل التلقائي لصنف singleton، هو مجهول، وكما رأينا من قبل، Rails فقط تتحقق من مجال الاسم الأعلى في حالة الحافة.

الحل السهل لهذا التحذير هو تأهيل الثابت:

module Hotel

 class GeoLocation

   class << self

     Hotel::Services

   end

 end

End

التحميل التلقائي في BasicObject

لا تملك أحفاد Direct من BasicObject كائن بين أسلافهم ولا يمكن حل ثوابت المستوى الأعلى:

class C < BasicObject

 String # NameError: uninitialized constant C::String

end

عندما يشارك التحميل التلقائي تلك المؤامرة لديها تطور. دعونا ننظر:

class C < BasicObject

 def user

   User # WRONG

 end

End

نظرًا لأن Rails يتحقق من مجال الاسم ذات المستوى الأعلى، يحصل User على التحميل التلقائي بشكل جيد في المرة الأولى التي يستدعى فيها التابع User. تحصل على الاستثناء فقط إذا كان الثابت User معروفًا عند هذه النقطة، خاصة في ثاني استدعاء للـ Use:

c = C.new

c.user # surprisingly fine, User

c.user # NameError: uninitialized constant C::User

لأنه يكتشف أن مجال الاسم الأصل تحتوي على الثابت (انظر مراجع مؤهلة).

كما هو الحال مع روبي الصافي، داخل جسم السلف المباشر من BasicObject، استخدم دائمًا المسارات الثابتة المطلقة:

class C < BasicObject

 ::String # RIGHT

 def user

   ::User # RIGHT

 end

End

التحميل التلقائي في بيئة الاختبار

عند تهيئة بيئة الاختبار للتحميل التلقائي، يمكنك التفكير في عدة عوامل.

على سبيل المثال، قد يكون من المفيد تشغيل اختباراتك باستخدام إعداد مماثل للإنتاج (config.eager_load = true, config.cache_classes = true) من أجل اكتشاف أي مشاكل قبل أن تصل إلى الإنتاج (وهذا تعويض عن عدم وجود تعادل dev-prod ). ومع ذلك فإن هذا سيبطئ وقت الإقلاع للاختبارات الفردية على جهاز dev (وهو غير متوافق على الفور مع الإنطلاقة انظر أدناه). لذا فإن أحد الاحتمالات هو القيام بذلك على جهاز CI فقط (والذي يجب أن يعمل دون الإنطلاقة).

على جهاز التطوير يمكنك بعد ذلك تشغيل الاختبارات الخاصة بك مع أي شيء أسرع (بشكل مثالي config.eager_load = false).

باستخدام أداة التحميل المسبق من Spring (المضمنة في تطبيقات rails الجديدة)، ستحتفظ بشكل مثالي بـ config.eager_load = false وفقًا للتطوير. في بعض الأحيان قد ينتهي بك الأمر مع تكوين مختلط (config.eager_load = true ، config.cache_classes = true AND config.enable_dependency_loading = true)، راجع قضية Spring. ومع ذلك، قد يكون من الأسهل الحفاظ على نفس التكوين مثل التطوير، اكتشف - حل كل سبب يؤدي الى فشل التحميل التلقائي  (ربما عن طريق نتائج اختبار CI الخاص بك).

من حين لآخر قد تحتاج إلى eager_load صراحة باستخدام Rails.application.eager_load! في إعداد اختباراتك - قد يحدث هذا إذا كانت اختباراتك تنطوي على تعدد مؤشرات.