الفرق بين المراجعتين ل"Rails/active support core extensions"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
(إنشاء الصفحة. هذه الصفحة من مساهمات "تسنيم ولهازي")
 
ط
 
(6 مراجعات متوسطة بواسطة نفس المستخدم غير معروضة)
سطر 1: سطر 1:
<noinclude>{{DISPLAYTITLE:ملحقات الدعم الفعال الأساسية في ريلز}}</noinclude>
+
<noinclude>{{DISPLAYTITLE:ملحقات Active Support الأساسية في ريلز}}</noinclude>
 
[[تصنيف:Rails]]
 
[[تصنيف:Rails]]
 
[[تصنيف:Rails Digging Deeper]]
 
[[تصنيف:Rails Digging Deeper]]
الدعم الفعَّال (Active Support) هو مكوّن ريلز المسؤول عن توفير ملحقات لغة [[Ruby|روبي]] والأدوات المساعدة والأشياء الأخرى المعترضة.
+
[[Rails/active support|Active Support]] هو مكوّن ريلز المسؤول عن توفير ملحقات لغة [[Ruby|روبي]] والأدوات المساعدة والأشياء الأخرى المعترضة.
  
 
كما يوفر حدًا أدنى من المعرفة الواسعة على مستوى اللغة يستهدف كل من تطوير تطبيقات ريلز، وتطوير ريلز نفسه. بعد قراءة هذا الدليل، ستتعلم:
 
كما يوفر حدًا أدنى من المعرفة الواسعة على مستوى اللغة يستهدف كل من تطوير تطبيقات ريلز، وتطوير ريلز نفسه. بعد قراءة هذا الدليل، ستتعلم:
سطر 8: سطر 8:
 
* كيفية تحميل كل الملحقات.
 
* كيفية تحميل كل الملحقات.
 
* كيفية انتقاء الملحقات التي تريدها فقط.
 
* كيفية انتقاء الملحقات التي تريدها فقط.
* ماهيّ الملحقات التي يوفرها الدعم الفعَّال.
+
* ماهيّ الملحقات التي يوفرها [[Rails/active support|Active Support]].
  
 
== كيفية تحميل الملحقات الأساسية ==
 
== كيفية تحميل الملحقات الأساسية ==
  
=== الدعم الفعَّال المستقل ===
+
=== Active Support المستقل ===
لا يحمّل الدعم الفعَّال (Active Support) أي شيء افتراضيًّا من أجل عدم شغل أية مساحة بل يُقسَّم لأجزاء صغيرة بحيث تستطيع تحميل ما تحتاجه فقط؛ كما أنه يملك بعض نقاط انطلاق ملائمة لتحميل الملحقات ذات الصلة مرّة واحدة أو حتى كل الملحقات.
+
لا يحمّل [[Rails/active support|Active Support]] أي شيء افتراضيًّا من أجل عدم شغل أية مساحة بل يُقسَّم لأجزاء صغيرة بحيث تستطيع تحميل ما تحتاجه فقط؛ كما أنه يملك بعض نقاط انطلاق ملائمة لتحميل الملحقات ذات الصلة مرّة واحدة أو حتى كل الملحقات.
  
 
يكون ذلك بعد استعمال الأمر <code>require</code> البسيط:<syntaxhighlight lang="rails">
 
يكون ذلك بعد استعمال الأمر <code>require</code> البسيط:<syntaxhighlight lang="rails">
سطر 29: سطر 29:
 
require 'active_support'
 
require 'active_support'
 
require 'active_support/core_ext/object/blank'
 
require 'active_support/core_ext/object/blank'
</syntaxhighlight>عُدّل "الدعم الفعَّال" بعناية بحيث لا يُحمّل انتقاء ملف (مثل المثال السابق) إلّا الاعتماديّات المطلوبة بشدّة إن وُجدَت.
+
</syntaxhighlight>عُدّل "[[Rails/active support|Active Support]]" بعناية بحيث لا يُحمّل انتقاء ملف (مثل المثال السابق) إلّا الاعتماديّات المطلوبة بشدّة إن وُجدَت.
  
==== تحميل الملحقات الأساسية المُجمّعة ====
+
==== تحميل الملحقات الأساسية المجمعة ====
 
المستوى التالي هو ببساطة تحميل جميع الملحقات إلى <code>Object</code>. كقاعدة عامة، تتوفّر الملحقات لـ <code>SomeClass</code> مرّة واحدة عن طريق تحميل active_support/core_ext/some_class.
 
المستوى التالي هو ببساطة تحميل جميع الملحقات إلى <code>Object</code>. كقاعدة عامة، تتوفّر الملحقات لـ <code>SomeClass</code> مرّة واحدة عن طريق تحميل active_support/core_ext/some_class.
  
وبالتالي تحميل جميع الملحقات لـ <code>Object</code> (بما في ذلك <code>?blank</code>) يكون بالشكل التالي:<syntaxhighlight lang="rials">
+
وبالتالي تحميل جميع الملحقات لـ <code>Object</code> (بما في ذلك <code>?blank</code>) يكون بالشكل التالي:<syntaxhighlight lang="rails">
 
require 'active_support'
 
require 'active_support'
 
require 'active_support/core_ext/object'
 
require 'active_support/core_ext/object'
سطر 45: سطر 45:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== تحميل جميع الدعم الفعَّال ====
+
==== تحميل جميع Active Support ====
وأخيرًا، إن رغبت في توفير الدعم الفعَّال (Active Support) كاملًا، ما عليك سوى كتابة:<syntaxhighlight lang="rails">
+
وأخيرًا، إن رغبت في توفير [[Rails/active support|Active Support]] كاملًا، ما عليك سوى كتابة:<syntaxhighlight lang="rails">
 
require 'active_support/all'
 
require 'active_support/all'
</syntaxhighlight>هذا لا يضع فعلًا الدعم الفعَّال بأكمله في الذاكرة مسبقًا، بل تُعدُّ بعض الأشياء عن طريق <code>autoload</code>، لذلك تُحمّل فقط إن استُخدمت.
+
</syntaxhighlight>هذا لا يضع فعلًا [[Rails/active support|Active Support]] بأكمله في الذاكرة مسبقًا، بل تُعدُّ بعض الأشياء عن طريق <code>autoload</code>، لذلك تُحمّل فقط إن استُخدمت.
  
=== الدعم الفعَّال ضمن تطبيق ريلز ===
+
=== Active Support ضمن تطبيق ريلز ===
يحمّل تطبيق ريلز كل الدعم الفعَّال ما لم تكن قيمة <code>config.active_support.bare</code> مساويةً إلى <code>true</code>. سُيحمّل التطبيق في هذه الحالة ما ينتقيه الإطار نفسه لاحتياجاته الخاصة فقط، ويظل بإمكانه الانتقاء بنفسه على أي مستوى من مستويات التقسيم (granularity level)، كما وُضّح في القسم السابق.
+
يحمّل تطبيق ريلز كل [[Rails/active support|Active Support]] ما لم تكن قيمة <code>config.active_support.bare</code> مساويةً إلى <code>true</code>. سُيحمّل التطبيق في هذه الحالة ما ينتقيه الإطار نفسه لاحتياجاته الخاصة فقط، ويظل بإمكانه الانتقاء بنفسه على أي مستوى من مستويات التقسيم (granularity level)، كما وُضّح في القسم السابق.
  
 
== ملحقات لجميع الكائنات ==
 
== ملحقات لجميع الكائنات ==
  
=== <code>?blank</code> و <code>?present</code> ===
+
=== التابعان <code>?blank</code> و <code>?present</code> ===
 
تُعتبر القيم التالية فارغة في تطبيق ريلز:
 
تُعتبر القيم التالية فارغة في تطبيق ريلز:
 
* <code>[[Ruby/NilClass|nil]]</code> و <code>[[Ruby/FalseClass|false]]</code>
 
* <code>[[Ruby/NilClass|nil]]</code> و <code>[[Ruby/FalseClass|false]]</code>
 
* [[Ruby/String|السلاسل النصيّة]] التي تتكوّن من مسافات بيضاء فقط (انظر الملاحظة أدناه)
 
* [[Ruby/String|السلاسل النصيّة]] التي تتكوّن من مسافات بيضاء فقط (انظر الملاحظة أدناه)
 
* [[Ruby/Array|المصفوفات]] و<nowiki/>[[Ruby/Hash|جداول Hash]] الفارغة
 
* [[Ruby/Array|المصفوفات]] و<nowiki/>[[Ruby/Hash|جداول Hash]] الفارغة
* أي كائن آخر يستجيب لـ <code>?empty</code> وهو خالي.
+
* أي كائن آخر يستجيب للتابع <code>?empty</code> وهو خالي.
 
'''تنويه''': تستخدم التوابع الخبرية (predicate) للسلاسل النصيّة صنف الحرف الواعي بترميز اليونيكود <code>[:space:]</code>، لذلك يُعتبر U+2029 (فاصل الفقرات) مثلًا مسافة بيضاء.
 
'''تنويه''': تستخدم التوابع الخبرية (predicate) للسلاسل النصيّة صنف الحرف الواعي بترميز اليونيكود <code>[:space:]</code>، لذلك يُعتبر U+2029 (فاصل الفقرات) مثلًا مسافة بيضاء.
  
سطر 77: سطر 77:
 
   ...
 
   ...
 
end
 
end
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/blank.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/blank.rb active_support/core_ext/object/blank.rb].
  
 
=== التابع <code>presence</code> ===
 
=== التابع <code>presence</code> ===
سطر 83: سطر 83:
 
host = config[:host].presence || 'localhost'
 
host = config[:host].presence || 'localhost'
  
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/blank.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/blank.rb active_support/core_ext/object/blank.rb].
  
 
=== التابع <code>?duplicable</code> ===
 
=== التابع <code>?duplicable</code> ===
سطر 92: سطر 92:
 
Complex(0).dup      # => TypeError: can't copy Complex
 
Complex(0).dup      # => TypeError: can't copy Complex
  
</syntaxhighlight>يُوفّر الدعم الفعَّال التابع <code>?duplicable</code> لاستعلام كائن حول هذا:<syntaxhighlight lang="rails">
+
</syntaxhighlight>يُوفّر Active Support التابع <code>?duplicable</code> لاستعلام كائن حول هذا:<syntaxhighlight lang="rails">
 
"foo".duplicable?          # => true
 
"foo".duplicable?          # => true
 
"".duplicable?              # => true
 
"".duplicable?              # => true
سطر 120: سطر 120:
 
</syntaxhighlight>'''تحذير''': يقدر أي صنف على منع التكرار عبر حذف التابع <code>dup</code> والتابع <code>clone</code> منه أو إطلاق استثناء منهما. وبالتالي، فقط التابع <code>rescue</code> يقدر على معرفة ما إذا كان كائن ما قابلًا للتكرار. يعتمد التابع <code>?duplicable</code> على القائمة الثابتة (hard-coded list) أعلاه لكنه أسرع بكثير من <code>rescue</code>. استخدمه فقط إن تيقنت أن القائمة الثابتة كافية في حالة استخدامك.
 
</syntaxhighlight>'''تحذير''': يقدر أي صنف على منع التكرار عبر حذف التابع <code>dup</code> والتابع <code>clone</code> منه أو إطلاق استثناء منهما. وبالتالي، فقط التابع <code>rescue</code> يقدر على معرفة ما إذا كان كائن ما قابلًا للتكرار. يعتمد التابع <code>?duplicable</code> على القائمة الثابتة (hard-coded list) أعلاه لكنه أسرع بكثير من <code>rescue</code>. استخدمه فقط إن تيقنت أن القائمة الثابتة كافية في حالة استخدامك.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/object/duplicable.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/duplicable.rb active_support/core_ext/object/duplicable.rb].
  
 
=== التابع <code>deep_dup</code> ===
 
=== التابع <code>deep_dup</code> ===
سطر 153: سطر 153:
 
duplicate = number.deep_dup
 
duplicate = number.deep_dup
 
number.object_id == duplicate.object_id  # => true
 
number.object_id == duplicate.object_id  # => true
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/deep_dup.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/deep_dup.rb active_support/core_ext/object/deep_dup.rb].
  
 
=== التابع <code>try</code> ===
 
=== التابع <code>try</code> ===
سطر 178: سطر 178:
 
@number.try(:nest)  # => nil
 
@number.try(:nest)  # => nil
 
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
 
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/try.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/try.rb active_support/core_ext/object/try.rb].
  
 
=== التابع <code>class_eval(*args, &block)‎</code> ===
 
=== التابع <code>class_eval(*args, &block)‎</code> ===
سطر 195: سطر 195:
 
end
 
end
  
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/kernel/singleton_class.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/kernel/singleton_class.rb active_support/core_ext/kernel/singleton_class.rb].
  
 
=== التابع <code>(acts_like?(duck</code> ===
 
=== التابع <code>(acts_like?(duck</code> ===
سطر 203: سطر 203:
 
</syntaxhighlight>وهي مجرّد علامة، إذ شكله والقيمة التي يعيدها غير ذي صلة. بعد ذلك، تستطيع تعليمات العميل البرمجيّة (client code) الاستعلام للتحقّق من أمان النوع (duck type safeness) بهذه الطريقة:<syntaxhighlight lang="rails">
 
</syntaxhighlight>وهي مجرّد علامة، إذ شكله والقيمة التي يعيدها غير ذي صلة. بعد ذلك، تستطيع تعليمات العميل البرمجيّة (client code) الاستعلام للتحقّق من أمان النوع (duck type safeness) بهذه الطريقة:<syntaxhighlight lang="rails">
 
some_klass.acts_like?(:string)
 
some_klass.acts_like?(:string)
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/acts_like.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/acts_like.rb active_support/core_ext/object/acts_like.rb].
  
 
=== التابع <code>to_param</code> ===
 
=== التابع <code>to_param</code> ===
سطر 230: سطر 230:
 
</syntaxhighlight>'''تحذير''': يجب أن تكون وحدات التحكّم على دراية بأية إعادة تعريف للتابع <code>to_param</code> لأنه عند قدوم طلب مثل المثال السابق، ستكون السلسلة "‎357-john-smith" هي قيمة <code>params[:id]</code>‎.
 
</syntaxhighlight>'''تحذير''': يجب أن تكون وحدات التحكّم على دراية بأية إعادة تعريف للتابع <code>to_param</code> لأنه عند قدوم طلب مثل المثال السابق، ستكون السلسلة "‎357-john-smith" هي قيمة <code>params[:id]</code>‎.
  
'''ملاحظة''': مُعرّف active_support/core_ext/object/to_param.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/to_param.rb active_support/core_ext/object/to_param.rb].
  
 
=== التابع <code>to_query</code> ===
 
=== التابع <code>to_query</code> ===
سطر 255: سطر 255:
 
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
 
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
  
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/to_query.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/to_query.rb active_support/core_ext/object/to_query.rb].
  
 
=== التابع <code>With_options</code> ===
 
=== التابع <code>With_options</code> ===
سطر 283: سطر 283:
 
</syntaxhighlight>بما أنّ <code>with_options</code> يعيد توجيه الاستدعاءات إلى مُستقبِلها، فيمكن أن تكون متداخلة. يدمج كل مستوى تشعب إعداداته الافتراضية الموروثة بالإضافة إلى قيمه الافتراضية الخاصة.
 
</syntaxhighlight>بما أنّ <code>with_options</code> يعيد توجيه الاستدعاءات إلى مُستقبِلها، فيمكن أن تكون متداخلة. يدمج كل مستوى تشعب إعداداته الافتراضية الموروثة بالإضافة إلى قيمه الافتراضية الخاصة.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/object/with_options.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/with_options.rb active_support/core_ext/object/with_options.rb].
  
 
=== دعم JSON ===
 
=== دعم JSON ===
يُوفّر الدعم الفعَّال تعريف استخدام (implementation) أفضل للتابع <code>to_json</code> من جوهرة json (أي json gem) التي تُوفّر عادةً كائنات [[Ruby|روبي]]. وذلك لأن بعض الأصناف، مثل <code>[[Ruby/Hash|Hash]]</code> و <code>[[Ruby/OrderedHash|OrderedHash]]</code> و <code>[[Ruby/Process::Status|Process::Status]]</code> تحتاج لمعالجة خاصّة كي تُوفّر تمثيل JSON مناسب.
+
يُوفّر Active Support تعريف استخدام (implementation) أفضل للتابع <code>to_json</code> من جوهرة json (أي json gem) التي تُوفّر عادةً كائنات [[Ruby|روبي]]. وذلك لأن بعض الأصناف، مثل <code>[[Ruby/Hash|Hash]]</code> و <code>[[Ruby/OrderedHash|OrderedHash]]</code> و <code>[[Ruby/Process::Status|Process::Status]]</code> تحتاج لمعالجة خاصّة كي تُوفّر تمثيل JSON مناسب.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/object/json.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/json.rb active_support/core_ext/object/json.rb].
  
 
=== متغيرات النسخة (Instance Variables) ===
 
=== متغيرات النسخة (Instance Variables) ===
يُوفّر الدعم الفعال (Active Support) عدّة توابع لتيسير الوصول إلى متغيرات النسخة.
+
يُوفّر Active Support عدّة توابع لتيسير الوصول إلى متغيرات النسخة.
  
 
==== <code>Instance_values</code> ====
 
==== <code>Instance_values</code> ====
سطر 302: سطر 302:
 
   
 
   
 
C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
 
C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/instance_variables.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/instance_variables.rb active_support/core_ext/object/instance_variables.rb].
  
 
==== <code>Instance_variable_names</code> ====
 
==== <code>Instance_variable_names</code> ====
سطر 313: سطر 313:
 
   
 
   
 
C.new(0, 1).instance_variable_names # => ["@x", "@y"]
 
C.new(0, 1).instance_variable_names # => ["@x", "@y"]
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/object/instance_variables.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/instance_variables.rb active_support/core_ext/object/instance_variables.rb].
  
 
=== إسكات التحذيرات والاستثناءات ===
 
=== إسكات التحذيرات والاستثناءات ===
 
يُغيّر التابعان <code>silence_warnings</code> و <code>enable_warnings</code> قيمة <code>VERBOSE$</code> كما يُناسب خلال فترة الكتلة الخاصة بهما ثم يعيدان ضبطها (reset) بعد ذلك:<syntaxhighlight lang="rails">
 
يُغيّر التابعان <code>silence_warnings</code> و <code>enable_warnings</code> قيمة <code>VERBOSE$</code> كما يُناسب خلال فترة الكتلة الخاصة بهما ثم يعيدان ضبطها (reset) بعد ذلك:<syntaxhighlight lang="rails">
 
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
 
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
</syntaxhighlight>إسكات الاستثناءات مُمكن أيضًا مع <code>suppress</code>. يتلقى هذا التابع عددًا عشوائيًّا من أصناف الاستثناءات. إن رُفع استثناءٌ أثناء تنفيذ الكتلة وكان <code>?kind_of</code> أيًّا من الوسائط، فسيلتقطها <code>suppress</code> ويرد بصمت. خلا ذلك، لن يُلتقَط الاستثناء:<syntaxhighlight lang="rails">
+
</syntaxhighlight>إسكات الاستثناءات مُمكن أيضًا مع <code>suppress</code>. يتلقى هذا التابع عددًا عشوائيًّا من أصناف الاستثناءات. إن رُفع استثناءٌ أثناء تنفيذ الكتلة وكان <code>?kind_of</code> أيًّا من الوسائط، فسيلتقطها <code>suppress</code> ويعيد بصمت. خلا ذلك، لن يُلتقَط الاستثناء:<syntaxhighlight lang="rails">
 
# ًإن قُفِل المُستخدم يُفقد مُعامل الزيادة لكن هذا ليس مشكلة
 
# ًإن قُفِل المُستخدم يُفقد مُعامل الزيادة لكن هذا ليس مشكلة
 
suppress(ActiveRecord::StaleObjectError) do
 
suppress(ActiveRecord::StaleObjectError) do
سطر 325: سطر 325:
  
  
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/kernel/reporting.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/kernel/reporting.rb active_support/core_ext/kernel/reporting.rb].
  
 
=== التابع <code>?in</code> ===
 
=== التابع <code>?in</code> ===
سطر 335: سطر 335:
 
25.in?(30..50)      # => false
 
25.in?(30..50)      # => false
 
1.in?(1)            # => ArgumentError
 
1.in?(1)            # => ArgumentError
</syntaxhighlight>مُعرّف في active_support/core_ext/object/inclusion.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/inclusion.rb active_support/core_ext/object/inclusion.rb].
  
== من ملحقات إلى وحدة (Extensions to Module) ==
+
== ملحقات للوحدات ==
  
=== الخاصيّات (Attributes) ===
+
=== الخاصيات (Attributes) ===
  
 
==== التابع <code>Alias_attribute</code> ====
 
==== التابع <code>Alias_attribute</code> ====
سطر 350: سطر 350:
  
  
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/module/aliasing.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/aliasing.rb active_support/core_ext/module/aliasing.rb].
  
 
==== الخاصيّات الداخلية ====
 
==== الخاصيّات الداخلية ====
 
يصبح تضارب الأسماء محتملًا وخطرًا عندما تُحدّد خاصيّة في صنف من المفترض أن يكون فرعيًّا. هذا مهم بشكل خاص للمكتبات.
 
يصبح تضارب الأسماء محتملًا وخطرًا عندما تُحدّد خاصيّة في صنف من المفترض أن يكون فرعيًّا. هذا مهم بشكل خاص للمكتبات.
  
يُعرّف الدعم الفعال وحدات الماكرو <code>attr_internal_reader</code> و <code>attr_internal_writer</code> و <code>attr_internal_accessor</code>. وهم يتصرّفون مثل نظرائهم <code>*_attr</code> في روبي باستثناء أن تسميتهم لمُتغيّر النسخة الضمنيّة تجعل التضارب أقلّ احتمالًا.
+
يُعرّف [[Rails/active support|Active Support]] وحدات الماكرو <code>attr_internal_reader</code> و <code>attr_internal_writer</code> و <code>attr_internal_accessor</code>. وهم يتصرّفون مثل نظرائهم <code>*_attr</code> في روبي باستثناء أن تسميتهم لمُتغيّر النسخة الضمنيّة تجعل التضارب أقلّ احتمالًا.
  
 
يُعد الماكرو <code>attr_internal</code> مرادفًا لـ <code>attr_internal_accessor</code>:<syntaxhighlight lang="rails">
 
يُعد الماكرو <code>attr_internal</code> مرادفًا لـ <code>attr_internal_accessor</code>:<syntaxhighlight lang="rails">
سطر 379: سطر 379:
 
   end
 
   end
 
end
 
end
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/module/attr_internal.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/attr_internal.rb active_support/core_ext/module/attr_internal.rb].
  
 
==== خصائص الوحدة ====
 
==== خصائص الوحدة ====
سطر 399: سطر 399:
 
   end
 
   end
 
end
 
end
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/module/attribute_accessors.rb.
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb active_support/core_ext/module/attribute_accessors.rb].
  
 
=== الآباء ===
 
=== الآباء ===
سطر 419: سطر 419:
 
'''تحذير''': لاحظ أنه في هذه الحالة، يعيد <code>parent_name</code> القيمة <code>nil</code>.
 
'''تحذير''': لاحظ أنه في هذه الحالة، يعيد <code>parent_name</code> القيمة <code>nil</code>.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/module/introspection.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/introspection.rb active_support/core_ext/module/introspection.rb].
  
 
==== التابع <code>parent_name</code> ====
 
==== التابع <code>parent_name</code> ====
سطر 437: سطر 437:
 
'''تحذير''': لاحظ أن التابع <code>parent</code> في هذه الحالة يعيد <code>Object</code>.
 
'''تحذير''': لاحظ أن التابع <code>parent</code> في هذه الحالة يعيد <code>Object</code>.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/module/introspection.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/introspection.rb active_support/core_ext/module/introspection.rb].
  
 
==== التابع <code>parents</code> ====
 
==== التابع <code>parents</code> ====
سطر 451: سطر 451:
 
X::Y::Z.parents # => [X::Y, X, Object]
 
X::Y::Z.parents # => [X::Y, X, Object]
 
M.parents      # => [X::Y, X, Object]
 
M.parents      # => [X::Y, X, Object]
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/module/intros·
+
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/introspection.rb active_support/core_ext/module/introspection.rb].
  
 
=== الوحدات المجهولة ===
 
=== الوحدات المجهولة ===
سطر 479: سطر 479:
 
</syntaxhighlight>رغم أن الوحدة المجهولة هي وحدة مستحيلة الوصول حسب التعريف.
 
</syntaxhighlight>رغم أن الوحدة المجهولة هي وحدة مستحيلة الوصول حسب التعريف.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/module/anonymous.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/anonymous.rb active_support/core_ext/module/anonymous.rb].
  
 
=== تفويض التوابع ===
 
=== تفويض التوابع ===
سطر 533: سطر 533:
 
</syntaxhighlight>في المثال السابق يُنشئ الماكرو <code>avatar_size</code> بدلًا من <code>size</code>.
 
</syntaxhighlight>في المثال السابق يُنشئ الماكرو <code>avatar_size</code> بدلًا من <code>size</code>.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/module/delegation.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/delegation.rb active_support/core_ext/module/delegation.rb].
  
 
==== <code>delegate_missing_to</code> ====
 
==== <code>delegate_missing_to</code> ====
سطر 544: سطر 544:
 
</syntaxhighlight>يمكن أن يكون الهدف أي شيء قابل للنداء داخل الكائن، على سبيل المثال متغيرات النسخة (instance)، والتوابع، والثوابت (constants)، إلخ. تُفوّض توابع الهدف العامّة فقط.
 
</syntaxhighlight>يمكن أن يكون الهدف أي شيء قابل للنداء داخل الكائن، على سبيل المثال متغيرات النسخة (instance)، والتوابع، والثوابت (constants)، إلخ. تُفوّض توابع الهدف العامّة فقط.
  
'''ملاحظة''': مُعرّف في active_support/core_ext/module/delegation.rb.
+
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/delegation.rb active_support/core_ext/module/delegation.rb].
  
 
=== إعادة تعريف التوابع ===
 
=== إعادة تعريف التوابع ===
سطر 555: سطر 555:
 
'''ملاحظة''': مُعرّف في active_support/core_ext/module/redefine_method.rb.
 
'''ملاحظة''': مُعرّف في active_support/core_ext/module/redefine_method.rb.
  
 +
== ملحقات للأصناف ==
 +
 +
=== خاصيات الصنف ===
 +
 +
==== التابع <code>class_attribute</code> ====
 +
يُصرّح (declare) التابع <code>class_attribute</code> عن واحدة أو أكثر من خاصيّات الصنف القابلة للوراثة التي يمكن إعادة تعريفها (override) في أي مستوى بالتسلسل الهرمي.<syntaxhighlight lang="rails">
 +
class A
 +
  class_attribute :x
 +
end
 +
 +
class B < A; end
 +
 +
class C < B; end
 +
 +
A.x = :a
 +
B.x # => :a
 +
C.x # => :a
 +
 +
B.x = :b
 +
A.x # => :a
 +
C.x # => :b
 +
 +
C.x = :c
 +
A.x # => :a
 +
B.x # => :b
 +
</syntaxhighlight>على سبيل المثال، يُعرّف <code>ActionMailer::Base</code>:<syntaxhighlight lang="rails">
 +
class_attribute :default_params
 +
self.default_params = {
 +
  mime_version: "1.0",
 +
  charset: "UTF-8",
 +
  content_type: "text/plain",
 +
  parts_order: [ "text/plain", "text/enriched", "text/html" ]
 +
}.freeze
 +
</syntaxhighlight>كما يمكن الوصول إليها وإعادة تعريفها على مستوى النسخة (instance).<syntaxhighlight lang="rails">
 +
A.x = 1
 +
 +
a1 = A.new
 +
a2 = A.new
 +
a2.x = 2
 +
 +
a1.x # => 1, comes from A
 +
a2.x # => 2, overridden in a2
 +
</syntaxhighlight>يمكن منع توليد تابع نسخة الكاتب (writer instance) بضبط الخيار <code>instance_writer:</code> إلى القيمة <code>false</code>.<syntaxhighlight lang="rails">
 +
module ActiveRecord
 +
  class Base
 +
    class_attribute :table_name_prefix, instance_writer: false, default: "my"
 +
  end
 +
end
 +
</syntaxhighlight>قد يجد نموذجٌ ما هذا الخيار مفيدًا كطريقة لمنع الإسناد الجماعي (mass-assignment) من تعيين الخاصيّة.
 +
 +
يمكن منع توليد تابع نسخة القارئ (writer instance) بضبط الخيار <code>instance_writer:</code> إلى القيمة <code>false</code>.<syntaxhighlight lang="rails">
 +
class A
 +
  class_attribute :x, instance_reader: false
 +
end
 +
 +
A.new.x = 1
 +
A.new.x # NoMethodError
 +
</syntaxhighlight>للتبسيط، يُعرّف <code>class_attribute</code> أيضًا تابع نُسخة خبري (instance predicate) وهو النفي المزدوج لما يعيده نسخة القارئ. في الأمثلة المذكورة أعلاه سوف، أطلقنا عليه <code>?x</code>.
 +
 +
عندما تكون قيمة الخيار <code>instance_reader:</code> القيمة <code>false</code>، يعيد تابع النسخة الخبري الخطأ <code>[[Ruby/NoMethodError|NoMethodError]]</code> مثل تابع القارئ تمامّا.
 +
 +
إن لم ترغب في استعمال تابع النسخة الخبري، مرّر <code>instance_predicate: false</code> ولن يُعرَّف آنذاك.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/class/attribute.rb active_support/core_ext/class/attribute.rb].
 +
 +
=== التوابع <code>cattr_reader</code>، و <code>cattr_writer</code>، و <code>cattr_accessor</code> ===
 +
تُماثل توابع الماكرو <code>cattr_reader</code> و <code>cattr_writer</code> و <code>cattr_accessor</code> نظيراتها <code>*_attr</code> ولكن للأصناف. ويهيؤون (initialize) متغيّر صنف بالقيمة <code>nil</code> إن لم يكن موجودًا بالفعل ويُولّدون تابع الصنف المناسب للوصول إليه:<syntaxhighlight lang="rails">
 +
class MysqlAdapter < AbstractAdapter
 +
  # @@emulate_booleans توليد توابع صنف للوصول إلى
 +
  cattr_accessor :emulate_booleans, default: true
 +
end
 +
</syntaxhighlight>تُنشأ أيضًا توابع النسخة للتبسيط التي هي مجرّد وكلاء (proxies) للخاصية <code>class</code>. لذلك، يمكن للنُسخ تغيير الخاصيّة <code>class</code>، لكن لا يمكنهم إعادة تعريفها كما يحدث مع <code>class_attribute</code> (انظر أعلاه). خذ هذا المثال:<syntaxhighlight lang="rails">
 +
module ActionView
 +
  class Base
 +
    cattr_accessor :field_error_proc, default: Proc.new { ... }
 +
  end
 +
end
 +
</syntaxhighlight>يمكننا الوصول إلى <code>field_error_proc</code> في العروض (views).<syntaxhighlight lang="rails">
 +
class MysqlAdapter < AbstractAdapter
 +
  # true مع القيمة الافتراضية @@emulate_booleans توليد توابع صنف للوصول إلى
 +
  cattr_accessor :emulate_booleans, default: true
 +
end
 +
</syntaxhighlight>يمكن منع توليد تابع نسخة القارئ عبر ضبط <code>instance_reader:</code> إلى القيمة <code>false</code> ومنع توليد تابع نسخة الكاتب عبر ضبط <code>instance_writer:</code> إلى القيمة <code>false</code>. يمكن منع توليد كلا التابعين عبر ضبط <code>instance_accessor:</code> إلى القيمة <code>false</code>. يجب أن تكون القيمة <code>false</code> تحديدًا في جميع الحالات وليس أي قيمة خطأ تقيَّم إلى <code>false</code>.<syntaxhighlight lang="rails">
 +
module A
 +
  class B
 +
    # first_name لن تولد نسخة القارئ
 +
    cattr_accessor :first_name, instance_reader: false
 +
    # last_name= لن تولد نسخة الكاتب
 +
    cattr_accessor :last_name, instance_writer: false
 +
    # surname= أو نسخة الكاتبsurname لن تولد نسخة القارئ
 +
    cattr_accessor :surname, instance_accessor: false
 +
  end
 +
end
 +
</syntaxhighlight>قد يجد النموذج أنه من المفيد ضبط <code>instance_accessor:</code> إلى القيمة <code>false</code> كطريقة لمنع الإسناد الجماعي (mass-assignment) من تعيين الخاصيّة.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb active_support/core_ext/module/attribute_accessors.rb].
 +
 +
=== الأصناف الفرعية والسليلة ===
 +
 +
==== التابع <code>subclasses</code> ====
 +
يعيد التابع <code>subclasses</code> الأصناف الفرعية للمستقبل:<syntaxhighlight lang="rails">
 +
class C; end
 +
C.subclasses # => []
 +
 +
class B < C; end
 +
C.subclasses # => [B]
 +
 +
class A < B; end
 +
C.subclasses # => [B]
 +
 +
class D < C; end
 +
C.subclasses # => [B, D]
 +
</syntaxhighlight>الترتيب الذي تعاد وفقه هذه الأصناف غير محدّد.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/class/subclasses.rb active_support/core_ext/class/subclasses.rb].
 +
 +
==== التابع <code>descendants</code> ====
 +
يعيد التابع <code>descendants</code> جميع الأصناف المنحدرة (<code>></code>) من المستقبل:<syntaxhighlight lang="rails">
 +
class C; end
 +
C.descendants # => []
 +
 +
class B < C; end
 +
C.descendants # => [B]
 +
 +
class A < B; end
 +
C.descendants # => [B, A]
 +
 +
class D < C; end
 +
C.descendants # => [B, A, D]
 +
</syntaxhighlight>الترتيب الذي تعاد وفقه هذه الأصناف غير محدّد.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/class/subclasses.rb active_support/core_ext/class/subclasses.rb].
 +
 +
== ملحقات للسلاسل النصية ==
 +
 +
=== سلامة الخَرج (Output Safety) ===
 +
 +
==== الدافع (Motivation) ====
 +
يحتاج إدخال البيانات إلى قوالب [[HTML]] عنايةً إضافيّة. لا تستطيع مثلًا حشر <code>review.title@</code> حرفيًا بصفحة [[HTML]]. فلو كان العنوان مثلًا "!Flanagan & Matz rules"، لن تكون تركيبة الخَرج صحيحة لأنه من الضروري تهريب حرف العطف <code>&</code> بهذا الشكل ";amp&". زيادةً على ذلك، قد يكون هذا ثغرة أمنيّة كبيرة لأنه من الممكن للمُستخدمين حقن شيفرة [[HTML]] خبيثة تضع عنوانًا آخر للصفحة.
 +
 +
ألقِ نظرة على القسم "البرمجة عبر المواقع" في دليل [[Rails/security|تأمين تطبيقات ريلز]] لمزيد من المعلومات حول المخاطر.
 +
 +
==== السلاسل النصية الآمنة ====
 +
يمتلك [[Rails/active support|Active Support]] مفهوم السلاسل الآمنة (html). السلسلة الآمنة هي التي تُعلَّم على أنها قابلة للإدراج في شيفرة HTML كما هي، إذ هي موثوقة بغض النظر عما إذا كانت مُهرّبة أم لا.
 +
 +
تعتبر السلاسل النصيّة غير آمنة افتراضيًّا:<syntaxhighlight lang="rails">
 +
"".html_safe? # => false
 +
</syntaxhighlight>تستطيع الحصول على سلسلة آمنة من أخرى عاديّة باستعمال التابع <code>html_safe</code>:<syntaxhighlight lang="rails">
 +
s = "".html_safe
 +
s.html_safe? # => true
 +
</syntaxhighlight>من المهم أن نفهم أن <code>html_safe</code> لا يُهرّب أي شيء على الإطلاق بل هو مجرد تأكيد:<syntaxhighlight lang="rails">
 +
s = "<script>...</script>".html_safe
 +
s.html_safe? # => true
 +
s            # => "<script>...</script>"
 +
</syntaxhighlight>تقع على عاتقك مسؤولية التأكد من أن استدعاء <code>html_safe</code> على سلسلة معيّنة أمر جيّد ولا يحمل أية مخاطر.
 +
 +
إن ألحقت (append) شيئًا بسلسلة آمنة إمّا بمكان مُحدّد مع <code>[[Ruby/String/3C-3C|>>]]</code>/<code>[[Ruby/String/concat|concat]]</code> أو مع <code>[[Ruby/String/2B|+]]</code>، فإن النتيجة هي سلسلة آمنة. تُهرّب الوسائط غير الآمنة:<syntaxhighlight lang="rails">
 +
"".html_safe + "<" # => "&lt;"
 +
</syntaxhighlight>تُلحق الوسائط الآمنة مباشرة:<syntaxhighlight lang="rails">
 +
"".html_safe + "<".html_safe # => "<"
 +
 +
</syntaxhighlight>يجب عدم استخدام هذه التوابع في [[Rails/action view overview|العروض]] العادية. تُهرّب القيم غير الآمنة تلقائيًا:<syntaxhighlight lang="html">
 +
<%= @review.title %> <%# fine, escaped if needed %>
 +
</syntaxhighlight>لإدراج شيء ما حرفيًا، استخدم المُساعد <code>raw</code> بدل استدعاء <code>html_safe</code>:<syntaxhighlight lang="html">
 +
<%= raw @cms.current_template %> <%# inserts @cms.current_template as is %>
 +
</syntaxhighlight>أو بطريقة أخرى مكافئة، استخدم <code>‎<%==‎</code>:<syntaxhighlight lang="html">
 +
<%== @cms.current_template %> <%# inserts @cms.current_template as is %>
 +
</syntaxhighlight>يستدعي المُساعد <code>raw</code> التابع <code>html_safe</code> بدلًا منك:<syntaxhighlight lang="rails">
 +
def raw(stringish)
 +
  stringish.to_s.html_safe
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/string/output_safety.rb.
 +
 +
==== التحوّل (Transformation) ====
 +
كقاعدة عامة وباستثناء عملية الدمج (concatenation) كما هو موضّح أعلاه، أي تابع يستطيع تغيير [[Ruby/String|سلسلة نصيّة]] يعطيك سلسلة غير آمنة. وهم <code>[[Ruby/String/downcase|downcase]]</code>، و <code>[[Ruby/String/gsub|gsub]]</code>، و <code>[[Ruby/String/strip|strip]]</code>، و <code>[[Ruby/String/chomp|chomp]]</code>، و <code>underscore</code> ...إلخ.
 +
 +
في حالة التحوّلات في نفس المكان مثل <code>[[Ruby/String/gsub-21|!gsub]]</code>، يصبح المستقبل نفسه غير آمن.
 +
 +
'''تنويه''': يُفقد بت (bit) السلامة دائمًا بغض النظر عن تغيير التحويل لأي شيء أم لا.
 +
 +
==== التحويل والتحويل بالإكراه (Conversion and Coercion) ====
 +
التحويل باستدعاء <code>[[Ruby/String/to s|to_s]]</code> على سلسلة نصيّة آمنة يعيد سلسلة نصيّة آمنة ولكن التحويل بالإكراه عبر التابع <code>[[Ruby/String/to str|to_str]]</code> يعيد سلسلة غير آمنة.
 +
 +
==== النسخ ====
 +
استدعاء <code>dup</code> أو <code>clone</code> على سلاسل آمنة ينتج سلاسل آمنة.
 +
 +
=== التابع <code>remove</code> ===
 +
يحذف التابع <code>remove</code> جميع تكرارات النمط:<syntaxhighlight lang="rails">
 +
"Hello World".remove(/Hello /) # => "World"
 +
</syntaxhighlight>توجد أيضًا النسخة المدمرة من هذا التابع هي <code>!String.remove</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/filters.rb active_support/core_ext/string/filters.rb].
 +
 +
=== التابع <code>squish</code> ===
 +
يزيل التابع <code>squish</code> المسافات البيضاء الزائدة السابقة واللاحقة ويستبدل المسافات البيضاء بين الكلمات بفراغ واحدة:<syntaxhighlight lang="rails">
 +
" \n  foo\n\r \t bar \n".squish # => "foo bar"
 +
</syntaxhighlight>توجد أيضًا نسخة مدمرة هي <code>!String.squish</code>.
 +
 +
لاحظ أنه يُعالج كل من مسافات ASCII و Unicode البيضاء.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/filters.rb active_support/core_ext/string/filters.rb].
 +
 +
=== التابع <code>truncate</code> ===
 +
يعيد التابع <code>truncate</code> نسخة (copy) مقطوعة (truncated) من مستقبلها بعد طول معيّن (يحدد بالمعامل <code>length</code>):<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate(20)
 +
# => "Oh dear! Oh dear!..."
 +
</syntaxhighlight>يمكن تخصيص النقط الثلاث (Ellipsis) التي توضع بدل الكلام المقتطع باستخدام الخيار <code>omission:</code>:<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '&hellip;')
 +
# => "Oh dear! Oh &hellip;"
 +
</syntaxhighlight>لاحظ خاصّة أنَّ الاقتطاع يأخذ طول سلسلة الحذف (omission string) في الحُسبان.
 +
 +
مرّر الخيار <code>separator:</code> لاقتطاع السلسلة في فاصل طبيعي:<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate(18)
 +
# => "Oh dear! Oh dea..."
 +
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: ' ')
 +
# => "Oh dear! Oh..."
 +
</syntaxhighlight>يمكن للخيار <code>separator:</code> أن يكون [[Ruby/Regexp|تعبيرًا نمطيًّا]]:<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
 +
# => "Oh dear! Oh..."
 +
</syntaxhighlight>في الأمثلة أعلاه تُقطع "dear" في البداية ثم يمنع <code>separator:</code> ذلك.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/filters.rb active_support/core_ext/string/filters.rb].
 +
 +
=== التابع <code>truncate_words</code> ===
 +
يعيد التابع <code>truncate_words</code> نسخة من مستقبله بعد اقتطاعه (truncated) عند عدد معيّن من الكلمات:<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate_words(4)
 +
# => "Oh dear! Oh dear!..."
 +
</syntaxhighlight>يمكن تخصيص النقط الثلاث (Ellipsis) التي توضع بدل الكلام المقتطع باستخدام الخيار <code>omission:</code>:<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '&hellip;')
 +
# => "Oh dear! Oh dear!&hellip;"
 +
</syntaxhighlight>مرّر الخيار <code>separator:</code> لاقتطاع السلسلة عند فاصل طبيعي:<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!')
 +
# => "Oh dear! Oh dear! I shall be late..."
 +
</syntaxhighlight>يمكن للخيار <code>separator:</code> أن يكون [[Ruby/Regexp|تعبيرًا نمطيًّا]]:<syntaxhighlight lang="rails">
 +
"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
 +
# => "Oh dear! Oh dear!..."
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/filters.rb active_support/core_ext/string/filters.rb].
 +
 +
=== التابع <code>Inquiry</code> ===
 +
يُحوّل التابع <code>inquiry</code> سلسلة نصيّة إلى كائن من النوع <code>StringInquirer</code> مما يجعل التحقّق من المساواة أجمل.<syntaxhighlight lang="rails">
 +
production".inquiry.production? # => true
 +
"active".inquiry.inactive?      # => false
 +
</syntaxhighlight>
 +
 +
=== التابعان <code>?starts_with</code> و <code>?ends_with</code> ===
 +
يعرّف [[Rails/active support|Active Support]] أسماء ضمير الغائب البديلة من <code>[[Ruby/String/start with-3F|?String.start_with]]</code> و <code>[[Ruby/String/end with-3F|?String#end_with]]</code>:<syntaxhighlight lang="rails">
 +
"foo".starts_with?("f") # => true
 +
"foo".ends_with?("o")  # => true
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/starts_ends_with.rb active_support/core_ext/string/starts_ends_with.rb].
 +
 +
=== التابع <code>strip_heredoc</code> ===
 +
يزيل التابع <code>strip_heredoc</code> المسافات البادئة (indentation) في الصيغة heredoc.
 +
 +
مثلًا:<syntaxhighlight lang="rails">
 +
if options[:usage]
 +
  puts <<-USAGE.strip_heredoc
 +
    This command does such and such.
 +
 +
    Supported options are:
 +
      -h        This message
 +
      ...
 +
  USAGE
 +
end
 +
</syntaxhighlight>سيرى المستخدم رسالة الاستخدام حذو الهامش الأيسر.
 +
 +
تقنيًّا، يبحث التابع عن الخط الأقل إزاحة (least indented) في السلسلة بأكملها ويزيل ذاك المقدار من المسافة البيضاء الزائدة.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/strip.rb active_support/core_ext/string/strip.rb].
 +
 +
=== التابع <code>Indent</code> ===
 +
يضيف مسافة بادئة للأسطر في المستقبل:<syntaxhighlight lang="rails">
 +
<<EOS.indent(2)
 +
def some_method
 +
  some_code
 +
end
 +
EOS
 +
# =>
 +
  def some_method
 +
    some_code
 +
  end
 +
</syntaxhighlight>يحدد الوسيط الثاني <code>indent_string</code> أي مسافة بادئة يُراد استخدامها. القيمة الافتراضيّة هي <code>nil</code> وهي تُخبر التابع أن يُخمّن عبر النظر في أوّل السطر مُزاح بمسافة بادئة والرجوع بمسافة فارغة إن لم يوجد.<syntaxhighlight lang="rails">
 +
"  foo".indent(2)        # => "    foo"
 +
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
 +
"foo".indent(2, "\t")    # => "\t\tfoo"
 +
 +
</syntaxhighlight>بينما يكون <code>indent_string</code> عادةً مسافة واحدة أو مسافة جدولة (tab)، يظل من الممكن أن تكون أي سلسلة.
 +
 +
الوسيط الثالث <code>indent_empty_lines</code> هي علامة تشير إلى ما إذا وجب وضع مسافات بادئة للسطور الفارغة. قيمتها الافتراضيّة هي <code>false</code>.<syntaxhighlight lang="rails">
 +
"foo\n\nbar".indent(2)            # => "  foo\n\n  bar"
 +
"foo\n\nbar".indent(2, nil, true) # => "  foo\n  \n  bar"
 +
 +
</syntaxhighlight>يضع التابع <code>!indent</code> المسافة البادئة في في السلسلة نفسها عبر تعديل المستقبل.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/indent.rb active_support/core_ext/string/indent.rb].
 +
 +
=== الوصول (Access) ===
 +
 +
==== التابع <code>at(position)‎</code> ====
 +
يعيد محرفًا ذا موضع محدَّد (في الموضع <code>position</code>) في السلسلة النصية:<syntaxhighlight lang="rails">
 +
"hello".at(0)  # => "h"
 +
"hello".at(4)  # => "o"
 +
"hello".at(-1) # => "o"
 +
"hello".at(10) # => nil
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/access.rb active_support/core_ext/string/access.rb].
 +
 +
==== التابع <code>from(position)‎</code> ====
 +
يعيد السلسلة الفرعية من السلسلة النصية المعطاة التي تبدأ من الموضع <code>position</code>:<syntaxhighlight lang="rails">
 +
"hello".from(0)  # => "hello"
 +
"hello".from(2)  # => "llo"
 +
"hello".from(-2) # => "lo"
 +
"hello".from(10) # => nil
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في active_support/core_ext/string/access.rb.
 +
 +
==== التابع <code>to(position)‎</code> ====
 +
يعيد السلسلة الفرعية للسلسلة النصية المعطاة حتى الموضع <code>position</code>:<syntaxhighlight lang="rails">
 +
"hello".to(0)  # => "h"
 +
"hello".to(2)  # => "hel"
 +
"hello".to(-2) # => "hell"
 +
"hello".to(10) # => "hello"
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/access.rb active_support/core_ext/string/access.rb].
 +
 +
==== التابع <code>first(limit = 1)‎</code> ====
 +
يُماثل استدعاء التابع <code>str.first(n)‎</code> الاستدعاء <code>str.to(n-1)‎</code> إن كانت <code>n > 0</code> ويعيد سلسلة فارغة عندما تكون <code>n == 0</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/access.rb active_support/core_ext/string/access.rb].
 +
 +
==== التابع <code>last(limit = 1)‎</code> ====
 +
يُماثل استدعاء التابع <code>str.last(n)‎</code> الاستدعاء <code>str.last(n)‎</code> إن كانت <code>n > 0</code> ويعيد سلسلة فارغة لمّا <code>n == 0</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/access.rb active_support/core_ext/string/access.rb].
 +
 +
=== المشتقات (Inflections) ===
 +
 +
==== التابع <code>pluralize</code> ====
 +
يعيد التابع <code>pluralize</code> صيغة جمع (plural) للمستقبل:<syntaxhighlight lang="rails">
 +
"table".pluralize    # => "tables"
 +
"ruby".pluralize      # => "rubies"
 +
"equipment".pluralize # => "equipment"
 +
 +
</syntaxhighlight>كما ترى، يعرف [[Rails/active support|Active Support]] بعض صيغ الجمع الشاذة (irregular plurals) والأسماء غير المعدودة (uncountable nouns). يمكن توسيع القواعد والمفردات في config/initializers/inflections.rb. يُنشَأ هذا الملف بواسطة الأمر <code>rails</code> وبه إرشادات في التعليقات.
 +
 +
يستطيع <code>pluralize</code> أيضًا أن يأخذ معاملًا (parameter) اختياريًا هو <code>count</code>. إن كان <code>count == 1</code>، فستُعاد الصيغة المفردة. بالنسبة لأية قيمة أخرى للمعامل <code>count</code>، ستعاد صيغة الجمع:<syntaxhighlight lang="rails">
 +
"dude".pluralize(0) # => "dudes"
 +
"dude".pluralize(1) # => "dude"
 +
"dude".pluralize(2) # => "dudes"
 +
</syntaxhighlight>يستخدم [[Rails/active record basics|Active Record]] هذه التابع لحساب اسم الجدول الافتراضي الذي يوافق النموذج:<syntaxhighlight lang="rails">
 +
# active_record/model_schema.rb
 +
def undecorated_table_name(class_name = base_class.name)
 +
  table_name = class_name.to_s.demodulize.underscore
 +
  pluralize_table_names ? table_name.pluralize : table_name
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>singularize</code> ====
 +
يسلك هذا التابع سلوكًا معاكسًا للتابع <code>pluralize</code>:<syntaxhighlight lang="rails">
 +
tables".singularize    # => "table"
 +
"rubies".singularize    # => "ruby"
 +
"equipment".singularize # => "equipment"
 +
</syntaxhighlight>تحسب [[Rails/association basics|الارتباطات]] (Associations) اسم الصنف الافتراضي المرتبط باستخدام هذا التابع:<syntaxhighlight lang="rails">
 +
# active_record/reflection.rb
 +
def derive_class_name
 +
  class_name = name.to_s.camelize
 +
  class_name = class_name.singularize if collection?
 +
  class_name
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>camelize</code> ====
 +
يعيد التابع <code>camelize</code> المستقبل في نمط سنام الجمل (CamelCase):<syntaxhighlight lang="rails">
 +
"product".camelize    # => "Product"
 +
"admin_user".camelize # => "AdminUser"
 +
</syntaxhighlight>تستطيع التفكير في هذه التابع باعتباره التابع التي يحوّل المسارات (paths) إلى صنف روبي أو أسماء وحدات (module) حيث تقوم الشرطات المائلة (slashes) بفصل مجالات الأسماء:<syntaxhighlight lang="rails">
 +
"backoffice/session".camelize # => "Backoffice::Session"
 +
</syntaxhighlight>يستخدم [[Rails/action pack|Action Pack]] مثلًا هذا التابع لتحميل الصنف الذي يوفّر مخزن جلسة معيّن:<syntaxhighlight lang="rails">
 +
# action_controller/metal/session_management.rb
 +
def session_store=(store)
 +
  @@session_store = store.is_a?(Symbol) ?
 +
    ActionDispatch::Session.const_get(store.to_s.camelize) :
 +
    store
 +
end
 +
</syntaxhighlight>يأخذ التابع <code>camelize</code> وسيطًا اختياريًّا يمكن أن يكون <code>upper:</code> (الافتراضي) أو <code>lower:</code>. مع القيمة الأخيرة للوسيط، يُصبح الحرف الأول صغيرًا:<syntaxhighlight lang="rails">
 +
"visual_effect".camelize(:lower) # => "visualEffect"
 +
</syntaxhighlight>قد يكون هذا مفيدًا لحساب أسماء التابع باللغات التي تتبع هذا العرف مثل لغة [[JavaScript|جافاسكربت]].
 +
 +
'''تنويه''': تستطيع عمومًا التفكير في <code>camelize</code> كعكس <code>underscore</code> على الرغم من وجود حالات تخالف ذلك:
 +
 +
"SSLError".underscore.camelize يعيد "SslError". لدعم  مثل هذه حالات، يسمح لك [[Rails/active support|Active Support]] بتحديد الاختصارات (acronyms) في config/initializers/inflections.rb:<syntaxhighlight lang="rails">
 +
ActiveSupport::Inflector.inflections do |inflect|
 +
  inflect.acronym 'SSL'
 +
end
 +
 +
"SSLError".underscore.camelize # => "SSLError"
 +
 +
</syntaxhighlight>التابع <code>camelcase</code> هو اسم بديل للتابع <code>camelize</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>underscore</code> ====
 +
يعمل التابع <code>underscore</code> عكس التابع <code>camelcase</code> إذ يأخذ سلسلة مكتوبة بنمط سنام الجمل ويحولها إلى مسار ويفصل الكلمات بشرطة سفلية:<syntaxhighlight lang="rails">
 +
"Product".underscore  # => "product"
 +
"AdminUser".underscore # => "admin_user"
 +
 +
</syntaxhighlight>كما يحوّل "::" أيضًا إلى "/":<syntaxhighlight lang="rails">
 +
"Backoffice::Session".underscore # => "backoffice/session"
 +
</syntaxhighlight>ويفهم السلاسل النصيّة التي تبدأ بالحروف الصغيرة:<syntaxhighlight lang="rails">
 +
"visualEffect".underscore # => "visual_effect"
 +
</syntaxhighlight>لا يقبل <code>underscore</code> أي وسيط رغم ذلك.
 +
 +
يستخدم التحميل التلقائي لأصناف ريلز ووحداته التابع <code>underscore</code> للاستدلال على المسار النسبي بدون امتداد ملف (file extension) والذي من شأنه تحديد ثابت مفقود محدّد:<syntaxhighlight lang="rails">
 +
# active_support/dependencies.rb
 +
def load_missing_constant(from_mod, const_name)
 +
  ...
 +
  qualified_name = qualified_name_for from_mod, const_name
 +
  path_suffix = qualified_name.underscore
 +
  ...
 +
end
 +
</syntaxhighlight>تستطيع افتراض أنَّ التابع <code>underscore</code> كعكس <code>camelize</code> كقاعدة عامة على الرغم من وجود حالات مخالفة لذلك. مثلًا "SSLError".underscore.camelize يعيد "SslError".
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>titleize</code> ====
 +
يحوّل التابع <code>titleize</code> الحروف لحروف كبيرة في المستقبل:<syntaxhighlight lang="rails">
 +
"alice in wonderland".titleize # => "Alice In Wonderland"
 +
"fermat's enigma".titleize    # => "Fermat's Enigma"
 +
</syntaxhighlight>التابع <code>titlecase</code> هو اسم بديل للتابع <code>titleize</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>dasherize</code> ====
 +
يبدل التابع <code>dasherize</code> الشرطات العادية في المستقبل مكان الشرطات السفلية:<syntaxhighlight lang="rails">
 +
"name".dasherize        # => "name"
 +
"contact_data".dasherize # => "contact-data"
 +
</syntaxhighlight>يستخدم مُسَلسِل XML للنماذج (XML serializer of models) هذا التابع لتطبيقه على أسماء العُقد (node names):<syntaxhighlight lang="rails">
 +
# active_model/serializers/xml.rb
 +
def reformat_name(name)
 +
  name = name.camelize if camelize?
 +
  dasherize? ? name.dasherize : name
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>demodulize</code> ====
 +
في حالة الحصول على سلسلة ذات اسم ثابت مُميّز (qualified constant name)، يعيد <code>demodulize</code> اسم الثابت تحديدًا، أي جزأه الأيمن:<syntaxhighlight lang="rails">
 +
"Product".demodulize                        # => "Product"
 +
"Backoffice::UsersController".demodulize    # => "UsersController"
 +
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
 +
"::Inflections".demodulize                  # => "Inflections"
 +
"".demodulize                              # => ""
 +
</syntaxhighlight>يستخدم [[Rails/active record basics|Active Record]] مثلًا هذا التابع لحساب اسم حقل عدّاد مؤقت (counter cache column):<syntaxhighlight lang="rails">
 +
# active_record/reflection.rb
 +
def counter_cache_column
 +
  if options[:counter_cache] == true
 +
    "#{active_record.name.demodulize.underscore.pluralize}_count"
 +
  elsif options[:counter_cache]
 +
    options[:counter_cache]
 +
  end
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>deconstantize</code> ====
 +
في حالة الحصول على سلسلة ذات اسم ثابت مُميّز (qualified constant)، يحذف التابع <code>deconstantize</code> جزئه الأيمن تاركًا، عادةً، اسم حاوي الثابت:<syntaxhighlight lang="rails">
 +
"Product".deconstantize                        # => ""
 +
"Backoffice::UsersController".deconstantize    # => "Backoffice"
 +
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>parameterize</code> ====
 +
يُطبّع التابع <code>parameterize</code> المستقبل بحيث يمكن استخدامه في عناوين URL جميلة.<syntaxhighlight lang="rails">
 +
"John Smith".parameterize # => "john-smith"
 +
"Kurt Gödel".parameterize # => "kurt-godel"
 +
</syntaxhighlight>للحفاظ على حالة السلسلة، اضبط الوسيط <code>preserve_case</code> إلى القيمة <code>true</code>. قيمة <code>preserve_case</code> هي <code>false</code> افتراضيًّا.<syntaxhighlight lang="rails">
 +
"John Smith".parameterize(preserve_case: true) # => "John-Smith"
 +
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"
 +
</syntaxhighlight>أعد تعريف (override) الوسيط <code>separator</code> لاستخدام فاصل مخصّص.<syntaxhighlight lang="rails">
 +
"John Smith".parameterize(separator: "_") # => "john\_smith"
 +
"Kurt Gödel".parameterize(separator: "_") # => "kurt\_godel"
 +
 +
</syntaxhighlight>تُلَف السلسلة النصية الناتجة في نسخة من <code>ActiveSupport::Multibyte::Chars</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>tableize</code> ====
 +
التابع <code>tableize</code> هو <code>underscore</code> يليه <code>pluralize</code>.<syntaxhighlight lang="rails">
 +
"Person".tableize      # => "people"
 +
"Invoice".tableize    # => "invoices"
 +
"InvoiceLine".tableize # => "invoice_lines"
 +
</syntaxhighlight>يعيد <code>tableize</code> اسم الجدول الذي يوافق نموذجًا محددُّا للحالات البسيطة. التطبيق الفعلي داخل [[Rails/active record|Active Record]] ليس <code>tableize</code> مباشرةً بالفعل، لأنه يُجرّد أيضًا اسم الصنف ويفحص بعض الخيارات التي قد تؤثر على السلسلة المعادة.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>classify</code> ====
 +
التابع <code>classify</code> هو عكس <code>tableize</code>، إذ يعطيك اسم الصنف الموافق لاسم الجدول:<syntaxhighlight lang="rails">
 +
"people".classify        # => "Person"
 +
"invoices".classify      # => "Invoice"
 +
"invoice_lines".classify # => "InvoiceLine"
 +
 +
</syntaxhighlight>يفهم التابع أسماء الجداول المُميّزة:<syntaxhighlight lang="rails">
 +
"highrise_production.companies".classify # => "Company"
 +
</syntaxhighlight>لاحظ أن <code>classify</code> يعيد اسم صنف كسلسلة نصية. تستطيع الحصول على كائن الصنف الفعلي باستدعاء <code>constantize</code> عليه، كما سيوضّح لاحقًا.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>constantize</code> ====
 +
يستبين (resolves) التابع <code>constantize</code> تعبير مرجع الثابت (constant reference expression) في المستقبل:<syntaxhighlight lang="rails">
 +
"Integer".constantize # => Integer
 +
 +
module M
 +
  X = 1
 +
end
 +
"M::X".constantize # => 1
 +
</syntaxhighlight>يرفع <code>constantize</code> الخطأ <code>[[Ruby/NameError|NameError]]</code> إن قُيِّمَت السلسلة النصيّة إلى ثابت غير معروف أو إن لم يكن محتواها اسم ثابت صالح.
 +
 +
يبدأ استبيان اسم الثابت بواسطة <code>constantize</code> دائمًا في الكائن <code>Object</code> ذي المستوى الأعلى حتى إن لم توجد البادئة "::".<syntaxhighlight lang="rails">
 +
X = :in_Object
 +
module M
 +
  X = :in_M
 +
 +
  X                # => :in_M
 +
  "::X".constantize # => :in_Object
 +
  "X".constantize  # => :in_Object (!)
 +
end
 +
</syntaxhighlight>لذلك، لا تعادل هذه العمليّة بشكل عام ما كان ليفعله [[Ruby|روبي]] في نفس الموقف لو قُيّم ثابت حقيقي.
 +
 +
تحصل حالات اختبار <code>[[Rails/action mailer basics|Mailer]]</code> على مُرسل البريد تحت الاختبار من اسم صنف الاختبار باستخدام <code>constantize</code>:<syntaxhighlight lang="rails">
 +
# action_mailer/test_case.rb
 +
def determine_default_mailer(name)
 +
  name.sub(/Test$/, '').constantize
 +
rescue NameError => e
 +
  raise NonInferrableMailerError.new(name)
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>humanize</code> ====
 +
يُعدّل التابع <code>humanize</code> اسم خاصيّة لعرضها للمستخدمين النهائيين. وتحديدًا ينفّذ هذه التحويلات:
 +
* تطبيق قواعد تصريف بشرية على الوسيط.
 +
* حذف الشرطات السفلية الأماميّة إن وجدت.
 +
* إزالة اللاحقة "‎_id" إذا كانت موجودة.
 +
* استبدال الشرطات السفلية بمسافات فارغة إن وجدت.
 +
* يُصغّر كل الحروف ما عدا المختصرات (acronyms).
 +
* تكبير حروف أوّل كلمة.
 +
يمكن إيقاف تكبير حروف الكلمة الأولى عبر ضبط الخيار <code>capitalize:</code> إلى القيمة <code>false</code> (القيمة الافتراضية هي <code>true</code>).<syntaxhighlight lang="rails">
 +
"name".humanize                        # => "Name"
 +
"author_id".humanize                    # => "Author"
 +
"author_id".humanize(capitalize: false) # => "author"
 +
"comments_count".humanize              # => "Comments count"
 +
"_id".humanize                          # => "Id"
 +
</syntaxhighlight>إذا عُرفّت "SSL" كاختصار:<syntaxhighlight lang="rails">
 +
'ssl_error'.humanize # => "SSL error"
 +
</syntaxhighlight>يستخدم التابع المساعد <code>full_messages</code> التابع <code>humanize</code> كخطّة احتياطيّة لتضمين أسماء الخاصيّات:<syntaxhighlight lang="rails">
 +
def full_messages
 +
  map { |attribute, message| full_message(attribute, message) }
 +
end
 +
 +
def full_message
 +
  ...
 +
  attr_name = attribute.to_s.tr('.', '_').humanize
 +
  attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
 +
  ...
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
==== التابع <code>foreign_key</code> ====
 +
يعطي التابع <code>foreign_key</code> اسم حقل مفتاح خارجي (foreign key column name) من اسم صنف. لذلك، يحذف الوحدة (demodulizes) ويضيف شرطة سفليّة (underscores) ويضيف "‎_id":<syntaxhighlight lang="rails">
 +
"User".foreign_key          # => "user_id"
 +
"InvoiceLine".foreign_key    # => "invoice_line_id"
 +
"Admin::Session".foreign_key # => "session_id"
 +
</syntaxhighlight>مرّر الوسيط <code>false</code> إن لم تُرد شرطة سفليّة في "id_":<syntaxhighlight lang="rails">
 +
"User".foreign_key(false) # => "userid"
 +
</syntaxhighlight>تستخدم الارتباطات (Association) هذا التابع لاستنتاج المفاتيح الخارجية، إذ يفعل [[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D9.85.D9.84.D9.83.D9.8A.D8.A9 .28has one.29|الارتباط has_one]] و<nowiki/>[[Rails/association basics#.D8.A7.D8.B1.D8.AA.D8.A8.D8.A7.D8.B7 .D8.A7.D9.84.D8.AA.D8.B9.D8.AF.D8.AF.D9.8A.D8.A9 .28has many.29|الارتباط has_many]] مثلًا هذا:<syntaxhighlight lang="rails">
 +
# active_record/associations.rb
 +
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
 +
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/inflections.rb active_support/core_ext/string/inflections.rb].
 +
 +
=== التحويلات (Conversions) ===
 +
 +
==== التوابع <code>to_date</code>، و <code>to_time</code>، و <code>to_datetime</code> ====
 +
تُعدّ التوابع <code>to_date</code> و <code>to_time</code> و <code>to_datetime</code> كأغلفة ملائمة حول <code>Date._parse</code>:<syntaxhighlight lang="rails">
 +
"2010-07-27".to_date              # => Tue, 27 Jul 2010
 +
"2010-07-27 23:37:00".to_time    # => 2010-07-27 23:37:00 +0200
 +
"2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000
 +
</syntaxhighlight>يتلقّى <code>to_time</code> وسيطًا اختياريًّا هو <code>utc:</code> أو <code>local:</code> للإشارة إلى المنطقة الزمنية التي تريد التوقيت حسبها:<syntaxhighlight lang="rails">
 +
"2010-07-27 23:42:00".to_time(:utc)  # => 2010-07-27 23:42:00 UTC
 +
"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200
 +
</syntaxhighlight>الوسيط الافتراضي هو <code>local:</code>.
 +
 +
يرجى الرجوع إلى توثيق <code>Date._parse</code> لمزيد من التفاصيل.
 +
 +
'''تنويه''': تعيد التوابع الثلاثة القيمة <code>nil</code> في حالة كون المستقبل فارغًا.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/string/conversions.rb active_support/core_ext/string/conversions.rb].
 +
 +
== ملحقات للأعداد ==
 +
 +
=== البايتات (Bytes) ===
 +
تستجيب جميع الأرقام لهذه التوابع:<syntaxhighlight lang="rails">
 +
bytes
 +
kilobytes
 +
megabytes
 +
gigabytes
 +
terabytes
 +
petabytes
 +
exabytes
 +
</syntaxhighlight>يُرجعون الكمية المقابلة من البايتات باستخدام عامل تحويل يبلغ 1024:<syntaxhighlight lang="rails">
 +
2.kilobytes  # => 2048
 +
3.megabytes  # => 3145728
 +
3.5.gigabytes # => 3758096384
 +
-4.exabytes  # => -4611686018427387904
 +
 +
</syntaxhighlight>تتخذ الصيغ الفردية أسماءً بديلة، لذلك تستطيع قول:<syntaxhighlight lang="rails">
 +
1.megabyte # => 1048576
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/numeric/bytes.rb active_support/core_ext/numeric/bytes.rb].
 +
 +
=== الوقت ===
 +
يُمكّن <code>Time</code> من استخدام حسابات الوقت والتصريحات، مثل ‎<code>45.minutes + 2.hours + 4.weeks</code>.
 +
 +
تستخدم هذه التوابع <code>Time.advance</code> لحساب التواريخ بدقّة عند استخدام <code>from_now</code>، و <code>ago</code> ...إلخ. بالإضافة إلى إضافة أو طرح نتائجها من كائن وقت <code>Time</code>. مثلًا:<syntaxhighlight lang="rails">
 +
# Time.current.advance(months: 1) يكافئ
 +
1.month.from_now
 +
 +
# Time.current.advance(weeks: 2) يكافئ
 +
2.weeks.from_now
 +
 +
# Time.current.advance(months: 4, weeks: 5) يكافئ
 +
(4.months + 5.weeks).from_now
 +
 +
</syntaxhighlight>'''تحذير''': لفترات وأوقات أخرى، يرجى الاطلاع على قسم ملحقات العدد الصحيح في الأسفل.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/numeric/time.rb active_support/core_ext/numeric/time.rb].
 +
 +
=== التنسيق ===
 +
يُمكّن من تنسيق الأرقام بطرق متنوعة منها:
 +
* إنتاج سلسلة نصيّة تمثِّل رقمًا كرقم هاتف:
 +
<syntaxhighlight lang="rails">
 +
5551234.to_s(:phone)
 +
# => 555-1234
 +
1235551234.to_s(:phone)
 +
# => 123-555-1234
 +
1235551234.to_s(:phone, area_code: true)
 +
# => (123) 555-1234
 +
1235551234.to_s(:phone, delimiter: " ")
 +
# => 123 555 1234
 +
1235551234.to_s(:phone, area_code: true, extension: 555)
 +
# => (123) 555-1234 x 555
 +
1235551234.to_s(:phone, country_code: 1)
 +
# => +1-123-555-1234
 +
</syntaxhighlight>
 +
* إنتاج سلسلة نصية تمثِّل رقمًا كعملة:
 +
<syntaxhighlight lang="rails">
 +
1234567890.50.to_s(:currency)                # => $1,234,567,890.50
 +
1234567890.506.to_s(:currency)                # => $1,234,567,890.51
 +
1234567890.506.to_s(:currency, precision: 3)  # => $1,234,567,890.506
 +
</syntaxhighlight>
 +
* إنتاج سلسلة نصية تمثِّل رقمًا كنسبة مئوية:
 +
<syntaxhighlight lang="rails">
 +
100.to_s(:percentage)
 +
# => 100.000%
 +
100.to_s(:percentage, precision: 0)
 +
# => 100%
 +
1000.to_s(:percentage, delimiter: '.', separator: ',')
 +
# => 1.000,000%
 +
302.24398923423.to_s(:percentage, precision: 5)
 +
# => 302.24399%
 +
</syntaxhighlight>
 +
* إنتاج سلسلة نصية تمثِّل رقمًا في شكل محدّد (delimited form):
 +
<syntaxhighlight lang="rails">
 +
12345678.to_s(:delimited)                    # => 12,345,678
 +
12345678.05.to_s(:delimited)                  # => 12,345,678.05
 +
12345678.to_s(:delimited, delimiter: ".")    # => 12.345.678
 +
12345678.to_s(:delimited, delimiter: ",")    # => 12,345,678
 +
12345678.05.to_s(:delimited, separator: " ")  # => 12,345,678 05
 +
</syntaxhighlight>
 +
* إنتاج سلسلة نصية تمثِّل عددًا مقربًا (number rounded) إلى دقة محدَّدة:
 +
<syntaxhighlight lang="rails">
 +
111.2345.to_s(:rounded)                    # => 111.235
 +
111.2345.to_s(:rounded, precision: 2)      # => 111.23
 +
13.to_s(:rounded, precision: 5)            # => 13.00000
 +
389.32314.to_s(:rounded, precision: 0)      # => 389
 +
111.2345.to_s(:rounded, significant: true)  # => 111
 +
</syntaxhighlight>
 +
* إنتاج سلسلة نصية تمثِّل رقمًا كرقم يمكن قرائته بسهولة:
 +
<syntaxhighlight lang="rails">
 +
123.to_s(:human_size)                  # => 123 Bytes
 +
1234.to_s(:human_size)                # => 1.21 KB
 +
12345.to_s(:human_size)                # => 12.1 KB
 +
1234567.to_s(:human_size)              # => 1.18 MB
 +
1234567890.to_s(:human_size)          # => 1.15 GB
 +
1234567890123.to_s(:human_size)        # => 1.12 TB
 +
1234567890123456.to_s(:human_size)    # => 1.1 PB
 +
1234567890123456789.to_s(:human_size)  # => 1.07 EB
 +
 +
</syntaxhighlight>
 +
* إنتاج سلسلة نصيّة تمثِّل رقمًا في كلمات يمكن للإنسان قراءتها:
 +
<syntaxhighlight lang="rails">
 +
123.to_s(:human)              # => "123"
 +
1234.to_s(:human)              # => "1.23 Thousand"
 +
12345.to_s(:human)            # => "12.3 Thousand"
 +
1234567.to_s(:human)          # => "1.23 Million"
 +
1234567890.to_s(:human)        # => "1.23 Billion"
 +
1234567890123.to_s(:human)    # => "1.23 Trillion"
 +
1234567890123456.to_s(:human)  # => "1.23 Quadrillion"
 +
 +
</syntaxhighlight>'''ملاحظة''': معرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/numeric/conversions.rb active_support/core_ext/numeric/conversions.rb].
 +
 +
== ملحقات للأعداد الصحيحة ==
 +
 +
=== التابع <code>?multiple_of</code> ===
 +
يختبر التابع <code>?multiple_of</code> ما إذا كان العدد الصحيح من مضاعفات الوسيط المُمرَّر إليه:<syntaxhighlight lang="rails">
 +
2.multiple_of?(1) # => true
 +
1.multiple_of?(2) # => false
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/integer/multiple.rb active_support/core_ext/integer/multiple.rb].
 +
 +
=== التابع <code>ordinal</code> ===
 +
يعيد التابع <code>ordinal</code> سلسلة نصيّة لاحقة ترتيبية (ordinal suffix string) تقابل العدد الصحيح للمستقبل:<syntaxhighlight lang="rails">
 +
1.ordinal    # => "st"
 +
2.ordinal    # => "nd"
 +
53.ordinal  # => "rd"
 +
2009.ordinal # => "th"
 +
-21.ordinal  # => "st"
 +
-134.ordinal # => "th"
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/integer/inflections.rb active_support/core_ext/integer/inflections.rb].
 +
 +
=== التابع <code>ordinalize</code> ===
 +
يعيد التابع <code>ordinalize</code> السلسلة الترتيبية المقابلة للعدد الصحيح للمستقبل. بالموازنة بين هذ االتابع والتابع <code>ordinal</code>، لاحظ أن التابع <code>ordinal</code> يعيد اللاحقة النصيّة فقط.<syntaxhighlight lang="rails">
 +
1.ordinalize    # => "1st"
 +
2.ordinalize    # => "2nd"
 +
53.ordinalize  # => "53rd"
 +
2009.ordinalize # => "2009th"
 +
-21.ordinalize  # => "-21st"
 +
-134.ordinalize # => "-134th"
 +
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/integer/inflections.rb active_support/core_ext/integer/inflections.rb].
 +
 +
=== الوقت (Time) ===
 +
يُمكّن الكائن <code>Time</code> من استخدام حسابات وتصريحات الوقت، مثل ‎<code>4.months + 5.years</code>.
 +
 +
تستخدم هذه التوابع <code>Time.advance</code> لحساب التواريخ بدقّة عند استخدام <code>from_now</code>، و <code>ago</code>، ...إلخ. بالإضافة إلى إضافة أو طرح نتائجها من الكائن <code>Time</code>. مثلًا:<syntaxhighlight lang="rails">
 +
# Time.current.advance(months: 1) يكافئ
 +
1.month.from_now
 +
 +
# Time.current.advance(years: 2) يكافئ
 +
2.years.from_now
 +
 +
# Time.current.advance(months: 4, years: 5) يكافئ
 +
(4.months + 5.years).from_now
 +
</syntaxhighlight>'''تحذير''': لفترات أخرى، يرجى الرجوع إلى [[Rails/active support core extensions#.D8.A7.D9.84.D9.88.D9.82.D8.AA|ملحقات الوقت للأعداد]] في الأعلى.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/integer/time.rb active_support/core_ext/numeric/time.rb].
 +
 +
== ملحقات للأعداد العشرية الكبيرة (BigDecimal) ==
 +
 +
=== التابع <code>to_s</code> ===
 +
يُوفّر التابع <code>to_s</code> محدّدًّا افتراضيًّا (default specifier) لـ "F". هذا يعني أن استدعاءً بسيطًا على <code>to_s</code> سيؤدي إلى تمثيل الفاصلة المُتحرّكة (floating point) بدلًا من التدوين الهندسيّة:<syntaxhighlight lang="rails">
 +
BigDecimal(5.00, 6).to_s      # => "5.0"
 +
</syntaxhighlight>وتُدعم أيضًا مُحدّدات الرمز (symbol specifiers):<syntaxhighlight lang="rails">
 +
BigDecimal(5.00, 6).to_s(:db)  # => "5.0"
 +
</syntaxhighlight>لا يزال التدوين الهندسيّ (engineering notation) مدعومًا:<syntaxhighlight lang="rails">
 +
BigDecimal(5.00, 6).to_s("e")  # => "0.5E1"
 +
</syntaxhighlight>
 +
 +
== ملحقات للكائنات القابلة للتعداد (Enumerable) ==
 +
 +
=== التابع <code>sum</code> ===
 +
يضيف التابع <code>sum</code> عناصر قابلة للتعداد:<syntaxhighlight lang="rails">
 +
[1, 2, 3].sum # => 6
 +
(1..100).sum  # => 5050
 +
</syntaxhighlight>تفترض عملية الجمع أن العناصر تستجيب لمعامل الجمع <code>+</code>:<syntaxhighlight lang="rails">
 +
[[1, 2], [2, 3], [3, 4]].sum    # => [1, 2, 2, 3, 3, 4]
 +
%w(foo bar baz).sum            # => "foobarbaz"
 +
{a: 1, b: 2, c: 3}.sum          # => [:b, 2, :c, 3, :a, 1]
 +
</syntaxhighlight>مجموعُ مجموعةٍ فارغة (empty collection) هو صفر افتراضيًّا ولكن هذا قابل للتخصيص:<syntaxhighlight lang="rails">
 +
[].sum    # => 0
 +
[].sum(1) # => 1
 +
</syntaxhighlight>يصبح <code>sum</code> مُكرِّرًا (iterator) إن أعطيت كتلة إذ يمرِّر عناصر المجموعة إليها ثم يجمع القيم التي تعيدها:<syntaxhighlight lang="rails">
 +
(1..5).sum {|n| n * 2 } # => 30
 +
[2, 4, 6, 8, 10].sum    # => 30
 +
</syntaxhighlight>يُمكن تخصيص مجموع مستقبل فارغ بهذا الشكل أيضًا:<syntaxhighlight lang="rails">
 +
[].sum(1) {|n| n**3} # => 1
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/enumerable.rb active_support/core_ext/enumerable.rb].
 +
 +
=== التابع <code>index_by</code> ===
 +
يُولّد التابع <code>index_by</code> [[Ruby/Hash|جدول hash]] لعناصر [[Ruby/Enumerable|كائن قابل للتعداد]] مُفهرسة عبر مفتاح ما.
 +
 +
يتكرّر التابع خلال المجموعة ويُمرّر كل عنصر إلى كتلة. سيعيَّن مفتاح العنصر إلى القيمة التي تعيدها الكتلة:<syntaxhighlight lang="rails">
 +
invoices.index_by(&:number)
 +
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}
 +
</syntaxhighlight>'''تحذير''': يجب أن تكون المفاتيح فريدة في العادة. في حالة أعادت الكتلة القيمة نفسها لعناصر مختلفة، فلن تُبنى مجموعةٌ لهذا المفتاح، إذ سيفوز العنصر الأخير دومًا.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/enumerable.rb active_support/core_ext/enumerable.rb].
 +
 +
=== التابع <code>?many</code> ===
 +
التابع <code>?many</code> هو اختزال للاستدعاء <code>collection.size > 1</code>:<syntaxhighlight lang="html">
 +
<% if pages.many? %>
 +
  <%= pagination_links %>
 +
<% end %>
 +
</syntaxhighlight>يأخذ <code>?many</code> في الحسبان فقط العناصر التي تعيد القيمة <code>true</code> إن أُعطيَت كتلة اختيارية:<syntaxhighlight lang="rails">
 +
@see_more = videos.many? {|video| video.category == params[:category]}
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/enumerable.rb active_support/core_ext/enumerable.rb].
 +
 +
=== التابع <code>?exclude</code> ===
 +
يختبر التابع <code>?exclude</code> الخبري ما إذا كان كائنٌ ما لا ينتمي للمجموعة، إذ هو نفي للتابع <code>?include</code>:<syntaxhighlight lang="rails">
 +
to_visit << node if visited.exclude?(node)
 +
</syntaxhighlight>ملاحظة: مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/enumerable.rb active_support/core_ext/enumerable.rb].
 +
 +
=== التابع <code>without</code> ===
 +
يعيد التابع <code>without</code> نسخة (copy) من [[Ruby/Enumerable|كائن قابل للتعداد]] مع إزالة عناصر محدّدة:<syntaxhighlight lang="rails">
 +
["David", "Rafael", "Aaron", "Todd"].without("Aaron", "Todd") # => ["David", "Rafael"]
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/enumerable.rb active_support/core_ext/enumerable.rb].
 +
 +
=== التابع <code>pluck</code> ===
 +
يعيد التابع <code>pluck</code> مصفوفةً بناءًا على مفتاح معيّن:<syntaxhighlight lang="rails">
 +
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/enumerable.rb active_support/core_ext/enumerable.rb].
 +
 +
== ملحقات للمصفوفات ==
 +
 +
=== الوصول (Accessing) ===
 +
يُحسّن [[Rails/active support|Active Support]] من الواجهة البرمجية للمصفوفات لتيسير طرق معيّنة للوصول إليها. على سبيل المثال، يعيد <code>to</code> مصفوفة فرعيّة (subarray) من العناصر حتّى عنصر ذي فهرس محدَّد:<syntaxhighlight lang="rails">
 +
%w(a b c d).to(2) # => ["a", "b", "c"]
 +
[].to(7)          # => []
 +
</syntaxhighlight>بشكل مشابه، يعيد <code>from</code> الذيل بدءًا من العنصر ذي الفهرس المعطى وحتى نهاية المصفوفة. إذا كان الفهرس المعطى أكبر من طول المصفوفة، فسيعيد التابع مصفوفة فارغة.<syntaxhighlight lang="rails">
 +
%w(a b c d).from(2)  # => ["c", "d"]
 +
%w(a b c d).from(10) # => []
 +
[].from(0)          # => []
 +
</syntaxhighlight>تعيد التوابع <code>second</code> و <code>third</code> و <code>fourth</code> و <code>fifth</code> العنصر المقابل للمعنى الحرفي لها كما يفعل التابعان <code>second_to_last</code> و <code>third_to_last</code> (التابعان <code>first</code> و <code>last</code> مُدمجان [built-in]) نفس الأمر أيضًا. بفضل الحكمة الاجتماعية (social wisdom) والعمل البنّاء الإيجابي في كل مكان، صار التابع <code>forty_two</code> متوافّرًا أيضًا.<syntaxhighlight lang="rails">
 +
%w(a b c d).third # => "c"
 +
%w(a b c d).fifth # => nil
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/access.rb active_support/core_ext/array/access.rb].
 +
 +
=== إضافة عناصر ===
 +
 +
==== التابع <code>prepend</code> ====
 +
هذا التابع هو اسم بديل للتابع <code>[[Ruby/Array/unshift|Array.unshift]]</code>.<syntaxhighlight lang="rails">
 +
%w(a b c d).prepend('e')  # => ["e", "a", "b", "c", "d"]
 +
[].prepend(10)            # => [10]
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb active_support/core_ext/array/prepend_and_append.rb].
 +
 +
==== التابع <code>append</code> ====
 +
هذا التابع هو اسم بديل للمعامل <code>[[Ruby/Array/append operator|>>#Array]]</code>.<syntaxhighlight lang="rails">
 +
%w(a b c d).append('e')  # => ["a", "b", "c", "d", "e"]
 +
[].append([1,2])        # => [[1, 2]]
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb active_support/core_ext/array/prepend_and_append.rb].
 +
 +
=== استخراج الخيارات ===
 +
يسمح لك [[Ruby|روبي]] بحذف الأقواس المربعة عندما يكون الوسيط الأخير في استدعاء تابع [[Ruby/Hash|جدول Hash]] باستثناء حالة الوسيط <code>block&</code>:<syntaxhighlight lang="rails">
 +
User.exists?(email: params[:email])
 +
</syntaxhighlight>يستخدم هذا التحسين في الصياغة كثيرًا في ريلز لتجنب الوسائط الموضعيّة حيث تكثر عن اللازم بكثير. ويقدّم بدل ذلك واجهات تحاكي المُعاملات المسماة (named parameters). من المفيد جدًا استخدام [[Ruby/Hash|جدول Hash]] زائد (trailing hash) للخيارات.
 +
 +
ولكن إن كان التابع يتوقّع عددًا متغيرًا من الوسائط ويستخدم <code>*</code> في تصريحه، فيصبح مثل [[Ruby/Hash|الجدول Hash]] من الخيارات هذا في النهاية عنصرًا من مصفوفة الوسائط حيث يفقد دوره.
 +
 +
تستطيع في هذه الحالات إعطاء جدول الخيارات Hash معاملة مميّزة مع <code>!extract_options</code>. يتحقق هذا التابع من نوع العنصر الأخير في المصفوفة. فإن كان [[Ruby/Hash|جدول Hash]]، يستخرجه ويعيده، وإلا يعيد جدولًا فارغًا.
 +
 +
فلنرى على سبيل المثال تعريف ماكرو وحدة التحكّم <code>caches_action</code>:<syntaxhighlight lang="rails">
 +
def caches_action(*actions)
 +
  return unless cache_configured?
 +
  options = actions.extract_options!
 +
  ...
 +
end
 +
</syntaxhighlight>يتَلقّى هذا التابع عددًا عشوائيًا من أسماء الإجراءات و<nowiki/>[[Ruby/Hash|جدول Hash]] اختياري من الخيارات كوسيط أخير. تتحصّل باستدعاء <code>!extract_options</code> على [[Ruby/Hash|جدول Hash]] من الخيارات وتزيله من <code>actions</code> بطريقة بسيطة وواضحة.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/extract_options.rb active_support/core_ext/array/extract_options.rb].
 +
 +
=== التحويلات ===
 +
 +
==== التابع <code>to_sentence</code> ====
 +
يحوّل التابع <code>to_sentence</code> مصفوفةً لسلسلة نصيّة تحتوي على جملة تُعدِّد (enumerates) عناصرها:<syntaxhighlight lang="rails">
 +
%w().to_sentence                # => ""
 +
%w(Earth).to_sentence          # => "Earth"
 +
%w(Earth Wind).to_sentence      # => "Earth and Wind"
 +
%w(Earth Wind Fire).to_sentence # => "Earth, Wind, and Fire"
 +
 +
</syntaxhighlight>يقبل هذا التابع ثلاثة خيارات:
 +
* <code>:two_words_connector</code>: ما يُستخدم للمصفوفات ذات الطول 2. الكلمة الافتراضية هي "and".
 +
 +
* <code>:words_connector</code>: ما يُستخدم لضم (join) عناصر مصفوفة مكونة من 3 عناصر أو أكثر باستثناء آخر عنصرين. الفاصل الافتراضي هو ",".
 +
 +
* <code>:last_word_connector</code>: ما يُستخدم لضمّ العناصر الأخيرة لمصفوفة مكونة من 3 عناصر أو أكثر. القيمة الافتراضية هي " and ,".
 +
يمكن تخصيص الإعدادات الافتراضية لهذه الخيارات بحسب المحلية، إذ مفاتيحها هي:
 +
{| class="wikitable"
 +
!الخيار
 +
!المفتاح I18n
 +
|-
 +
|<code>two_words_connector:‎</code>
 +
|<code>support.array.two_words_connector</code>
 +
|-
 +
|<code>words_connector:‎</code>
 +
|<code>support.array.words_connector</code>
 +
|-
 +
|<code>last_word_connector:‎</code>
 +
|<code>support.array.last_word_connector</code>
 +
|}
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/conversions.rb active_support/core_ext/array/conversions.rb].
 +
 +
==== التابع <code>to_formatted_s</code> ====
 +
يتصرّف التابع <code>to_formatted_s</code> مثل <code>to_s</code> افتراضيًّا.
 +
 +
ولكن إن احتوت المصفوفة على عناصر تستجيب للمعرِّف <code>id</code>، يمكن تمرير الرمز <code>db:</code> كوسيط. يستخدم ذلك عادةً مع مجموعات كائنات Active Record. السلاسل النصيّة المعادة هي:<syntaxhighlight lang="rails">
 +
[].to_formatted_s(:db)            # => "null"
 +
[user].to_formatted_s(:db)        # => "8456"
 +
invoice.lines.to_formatted_s(:db) # => "23,567,556,12"
 +
</syntaxhighlight>من المفترض أن تأتي الأعداد الصحيحة في المثال أعلاه من الاستدعاءات المعنيّة إلى <code>id</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/conversions.rb active_support/core_ext/array/conversions.rb].
 +
 +
==== التابع <code>to_xml</code> ====
 +
يعيد التابع <code>to_xml</code> سلسلة نصية تحتوي على تمثيل XML للمستقبل المعطى:<syntaxhighlight lang="rails">
 +
Contributor.limit(2).order(:rank).to_xml
 +
# =>
 +
# <?xml version="1.0" encoding="UTF-8"?>
 +
# <contributors type="array">
 +
#  <contributor>
 +
#    <id type="integer">4356</id>
 +
#    <name>Jeremy Kemper</name>
 +
#    <rank type="integer">1</rank>
 +
#    <url-id>jeremy-kemper</url-id>
 +
#  </contributor>
 +
#  <contributor>
 +
#    <id type="integer">4404</id>
 +
#    <name>David Heinemeier Hansson</name>
 +
#    <rank type="integer">2</rank>
 +
#    <url-id>david-heinemeier-hansson</url-id>
 +
#  </contributor>
 +
# </contributors>
 +
</syntaxhighlight>للقيام بذلك، يرسل <code>to_xml</code> إلى كل عنصر بدوره ويجمع النتائج تحت عقدة جذر (root node). يجب أن تستجيب كل العناصر إلى <code>to_xml</code> ويُرفَع استثناء فيما عدا ذلك.
 +
 +
افتراضيًّا اسم العنصر الجذر هو صيغة الجمع عبر الشرطة السفلية (underscored) والشرطة العادية (dasherized) من اسم صنف العنصر الأول، شريطة أن تنتمي بقية العناصر لنفس النوع (يُتحقَّق منها عبر <code>?is_a</code>) وأن لا يكونوا [[Ruby/Hash|جداول Hash]]. في المثال أعلاه ذاك هو "contributors".
 +
 +
إن كان هناك أي عنصر لا ينتمي للنوع الأول، تصبح العقدة الجذرية "objects":<syntaxhighlight lang="rails">
 +
[Contributor.first, Commit.first].to_xml
 +
# =>
 +
# <?xml version="1.0" encoding="UTF-8"?>
 +
# <objects type="array">
 +
#  <object>
 +
#    <id type="integer">4583</id>
 +
#    <name>Aaron Batalion</name>
 +
#    <rank type="integer">53</rank>
 +
#    <url-id>aaron-batalion</url-id>
 +
#  </object>
 +
#  <object>
 +
#    <author>Joshua Peek</author>
 +
#    <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp>
 +
#    <branch>origin/master</branch>
 +
#    <committed-timestamp type="datetime">2009-09-02T16:44:36Z</committed-timestamp>
 +
#    <committer>Joshua Peek</committer>
 +
#    <git-show nil="true"></git-show>
 +
#    <id type="integer">190316</id>
 +
#    <imported-from-svn type="boolean">false</imported-from-svn>
 +
#    <message>Kill AMo observing wrap_with_notifications since ARes was only using it</message>
 +
#    <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
 +
#  </object>
 +
# </objects>
 +
</syntaxhighlight>يُعتبر أيضًا عنصر الجذر افتراضيَّا "objects" إن كان المستقبل [[Ruby/Array|مصفوفة]] من [[Ruby/Hash|جداول Hash]]:<syntaxhighlight lang="rails">
 +
[{a: 1, b: 2}, {c: 3}].to_xml
 +
# =>
 +
# <?xml version="1.0" encoding="UTF-8"?>
 +
# <objects type="array">
 +
#  <object>
 +
#    <b type="integer">2</b>
 +
#    <a type="integer">1</a>
 +
#  </object>
 +
#  <object>
 +
#    <c type="integer">3</c>
 +
#  </object>
 +
# </objects>
 +
</syntaxhighlight>'''تحذير''': إذا كانت المجموعة فارغةً، يكون العنصر الجذر افتراضيًا هو "nil-classes" (أصناف عدمية). على سبيل المثال، لم يكن العنصر الجذر ليكون في قائمة المساهمين أعلاه "contributors" إذا كانت المجموعة فارغةً لكن كانت لتكون "nil-classes" (أصناف عدمية). تستطيع استخدام الخيار <code>root:</code> لضمان عنصر جذر ثابت.
 +
 +
اسم العقد الأبناء (children nodes) هو افتراضيًّا صيغة المفرد من اسم عقدة الجذر. رأينا في الأمثلة أعلاه "contributor" و "object". يسمح لك الخيار <code>children:</code> بتعيين أسماء العقد.
 +
 +
الباني XML الافتراضي هو نسخة (instance) جديدة من <code>Builder::XmlMarkup</code>. تستطيع إعداد الباني الخاص بك عبر الخيار <code>builder:</code>. يقبل التابع أيضًا خيارات مثل الخيار <code>dasherize:</code> ورفقائه، وتُوجَّه إلى الباني:<syntaxhighlight lang="rails">
 +
Contributor.limit(2).order(:rank).to_xml(skip_types: true)
 +
# =>
 +
# <?xml version="1.0" encoding="UTF-8"?>
 +
# <contributors>
 +
#  <contributor>
 +
#    <id>4356</id>
 +
#    <name>Jeremy Kemper</name>
 +
#    <rank>1</rank>
 +
#    <url-id>jeremy-kemper</url-id>
 +
#  </contributor>
 +
#  <contributor>
 +
#    <id>4404</id>
 +
#    <name>David Heinemeier Hansson</name>
 +
#    <rank>2</rank>
 +
#    <url-id>david-heinemeier-hansson</url-id>
 +
#  </contributor>
 +
# </contributors>
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/conversions.rb active_support/core_ext/array/conversions.rb].
 +
 +
=== التغليف ===
 +
يلف التابع <code>Array.wrap</code> وسائطه بمصفوفة إلا إذا كانت بالفعل في مصفوفة (أو شبه مصفوفة).
 +
 +
تحديدًا:
 +
* إن كان الوسيط هو <code>nil</code>، فتعاد مصفوفة فارغة.
 +
 +
* في ما عدا ذلك، يستدعى <code>to_ary</code> إن كان الوسيط يستجيب له، ويعيد قيمة إن كانت تخالف <code>nil</code>.
 +
 +
* في ما عدا ذلك، تعاد مصفوفة مع الوسيط كعنصر واحد.
 +
<syntaxhighlight lang="rails">
 +
Array.wrap(nil)      # => []
 +
Array.wrap([1, 2, 3]) # => [1, 2, 3]
 +
Array.wrap(0)        # => [0]
 +
</syntaxhighlight>يشابه هذا التابع في الغرض التابع <code>[[Ruby/Kernel/Array|Kernel.Array]]</code> لكن هناك بعض الاختلافات:
 +
* إن استجاب الوسيط للتابع <code>to_ary</code>، فيُستدعَى التابع. ينتقل <code>[[Ruby/Kernel/Array|Kernel.Array]]</code> لتجربة <code>to_a</code> إن كانت القيمة المعادة <code>nil</code> لكن يعيد <code>Array.wrap</code> مصفوفة مع الوسيط كعنصرها الوحيد فورًا.
 +
 +
* يرمي التابع <code>[[Ruby/Kernel/Array|Kernel.Array]]</code> استثناءً إن لم تكن القيمة المعادة من <code>to_ary</code> هي <code>nil</code> ولا هي [[Ruby/Array|مصفوفة]]، في حين أن <code>Array.wrap</code> لا ترمي أي استثناء، بل فقط تعيد القيمة.
 +
 +
* لا يستدعي <code>to_a</code> مع الوسيط؛ فإن لم يستجب الوسيط للتابع <code>to_ary</code>، فيعيد مصفوفة مع الوسيط كعنصره الوحيد.
 +
النقطة الأخيرة هي أهمية الموازنة لبعض الكائنات [[Ruby/Enumerable|القابلة للتعداد]] (enumerables):<syntaxhighlight lang="rails">
 +
Array.wrap(foo: :bar) # => [{:foo=>:bar}]
 +
Array(foo: :bar)      # => [[:foo, :bar]]
 +
</syntaxhighlight>يوجد أيضًا مصطلح ذو علاقة يستخدم المعامل <code>*</code>:<syntaxhighlight lang="rails">
 +
[*object]
 +
</syntaxhighlight>والذي في الإصدار 1.8 من روبي يعيد <code>[nil]</code> من أجل <code>nil</code> ويستدعي إلى <code>Array(object)‎</code> في ما عدا ذلك.
 +
 +
وبالتالي، يختلف في هذه الحالة السلوك عن <code>nil</code> والاختلافات مع <code>[[Ruby/Kernel/Array|Kernel.Array]]</code> الموضّحة أعلاه تنطبق على بقية الكائنات <code>object</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/wrap.rb active_support/core_ext/array/wrap.rb].
 +
 +
=== التكرار ===
 +
يكرِّر التابع <code>Array.deep_dup</code> نفسه وكافة الكائنات داخله بشكل عَودي (recursively) مع التابع <code>Object.deep_dup</code> الذي يخص [[Rails/active support|Active Support]]. وتعمل مثل <code>[[Ruby/Array/map|Array.map]]</code> مع إرسال التابع <code>deep_dup</code> لكل كائن بالداخل.<syntaxhighlight lang="rails">
 +
array = [1, [2, 3]]
 +
dup = array.deep_dup
 +
dup[1][2] = 4
 +
array[1][2] == nil  # => true
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/deep_dup.rb active_support/core_ext/object/deep_dup.rb].
 +
 +
=== التجميع ===
 +
 +
==== التابع <code>in_groups_of(number, fill_with = nil)‎</code> ====
 +
يقسّم التابع <code>in_groups_of</code> مصفوفة إلى مجموعات متتالية ذات حجم معين. ثم يعيد مصفوفة من المجموعات:<syntaxhighlight lang="rails">
 +
[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
 +
</syntaxhighlight>أو يمرِّرها إلى الكتلة إن أعطيت:<syntaxhighlight lang="html">
 +
<% sample.in_groups_of(3) do |a, b, c| %>
 +
  <tr>
 +
    <td><%= a %></td>
 +
    <td><%= b %></td>
 +
    <td><%= c %></td>
 +
  </tr>
 +
<% end %>
 +
</syntaxhighlight>يعرض المثال الأول <code>in_groups_of</code> المجموعة الأخيرة بعدد عناصر من القيمة <code>nil</code> حسب الحاجة للحصول على الحجم المطلوب. تستطيع تغيير قيمة هذه الحشوة (padding) باستخدام الوسيط الاختياري الثاني:<syntaxhighlight lang="rails">
 +
[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
 +
</syntaxhighlight>وتستطيع أمر التابع بعدم ملء آخر مجموعة بالقيمة <code>nil</code> عبر تمرير القيمة <code>false</code>:<syntaxhighlight lang="rails">
 +
[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
 +
</syntaxhighlight>نتيجةً لذلك، لا يمكن استخدام <code>false</code> كحشوة (padding) له.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/grouping.rb active_support/core_ext/array/grouping.rb].
 +
 +
==== التابع <code>in_groups(number, fill_with = nil)‎</code> ====
 +
يقسّم التابع <code>in_groups</code> المصفوفة إلى عدد معيّن من المجموعات. يعيد التابع مصفوفة من المجموعات:<syntaxhighlight lang="rails">
 +
%w(1 2 3 4 5 6 7).in_groups(3)
 +
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
 +
 +
</syntaxhighlight>أو يمرِّرها إلى كتلة إن أعطيت:<syntaxhighlight lang="rails">
 +
%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
 +
["1", "2", "3"]
 +
["4", "5", nil]
 +
["6", "7", nil]
 +
</syntaxhighlight>الأمثلة أعلاه تبين أنَّ <code>in_groups</code> تملأ بعض المجموعات مع القيمة <code>nil</code> الزائدة حسب الحاجة. يمكن للمجموعة الحصول على واحدة على الأكثر من هذه العناصر الإضافيّة في أقصى اليمين إن أمكن. والمجموعات التي تُحشَى بهذه القيمة هي دائمًا المجموعات الأخيرة.
 +
 +
تستطيع تغيير قيمة هذه الحشوة باستخدام الوسيط الاختياري الثاني:<syntaxhighlight lang="rails">
 +
%w(1 2 3 4 5 6 7).in_groups(3, "0")
 +
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
 +
 +
</syntaxhighlight>وتستطيع أن تأمر التابع بعدم حشو المجموعات الأصغر عبر تمرير القيمة <code>false</code>:<syntaxhighlight lang="rails">
 +
%w(1 2 3 4 5 6 7).in_groups(3, false)
 +
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
 +
</syntaxhighlight>وكنتيجة، لا يمكن استخدام <code>false</code> كحشوة (padding) بدل العناصر الناقصة.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/grouping.rb active_support/core_ext/array/grouping.rb].
 +
 +
==== التابع <code>split(value = nil)‎</code> ====
 +
يقسم التابع <code>split</code> المصفوفة بفاصل ويعيد القطع الناتجة.
 +
 +
إذا مُرَّرت كتلة، فالفواصل هي عناصر المصفوفة التي تعيد الكتلة القيمة <code>true</code> معها:<syntaxhighlight lang="rails">
 +
(-5..5).to_a.split { |i| i.multiple_of?(4) }
 +
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
 +
 +
</syntaxhighlight>خلا ذلك، تكون القيمة المستلمة كوسيط فاصلًا وهي افتراضيًّا <code>nil</code>:<syntaxhighlight lang="rails">
 +
[0, 1, -5, 1, 1, "foo", "bar"].split(1)
 +
# => [[0], [-5], [], ["foo", "bar"]]
 +
</syntaxhighlight>لاحظ في المثال السابق أن الفواصل المتتالية تُنتج مصفوفات فارغة.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/array/grouping.rb active_support/core_ext/array/grouping.rb].
 +
 +
== ملحقات للجداول Hash ==
 +
 +
=== التحويلات ===
 +
 +
==== التابع <code>to_xml</code> ====
 +
يعيد التابع <code>to_xml</code> سلسلة نصيّة تحتوي على تمثيل XML للمستقبل المعطى:<syntaxhighlight lang="rails">
 +
{"foo" => 1, "bar" => 2}.to_xml
 +
# =>
 +
# <?xml version="1.0" encoding="UTF-8"?>
 +
# <hash>
 +
#  <foo type="integer">1</foo>
 +
#  <bar type="integer">2</bar>
 +
# </hash>
 +
</syntaxhighlight>يصنع التابع حلقات تكراريّة حول الأزواج ويبني العُقد التي تعتمد على "القيم" (values). انطلاقًا من زوج من مفتاح <code>key</code>/قيمة <code>value</code>:
 +
* يوجد نداء عودي مع المفتاح <code>key</code> كجذر <code>root:</code> إن كانت القيمة <code>value</code> [[Ruby/Hash|جدول Hash]].
 +
 +
* إن كانت القيمة <code>value</code> مصفوفة، يوجد نداء عودي مع المفتاح <code>key</code> كجذر <code>root:</code> ومُفرد المفتاح <code>key</code> كأبناء <code>children:</code>.
 +
 +
* إن كانت <code>value</code> كائنًا قابلًا للاستدعاء، فيجب أن تتوقع وسيطًا واحدًا أو وسيطين. يستدعى الكائن القابل للاستدعاء (callable) اعتمادًا على <code>arity</code> مع <code>options</code> ذي النوع <code>[[Ruby/Hash|Hash]]</code> كوسيط أوّل مع <code>key</code> كجذر <code>root:</code> ومُفرد <code>key</code> كوسيط ثاني. تصبح القيمة المعادة له عقدةً جديدةً.
 +
 +
* يستدعَى التابع مع <code>key</code> كجذر ‎<code>:root</code> إن كانت <code>value</code> تستجيب إلى <code>to_xml</code>.
 +
 +
* في ما عدا ذلك، تُنشَأ عقدة مع <code>key</code> كوسم (tag) مع تمثيل سلسلة من <code>value</code> كعقدة نصية. إذا كانت <code>value</code> ذات قيمة <code>nil</code>، تُضاف إليها الخاصيّة "nil" قيمتها "true". ما لم يكن الخيار <code>skip_types:</code> ومضبوطًا إلى القيمة "true"، تُضاف الخاصيّة "type" أيضًا وفقًا للتعين التالي:
 +
<syntaxhighlight lang="rails">
 +
XML_TYPE_NAMES = {
 +
  "Symbol"    => "symbol",
 +
  "Integer"    => "integer",
 +
  "BigDecimal" => "decimal",
 +
  "Float"      => "float",
 +
  "TrueClass"  => "boolean",
 +
  "FalseClass" => "boolean",
 +
  "Date"      => "date",
 +
  "DateTime"  => "datetime",
 +
  "Time"      => "datetime"
 +
}
 +
</syntaxhighlight>تكون العقدة الجذرية هي "hash" افتراضيًّا ولكن هذا قابلة للضبط (configurable) عبر الخيار <code>root:</code>.
 +
 +
باني XML الافتراضي هو نسخة (instance) جديدة من <code>Builder::XmlMarkup</code>. تستطيع إعداد الباني الخاص بك عبر الخيار <code>builder:</code>. يقبل التابع أيضًا خيارات مثل الخيار <code>dasherize:</code> ورفقائه، وتُوجّه إلى الباني:
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/conversions.rb active_support/core_ext/hash/conversions.rb].
 +
 +
=== الدمج ===
 +
يحتوي روبي على التابع <code>[[Ruby/Hash/merge|Hash.merge]]</code> الضمني الذي يدمج كائنين من النوع <code>[[Ruby/Hash|Hash]]</code>:<syntaxhighlight lang="rails">
 +
{a: 1, b: 1}.merge(a: 0, c: 2)
 +
# => {:a=>0, :b=>1, :c=>2}
 +
</syntaxhighlight>يحدّد [[Rails/active support|Active Support]] عدة طرق أخرى لدمج [[Ruby/Hash|الجداول Hash]] التي قد تكون مريحة.
 +
 +
==== التابعان <code>reverse_merge!‎</code> و <code>reverse_merge</code> ====
 +
في حالة الصدام، يفوز المفتاح في [[Ruby/Hash|الجدول Hash]] للوسيط في <code>merge</code>. تستطيع دعم [[Ruby/Hash|الجداول Hash]] للخيارات مع القيم الافتراضية بطرق مختصرة باستخدام هذا المصطلح:<syntaxhighlight lang="rails">
 +
options = {length: 30, omission: "..."}.merge(options)
 +
</syntaxhighlight>يعرّف "[[Rails/active support|Active Support]]" التابع <code>reverse_merge</code> في حال كنت تفضّل هذا الترميز التدوين:<syntaxhighlight lang="rails">
 +
options = options.reverse_merge(length: 30, omission: "...")
 +
</syntaxhighlight>والإصدار المُغيِّر (bang version) للتابع <code>!reverse_merge</code> الذي يعدِّل على المستقبل نفسه:<syntaxhighlight lang="rails">
 +
options.reverse_merge!(length: 30, omission: "...")
 +
</syntaxhighlight>'''تحذير''': خذ بالحسبان أنَّ <code>!reverse_merge</code> قد يغيّر [[Ruby/Hash|الجدول Hash]] في المستدعي وهذا قد أو قد لا يكون فكرة جيدة.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb active_support/core_ext/hash/reverse_merge.rb].
 +
 +
==== التابع <code>reverse_update</code> ====
 +
التابع <code>reverse_update</code> هو اسم بديل للتابع <code>!reverse_merge</code>، كما وضّح أعلاه.
 +
 +
'''تحذير''': لاحظ أنَّ التابع <code>reverse_update</code> لا يملك تابعًا مغيّرًا (bang).
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb active_support/core_ext/hash/reverse_merge.rb].
 +
 +
==== التابعان <code>deep_merge</code> و <code>deep_merge!‎</code> ====
 +
كما ترى في المثال السابق، إن عُثر على مفتاح في كلا [[Ruby/Hash|جدولي Hash]]، ستفوز القيمة الموجودة في الوسيط.
 +
 +
يعرّف "[[Rails/active support|Active Support]]" التابع <code>Hash.deep_merge</code>. إن عُثر في عملية دمج عميقة على مفتاح في كلا [[Ruby/Hash|الجدولين]] وكانت قيمة كل منهما [[Ruby/Hash|جدول Hash]] أيضًا، يصبح دمجهما (merge) القيمة في [[Ruby/Hash|الجدول Hash]] الناتج:<syntaxhighlight lang="rails">
 +
{a: {b: 1}}.deep_merge(a: {c: 2})
 +
# => {:a=>{:b=>1, :c=>2}}
 +
</syntaxhighlight>ينفذّ التابع <code>deep_merge!‎</code> عملية دمج عميقة في الموضع.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/deep_merge.rb active_support/core_ext/hash/deep_merge.rb].
 +
 +
=== التكرار العميق ===
 +
يكرّر التابع <code>Hash.deep_dup</code> نفسه كل المفاتيح والقيم داخله بشكل عودي مع التابع <code>Object.deep_dup</code> الذي يخص [[Rails/active support|Active Support]]. ويعمل مثل <code>[[Ruby/Enumerator/each with object|Enumerator.each_with_object]]</code> بإرسال التابع <code>deep_dup</code> لكل زوج داخله.<syntaxhighlight lang="rails">
 +
hash = { a: 1, b: { c: 2, d: [3, 4] } }
 +
 +
dup = hash.deep_dup
 +
dup[:b][:e] = 5
 +
dup[:b][:d] << 5
 +
 +
hash[:b][:e] == nil      # => true
 +
hash[:b][:d] == [3, 4]  # => true
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/object/deep_dup.rb active_support/core_ext/object/deep_dup.rb].
 +
 +
=== العمل مع المفاتيح ===
 +
 +
==== التابعان <code>except</code> و <code>!except</code> ====
 +
يعيد التابع <code>except</code> [[Ruby/Hash|جدول Hash]] بعد حذف قائمة المفاتيح المُمرَّرة إليه من [[Ruby/Hash|الجدول Hash]] الذي استدعي معه:<syntaxhighlight lang="rails">
 +
{a: 1, b: 2}.except(:a) # => {:b=>2}
 +
</syntaxhighlight>إن كان المستقبل يستجيب للتابع <code>convert_key</code>، يُستدعَى التابع على كلّ وسيط من الوسائط. يسمح هذا للتابع <code>except</code> بالتوافق مع [[Ruby/Hash|الجداول Hash]] مع وصول بلا مشاكل للنسخة:<syntaxhighlight lang="rails">
 +
{a: 1}.with_indifferent_access.except(:a)  # => {}
 +
{a: 1}.with_indifferent_access.except("a") # => {}
 +
</syntaxhighlight>يوجد أيضًا بديل مُغيّر هو <code>!except</code> يزيل المفاتيح من المستقبل نفسه.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/except.rb active_support/core_ext/hash/except.rb].
 +
 +
==== التابعان <code>transform_keys</code> و <code>!transform_keys</code> ====
 +
يقبل التابع <code>transform_keys</code> كتلة ويعيد [[Ruby/Hash|جدول Hash]] المفاتيح فيه نتجت عن تطبيق الكتلة على مفاتيح المستقبل:<syntaxhighlight lang="rails">
 +
{nil => nil, 1 => 1, a: :a}.transform_keys { |key| key.to_s.upcase }
 +
# => {"" => nil, "1" => 1, "A" => :a}
 +
 +
</syntaxhighlight>في حالة صدام المفاتيح، ستُختَار واحدة من القيم. قد لا تكون القيمة المختارة ذاتها كلّ مرّة عند اعطاء [[Ruby/Hash|الجدول Hash]] نفسه:<syntaxhighlight lang="rails">
 +
{"a" => 1, a: 2}.transform_keys { |key| key.to_s.upcase }
 +
# The result could either be
 +
# => {"A"=>2}
 +
# or
 +
# => {"A"=>1}
 +
</syntaxhighlight>قد يكون هذا التابع مفيدًا على سبيل المثال ببناء تحويلات متخصّصة. على سبيل المثال يستخدم التابعان <code>stringify_keys</code> و <code>symbolize_keys</code> التابع <code>transform_keys</code> لإجراء تحويلات على مفاتيحهم:<syntaxhighlight lang="rails">
 +
def stringify_keys
 +
  transform_keys { |key| key.to_s }
 +
end
 +
...
 +
def symbolize_keys
 +
  transform_keys { |key| key.to_sym rescue key }
 +
end
 +
</syntaxhighlight>هناك أيضًا إصدار مُغيّر هو <code>!transform_keys</code> الذي يطبّق الكتلة المعطاة على مفاتيح المستقبل ذاته.
 +
 +
يمكن للمرء علاوة على ذلك استخدام التابع <code>deep_transform_keys</code> والتابع <code>!deep_transform_keys</code> لتنفيذ الكتلة على جميع المفاتيح في [[Ruby/Hash|الجدول Hash]] المُعطى وكل [[Ruby/Hash|الجداول]] المتداخلة داخله. إليك هذا المثال:<syntaxhighlight lang="rails">
 +
{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_transform_keys { |key| key.to_s.upcase }
 +
# => {""=>nil, "1"=>1, "NESTED"=>{"A"=>3, "5"=>5}}
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/keys.rb active_support/core_ext/hash/keys.rb].
 +
 +
التابعان <code>stringify_keys</code> و <code>!stringify_keys</code>
 +
 +
يعيد التابع <code>stringify_keys</code> [[Ruby/Hash|جدول Hash]] يحتوي على نسخة في شكل سلسلة نصيّة من مفاتيح المستقبل. ويفعل ذلك عن طريق إرسال <code>to_s</code> لهم:<syntaxhighlight lang="rails">
 +
{nil => nil, 1 => 1, a: :a}.stringify_keys
 +
# => {"" => nil, "1" => 1, "a" => :a}
 +
</syntaxhighlight>في حالة صدام المفاتيح، ستُختار واحدة من القيم. قد لا تكون القيمة المختارة ذاتها كلّ مرّة عند إعطاء [[Ruby/Hash|جدول Hash]] نفسه:<syntaxhighlight lang="rails">
 +
{"a" => 1, a: 2}.stringify_keys
 +
# The result could either be
 +
# => {"a"=>2}
 +
# or
 +
# => {"a"=>1}
 +
</syntaxhighlight>قد يكون هذا التابع مفيدًا مثلًا لقبول كل من الرموز والسلاسل النصيّة كخيارات بسهولة. على سبيل المثال يعرّف <code>ActionView::Helpers::FormHelper</code>:<syntaxhighlight lang="rails">
 +
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
 +
  options = options.stringify_keys
 +
  options["type"] = "checkbox"
 +
  ...
 +
end
 +
</syntaxhighlight>يمكن للسطر الثاني الوصول بأمان إلى المفتاح "type"، والسماح للمستخدم بتمرير إمّا <code>type:</code> أو "type".
 +
 +
يوجد أيضًا البديل المُغيّر وهو <code>!stringify_keys</code> الذي يعمل على تحويل المفاتيح لسلاسل نصيّة في المستقبل ذاته.
 +
 +
يمكن للمرء علاوة على ذلك استخدام <code>deep_stringify_keys</code> و <code>!deep_stringify_keys</code> لتحويل جميع المفاتيح لسلسة نصيّة في [[Ruby/Hash|الجدول Hash]] المُعطى وكل [[Ruby/Hash|الجداول]] المتداخلة داخله. هذا مثال على النتيجة:<syntaxhighlight lang="rails">
 +
{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_stringify_keys
 +
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/keys.rb active_support/core_ext/hash/keys.rb].
 +
 +
==== التابعان <code>symbolize_keys</code> و <code>!symbolize_keys</code> ====
 +
يعيد التابع <code>symbolize_keys</code> [[Ruby/Hash|جدول Hash]] يحتوي على نسخة رمزية من مفاتيح المستقبل حيثما أمكن ذلك وذلك عن طريق تطبيق <code>to_sym</code> عليهم:<syntaxhighlight lang="rails">
 +
{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
 +
# => {nil=>nil, 1=>1, :a=>"a"}
 +
</syntaxhighlight>'''تحذير''': لاحظ في المثال السابق أن مفتاح واحد فقط تحول إلى رُمّز.
 +
 +
في حالة صدام المفاتيح، ستُختار واحدة من القيم. قد لا تكون القيمة المختارة ذاتها كلّ مرّة عند إعطاء [[Ruby/Hash|الجدول Hash]] نفسه:<syntaxhighlight lang="rails">
 +
{"a" => 1, a: 2}.symbolize_keys
 +
# The result could either be
 +
# => {:a=>2}
 +
# or
 +
# => {:a=>1}
 +
</syntaxhighlight>قد يكون هذا التابع مفيدًا على سبيل المثال بقبول كل من الرموز والسلاسل النصيّة كخيارات بسهولة. على سبيل المثال يُعرّف <code>ActionController::UrlRewriter</code>:<syntaxhighlight lang="rails">
 +
def rewrite_path(options)
 +
  options = options.symbolize_keys
 +
  options.update(options[:params].symbolize_keys) if options[:params]
 +
  ...
 +
end
 +
</syntaxhighlight>يمكن للسطر الثاني الوصول بأمان إلى المفتاح <code>params:</code> والسماح للمستخدم بتمرير <code>params:</code> أو "params".
 +
 +
يوجد أيضًا التابع المغيّر <code>!symbolize_keys</code> لهذا التابع الذي يُرمّز مفاتيح المستقبل نفسه.
 +
 +
يمكن للمرء عدا ذلك استخدام <code>deep_symbolize_keys</code> و <code>!deep_symbolize_keys</code> لترميز جميع المفاتيح في [[Ruby/Hash|جدول Hash]] معيّن وكلّ [[Ruby/Hash|الجداول]] المتداخلة ضمنه. مثال على النتيجة:<syntaxhighlight lang="rails">
 +
{nil => nil, 1 => 1, "nested" => {"a" => 3, 5 => 5}}.deep_symbolize_keys
 +
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/keys.rb active_support/core_ext/hash/keys.rb].
 +
 +
==== التابعان <code>to_options</code> و <code>!to_options</code> ====
 +
التابعان <code>to_options</code> و <code>!to_options</code> هما اسمان بديلان للتابعين <code>symbolize_keys</code> و <code>!symbolize_keys</code> على التوالي.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/keys.rb active_support/core_ext/hash/keys.rb].
 +
 +
==== التابع <code>assert_valid_keys</code> ====
 +
يأخذ التابع <code>assert_valid_keys</code> عددًا عشوائيًا من الوسائط ويتحقّق من امتلاك المستقبل لأي مفتاح خارج تلك القائمة البيضاء. إن كان هنالك مفتاح خارج تلك القائمة، يطلق التابع الاستثناء <code>[[Ruby/ArgumentError|ArgumentError]]</code>.<syntaxhighlight lang="rails">
 +
{a: 1}.assert_valid_keys(:a)  # passes
 +
{a: 1}.assert_valid_keys("a") # ArgumentError
 +
</syntaxhighlight>لا يقبل [[Rails/active record basics|Active Record]] خيارات غير معروفة عند إنشاء [[Rails/association basics|الارتباطات]] مثلًا. ينفّذ هذا التحكّم عبر <code>assert_valid_keys</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/keys.rb active_support/core_ext/hash/keys.rb].
 +
 +
=== العمل مع القيم ===
 +
 +
==== التابعان <code>transform_values</code> و <code>!transform_values</code> ====
 +
يقبل التابع <code>transform_values</code> كتلة ويعيد [[Ruby/Hash|جدول Hash]] القيم فيه ناتجة عن تطبيق الكتلة على كل قيمة من قيم المستقبل.<syntaxhighlight lang="rails">
 +
{ nil => nil, 1 => 1, :x => :a }.transform_values { |value| value.to_s.upcase }
 +
# => {nil=>"", 1=>"1", :x=>"A"}
 +
</syntaxhighlight>يوجد أيضًا إصدار مغيِّر لهذا التابع هو <code>!transform_values</code> الذي يطبّق الكتلة على القيم في المستقبل ذاته ويستبدلها.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/transform_values.rb active_support/core_ext/hash/transform_values.rb].
 +
 +
=== التقطيع ===
 +
يحتوي [[Ruby|روبي]] على دعم مدمج لاقتطاع قطعة خارج السلاسل النصيّة والمصفوفات. يمتد [[Rails/active support|Active Support]] ليشمل [[Ruby/Hash|جداول Hash]] أيضًا:<syntaxhighlight lang="rails">
 +
{a: 1, b: 2, c: 3}.slice(:a, :c)
 +
# => {:a=>1, :c=>3}
 +
 +
{a: 1, b: 2, c: 3}.slice(:b, :X)
 +
# => {:b=>2} # المفاتيح غير الموجودة يجري تجاهلها
 +
</syntaxhighlight>توَّحد (normalize) المفاتيح إن استجاب المستقبل للتابع <code>convert_key</code>:<syntaxhighlight lang="rails">
 +
{a: 1, b: 2}.with_indifferent_access.slice("a")
 +
# => {:a=>1}
 +
</syntaxhighlight>'''ملاحظة''': قد يكون التقطيع مفيدًا لتعقيم خيارات [[Ruby/Hash|الجداول Hash]] باستخدام قائمة بيضاء من المفاتيح.
 +
 +
هناك أيضًا الإصدار المغير <code>!slice</code> الذي - بالإضافة إلى التعديل على المستقبل نفسه - يعيد  ما اقتطعه:<syntaxhighlight lang="rails">
 +
hash = {a: 1, b: 2}
 +
rest = hash.slice!(:a) # => {:b=>2}
 +
hash                  # => {:a=>1}
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/slice.rb active_support/core_ext/hash/slice.rb].
 +
 +
=== الاستخراج ===
 +
يزيل التابع <code>!extract</code> أزواج المفاتيح/القيم المطابقة للمفاتيح المعطاة ويعيدها.<syntaxhighlight lang="rails">
 +
hash = {a: 1, b: 2}
 +
rest = hash.extract!(:a) # => {:a=>1}
 +
hash                    # => {:b=>2}
 +
</syntaxhighlight>يعيد التابع <code>!extract</code> نفس صنف المستقبل الفرعي من [[Ruby/Hash|الجدول Hash]].<syntaxhighlight lang="rails">
 +
hash = {a: 1, b: 2}.with_indifferent_access
 +
rest = hash.extract!(:a).class
 +
# => ActiveSupport::HashWithIndifferentAccess
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/slice.rb active_support/core_ext/hash/slice.rb].
 +
 +
=== الوصول غير المبال (Indifferent Access) ===
 +
التابع <code>with_indifferent_access</code> يعيد <code>ActiveSupport::HashWithIndifferentAccess</code> من مستقبله:<syntaxhighlight lang="rails">
 +
{a: 1}.with_indifferent_access["a"] # => 1
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb active_support/core_ext/hash/indifferent_access.rb].
 +
 +
=== الضغط (Compacting) ===
 +
يعيد التابعان <code>compact</code> و <code>!compact</code> [[Ruby/Hash|جدول Hash]] بدون عناصر قيمتها هي <code>nil</code>.<syntaxhighlight lang="rails">
 +
{a: 1, b: 2, c: nil}.compact # => {a: 1, b: 2}
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/hash/compact.rb active_support/core_ext/hash/compact.rb].
 +
 +
== ملحقات للتعابير النمطية ==
 +
 +
=== التابع <code>?multiline</code> ===
 +
يعلمنا التابع <code>?multiline</code> بما إذا كان [[Ruby/Regexp|تعبير نمطي]] عيَّن الراية <code>m/</code> أي ما إذا تطابق النقطة سطورًا جديدةً.<syntaxhighlight lang="rails">
 +
%r{.}.multiline?  # => false
 +
%r{.}m.multiline? # => true
 +
 +
Regexp.new('.').multiline?                    # => false
 +
Regexp.new('.', Regexp::MULTILINE).multiline? # => true
 +
</syntaxhighlight>يستخدم ريلز هذا التابع في مكان واحد وهو أيضًا في تعليمات التوجيه البرمجيّة. التعابير النمطية متعددة الأسطر غير مسموح بها لمتطلّبات توجيه المسار (route requirements) وتسهّل هذه الراية فرض هذا القيد.<syntaxhighlight lang="rails">
 +
def assign_route_options(segments, defaults, requirements)
 +
  ...
 +
  if requirement.multiline?
 +
    raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
 +
  end
 +
  ...
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/regexp.rb active_support/core_ext/regexp.rb].
 +
 +
=== التابع <code>?match</code> ===
 +
يعرّف ريلز استخدام <code>?Regexp.match</code> في إصدارات روبي قبل 2.4:<syntaxhighlight lang="rails">
 +
/oo/.match?('foo')    # => true
 +
/oo/.match?('bar')    # => false
 +
/oo/.match?('foo', 1) # => true
 +
</syntaxhighlight>يملك الحمل العكسي (backport) نفس الواجهة ونفس غياب الآثار الجانبيّة عند المستدعي مثل عدم تعيين <code>1$</code> ورفقائه ولكنه لا يتمتّع بمزايا السرعة. الغرض منه هو القدرة على كتابة تعليمات برمجيّة توافق الإصدار 2.4. مثلًا، يستخدم ريلز نفسه هذا التابع الخبري داخليًا.
 +
 +
يُعرّف [[Rails/active support|Active Support]] التابع <code>[[Ruby/Regexp/match-3F|?Regexp.match]]</code> فقط إذا لم يكن موجودًا بحيث تعمل التعليمات البرمجيّة من قبل الإصدار 2.4 أو أحدث حسب الإصدار الأصلي وتستفيد من تحسين الأداء.
 +
 +
== ملحقات للمجالات ==
 +
 +
=== التابع <code>to_s</code> ===
 +
يوسِّع [[Rails/active support|Active Support]] التابع <code>Range.to_s</code> حتى يفهم وسيط تنسيق اختياري. حتى كتابة هذه السطور، التنسيق غير الافتراضي الوحيد المدعوم هو <code>db:</code>:<syntaxhighlight lang="rails">
 +
(Date.today..Date.tomorrow).to_s
 +
# => "2009-10-25..2009-10-26"
 +
 +
(Date.today..Date.tomorrow).to_s(:db)
 +
# => "BETWEEN '2009-10-25' AND '2009-10-26'"
 +
 +
</syntaxhighlight>ينشئ تنسيق <code>db:</code>، كما يوضح المثال، العبارة <code>[[SQL/Interval Operators#.D9.85.D8.B9.D8.A7.D9.85.D9.84 BETWEEN|BETWEEN]]</code> في [[SQL]]. ويُستخدم من طرف [[Rails/active record|Active Record]] في دعمه لقيم المجال تحت شروط.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/range/conversions.rb active_support/core_ext/range/conversions.rb].
 +
 +
=== التابع <code>?include</code> ===
 +
يعلمنا التابعان <code>?Range.include</code> و <code>===.Range</code> إن وقعت بعض القيم بين نهايات نسخة (instance) معيّنة:<syntaxhighlight lang="rails">
 +
(2..3).include?(Math::E) # => true
 +
</syntaxhighlight>يوسِّع [[Rails/active support|Active Support]] هذه التوابع بحيث يكون الوسيط نطاقًا آخر بدوره. نختبر في هذه الحالة ما إذا انتمت نهايات مجال الوسيط للمستقبل نفسه:<syntaxhighlight lang="rails">
 +
(1..10).include?(3..7)  # => true
 +
(1..10).include?(0..7)  # => false
 +
(1..10).include?(3..11) # => false
 +
(1...9).include?(3..9)  # => false
 +
 +
(1..10) === (3..7)  # => true
 +
(1..10) === (0..7)  # => false
 +
(1..10) === (3..11) # => false
 +
(1...9) === (3..9)  # => false
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/range/include_range.rb active_support/core_ext/range/include_range.rb].
 +
 +
=== التابع <code>?overlaps</code> ===
 +
يُعلمنا التابع <code>?Range.overlaps</code> إن وُجد تقاطع غير خالي (non-void intersection) بين أي مجالين محددين:<syntaxhighlight lang="rails">
 +
(1..10).overlaps?(7..11)  # => true
 +
(1..10).overlaps?(0..7)  # => true
 +
(1..10).overlaps?(11..27) # => false
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/range/overlaps.rb active_support/core_ext/range/overlaps.rb].
 +
 +
== ملحقات للتاريخ (Date) ==
 +
 +
=== الحسابات ===
 +
'''ملاحظة''': تُعرّف كل التوابع التالية في active_support/core_ext/date/calculations.rb.<syntaxhighlight lang="rails">
 +
yesterday
 +
tomorrow
 +
beginning_of_week (at_beginning_of_week)
 +
end_of_week (at_end_of_week)
 +
monday
 +
sunday
 +
weeks_ago
 +
prev_week (last_week)
 +
next_week
 +
months_ago
 +
months_since
 +
beginning_of_month (at_beginning_of_month)
 +
end_of_month (at_end_of_month)
 +
last_month
 +
beginning_of_quarter (at_beginning_of_quarter)
 +
end_of_quarter (at_end_of_quarter)
 +
beginning_of_year (at_beginning_of_year)
 +
end_of_year (at_end_of_year)
 +
years_ago
 +
years_since
 +
last_year
 +
on_weekday?
 +
on_weekend?
 +
</syntaxhighlight>'''تنبيه''': تملك توابع الحساب التالية حالات حديِّة (edge cases) في تشرين الأول 1582 وبما أنّ الأيّام 5..14 غير موجودة. لا يُوثّق هذا الدليل سلوكهم في تلك الأيام للإيجاز ولكن يكفي القول أنهم يتصرّفون مثلما تتوقّع تمامًا. أي أنّ <code>Date.new(1582, 10, 4).tomorrow</code> يعيد <code>Date.new(1582, 10, 15).tomorrow</code> وهكذا دواليك. الرجاء التحقّق من test/core_ext/date_ext_test.rb في مجموعة اختبار [[Rails/active support|Active Support]] من أجل الإطلاع على السلوك المتوقّع.
 +
 +
==== الخاصية <code>Date.current</code> ====
 +
يعرّف "[[Rails/active support|Active Support]]" الخاصية <code>Date.current</code> لتمثِّل اليوم في المنطقة الزمنية الحاليّة. أي مثل <code>Date.today</code> إلّا أنه يحترم المنطقة الزمنية للمستخدم إن حدِّدت. ويعرّف أيضًا <code>Date.yesterday</code> و <code>Date.tomorrow</code> وتوابع نسخة خبرية (instance predicates) مثل <code>?past</code> و <code>?today</code> و <code>?future</code>، و <code>?on_weekday</code> و <code>?on_weekend</code>، كل منهم نسبةً إلى <code>Date.current</code>.
 +
 +
تأكّد من استخدام <code>Date.current</code> وليس <code>Date.today</code> عند إجراء مقارنات تاريخ باستخدام التوابع التي تحترم المنطقة الزمنية للمستخدم. هناك حالات قد تكون فيها المنطقة الزمنية للمستخدم في المستقبل مقارنة بالمنطقة الزمنية للنظام والتي يستخدمها <code>Date.today</code> افتراضيًّا. هذا يعني <code>Date.today</code> قد تساوي <code>Date.yesterday</code>.
 +
 +
==== التواريخ المسماة (Named dates) ====
 +
 +
===== <code>beginning_of_week</code> و <code>end_of_week</code> =====
 +
تعيد التوابع <code>beginning_of_week</code> و <code>end_of_week</code> مواعيد بداية ونهاية الأسبوع على التوالي. من المفترض أن الأسابيع تبدأ يوم الاثنين ولكن يمكن تغيير ذلك من خلال تمرير وسيط أو ضبط الخيط المحلّي <code>Date.beginning_of_week</code> أو <code>config.beginning_of_week</code>.<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 8)    # => Sat, 08 May 2010
 +
d.beginning_of_week          # => Mon, 03 May 2010
 +
d.beginning_of_week(:sunday) # => Sun, 02 May 2010
 +
d.end_of_week                # => Sun, 09 May 2010
 +
d.end_of_week(:sunday)      # => Sat, 08 May 2010
 +
 +
</syntaxhighlight>التابع <code>at_beginning_of_week</code> هو اسم بديل للتابع <code>beginning_of_week</code> والتابع <code>at_end_of_week</code> هو اسم بديل للتابع <code>end_of_week</code>.
 +
 +
===== <code>sunday</code> و <code>monday</code> =====
 +
يعيد التابعان <code>monday</code> و <code>sunday</code> تواريخ الإثنين والسبت السابقين على التوالي.<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 8)    # => Sat, 08 May 2010
 +
d.monday                    # => Mon, 03 May 2010
 +
d.sunday                    # => Sun, 09 May 2010
 +
 +
d = Date.new(2012, 9, 10)    # => Mon, 10 Sep 2012
 +
d.monday                    # => Mon, 10 Sep 2012
 +
 +
d = Date.new(2012, 9, 16)    # => Sun, 16 Sep 2012
 +
d.sunday                    # => Sun, 16 Sep 2012
 +
</syntaxhighlight>
 +
 +
===== <code>prev_week</code> و <code>next_week</code> =====
 +
يأخذ التابع <code>next_week</code> [[Ruby/Symbol|رمزًا]] باسم يوم باللغة الإنجليزية (الافتراضي هو مؤشّر محلية الخيط <code>Date.beginning_of_week</code>، أو <code>config.beginning_of_week</code>، أو <code>monday:</code>) ويعيد التاريخ المقابل لذلك اليوم.<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
 +
d.next_week              # => Mon, 10 May 2010
 +
d.next_week(:saturday)  # => Sat, 15 May 2010
 +
</syntaxhighlight>التابع <code>prev_week</code> مشابه (analogous):<syntaxhighlight lang="rails">
 +
d.prev_week              # => Mon, 26 Apr 2010
 +
d.prev_week(:saturday)  # => Sat, 01 May 2010
 +
d.prev_week(:friday)    # => Fri, 30 Apr 2010
 +
</syntaxhighlight>التابع <code>last_week</code> هو اسم بديل للتابع <code>prev_week</code>.
 +
 +
يعمل التابعين <code>next_week</code> و <code>prev_week</code> كلاهما كما هو متوقع عند ضبط <code>Date.beginning_of_week</code> أو <code>config.beginning_of_week</code>.
 +
===== <code>beginning_of_month</code> و <code>end_of_month</code> =====
 +
يعيد التابعان <code>beginning_of_month</code> و <code>end_of_month</code> مواعيد بداية ونهاية الشهر:<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
 +
d.beginning_of_month    # => Sat, 01 May 2010
 +
d.end_of_month          # => Mon, 31 May 2010
 +
</syntaxhighlight>التابع <code>beginning_of_month</code> هو اسم بديل للتابع <code>at_beginning_of_month</code> والتابع <code>end_of_month</code> هو اسم بديل للتابع <code>at_end_of_month</code>.
 +
 +
===== <code>beginning_of_quarter</code> و <code>end_of_quarter</code> =====
 +
يعيد التابعان <code>beginning_of_quarter</code> و <code>end_of_quarter</code> مواعيد بداية ونهاية ربع السنة التقويمية للمستقبل:<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
 +
d.beginning_of_quarter  # => Thu, 01 Apr 2010
 +
d.end_of_quarter        # => Wed, 30 Jun 2010
 +
 +
</syntaxhighlight>التابع <code>beginning_of_quarter</code> هو اسم بديل للتابع <code>at_beginning_of_quarter</code> والتابع <code>end_of_quarter</code> هو اسم بديل للتابع <code>at_end_of_quarter</code>.
 +
===== <code>beginning_of_year</code> و <code>end_of_year</code> =====
 +
يعيد التابعان <code>beginning_of_year</code> و <code>end_of_year</code> مواعيد بداية ونهاية العام:<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
 +
d.beginning_of_year      # => Fri, 01 Jan 2010
 +
d.end_of_year            # => Fri, 31 Dec 2010
 +
</syntaxhighlight>التابع <code>beginning_of_year</code> هو اسم بديل للتابع <code>at_beginning_of_year</code> والتابع <code>end_of_year</code> هو اسم بديل للتابع <code>at_end_of_year</code>.
 +
==== حسابات تاريخ أخرى ====
 +
 +
===== <code>years_ago</code> و <code>years_since</code> =====
 +
يُستدعَى التابع <code>years_ago</code> مع تاريخ ويعيد التاريخ المقابل قبل السنوات المحدَّدة المُمرَّرة إليه:<syntaxhighlight lang="rails">
 +
date = Date.new(2010, 6, 7)
 +
date.years_ago(10) # => Wed, 07 Jun 2000
 +
</syntaxhighlight>بشكل مشابه، التابع <code>years_since</code> يعيد التاريخ المقابل بعد السنوات المحدَّدة المُمرَّرة إليه:<syntaxhighlight lang="rails">
 +
date = Date.new(2010, 6, 7)
 +
date.years_since(10) # => Sun, 07 Jun 2020
 +
 +
</syntaxhighlight>إن لم يوجد مثل هذا اليوم، فسيعاد اليوم الأخير من الشهر المقابل:<syntaxhighlight lang="rails">
 +
Date.new(2012, 2, 29).years_ago(3)    # => Sat, 28 Feb 2009
 +
Date.new(2012, 2, 29).years_since(3)  # => Sat, 28 Feb 2015
 +
</syntaxhighlight>التابع <code>last_year</code> هو اختصار للتابع <code>(1)years_ago.</code>.
 +
===== <code>months_ago</code> و <code>months_since</code> =====
 +
يسلك التابعان <code>months_ago</code> و <code>months_since</code> نفس سلوك التابعين السابقين ولكن مع الأشهر:<syntaxhighlight lang="rails">
 +
Date.new(2010, 4, 30).months_ago(2)  # => Sun, 28 Feb 2010
 +
Date.new(2010, 4, 30).months_since(2) # => Wed, 30 Jun 2010
 +
</syntaxhighlight>إن لم يوجد مثل هذا اليوم، فسيعاد اليوم الأخير من الشهر المقابل:<syntaxhighlight lang="rails">
 +
Date.new(2010, 4, 30).months_ago(2)    # => Sun, 28 Feb 2010
 +
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010
 +
</syntaxhighlight>التابع <code>last_month</code> هو اختصار للتابع <code>(1)months_ago.</code>.
 +
===== <code>weeks_ago</code> =====
 +
يعمل التابع <code>weeks_ago</code> بشكل مماثل للتابعين السابقين ولكن بالنسبة للأسابيع:<syntaxhighlight lang="rails">
 +
Date.new(2010, 5, 24).weeks_ago(1)    # => Mon, 17 May 2010
 +
Date.new(2010, 5, 24).weeks_ago(2)    # => Mon, 10 May 2010
 +
</syntaxhighlight>
 +
 +
===== <code>advance</code> =====
 +
الطريقة الأكثر بساطة للقفز لأيام أخرى هي عبر استعمال التابع <code>advance</code>. يتلقّى هذا التابع [[Ruby/Hash|جدول Hash]] مع المفاتيح: <code>:years</code> و <code>:months</code> و <code>:weeks</code> و <code>:days</code> ويعيد تاريخًا لاحقًا بقدر ما تشير إليه المفاتيح الحالية:<syntaxhighlight lang="rails">
 +
date = Date.new(2010, 6, 6)
 +
date.advance(years: 1, weeks: 2)  # => Mon, 20 Jun 2011
 +
date.advance(months: 2, days: -2) # => Wed, 04 Aug 2010
 +
 +
</syntaxhighlight>لاحظ في المثال السابق أن الزيادات قد تكون سالبة.
 +
 +
لإجراء الحساب، يزيد التابع أولًا السنوات، ثم الشهور، ثم الاسابيع، ثم أخيرًا الأيّام. هذا الترتيب مهم قرابة نهاية الأشهر. فلنفرض على سبيل المثال أنّنا في نهاية شهر شباط لعام 2010 ونريد أن نقفز مدّة شهر واحد ويوم واحد فقط. يقدّم التابع <code>advance</code> اولًا شهرًا واحدًا ثمّ يومًا واحدًا والنتيجة هي:<syntaxhighlight lang="rails">
 +
Date.new(2010, 2, 28).advance(months: 1, days: 1)
 +
# => Sun, 29 Mar 2010
 +
</syntaxhighlight>بينما لو قلبت الترتيب ستختلف النتيجة:<syntaxhighlight lang="rails">
 +
Date.new(2010, 2, 28).advance(days: 1).advance(months: 1)
 +
# => Thu, 01 Apr 2010
 +
</syntaxhighlight>
 +
 +
==== تغيير المكونات (Changing Components) ====
 +
يسمح لك التابع <code>change</code> بالحصول على تاريخ جديد وهو نفس تاريخ المستقبل باستثناء السنة أو الشهر أو اليوم المحدّد:<syntaxhighlight lang="rails">
 +
Date.new(2010, 12, 23).change(year: 2011, month: 11)
 +
# => Wed, 23 Nov 2011
 +
</syntaxhighlight>لا يتسامح هذا التابع مع التواريخ غير الموجودة إذ يرفع الاستثناء <code>[[Ruby/ArgumentError|ArgumentError]]</code> إن كان التغيير غير صالح:<syntaxhighlight lang="rails">
 +
Date.new(2010, 1, 31).change(month: 2)
 +
# => ArgumentError: invalid date
 +
</syntaxhighlight>
 +
 +
==== الفترات (Durations) ====
 +
يمكن إضافة فترات وطرحها من التواريخ:<syntaxhighlight lang="rails">
 +
d = Date.current
 +
# => Mon, 09 Aug 2010
 +
d + 1.year
 +
# => Tue, 09 Aug 2011
 +
d - 3.hours
 +
# => Sun, 08 Aug 2010 21:00:00 UTC +00:00
 +
</syntaxhighlight>الفترات تحول إلى استدعاءات إلى التابع <code>since</code> أو التابع <code>advance</code>. نحصل على سبيل المثال على القفزة الصحيحة في إصلاح التقويم:<syntaxhighlight lang="rails">
 +
Date.new(1582, 10, 4) + 1.day
 +
# => Fri, 15 Oct 1582
 +
</syntaxhighlight>
 +
 +
==== البصمات الزمنية (Timestamps) ====
 +
'''تنبيه''': تعيد التوابع التالية كائنًا من النوع <code>[[Ruby/Time|Time]]</code> إن أمكن وإلا تعيد <code>DateTime</code>. إن ضُبطَت، فإنها تحترم المنطقة الزمنيّة للمستخدم.
 +
 +
===== <code>beginning_of_day</code> و <code>end_of_day</code> =====
 +
يعيد التابع <code>beginning_of_day</code> بصمة زمنيًّا في بداية اليوم (00:00:00):<syntaxhighlight lang="rails">
 +
date = Date.new(2010, 6, 7)
 +
date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010
 +
</syntaxhighlight>يعيد التابع <code>end_of_day</code> بصمة زمنية بنهاية اليوم (23:59:59):<syntaxhighlight lang="rails">
 +
ate = Date.new(2010, 6, 7)
 +
date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010
 +
</syntaxhighlight>التابع <code>beginning_of_day</code> هو اسم بديل للتابع <code>at_beginning_of_day</code> والتابع <code>midnight</code> والتابع <code>at_midnight</code>.
 +
===== <code>beginning_of_hour</code> و <code>end_of_hour</code> =====
 +
يعيد التابع <code>beginning_of_hour</code> بصمة زمنية في بداية الساعة (hh:00:00):<syntaxhighlight lang="rails">
 +
date = DateTime.new(2010, 6, 7, 19, 55, 25)
 +
date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010
 +
</syntaxhighlight>يعيد التابع <code>end_of_hour</code> بصمة زمنية في نهاية الساعة (hh:59:59):<syntaxhighlight lang="rails">
 +
date = DateTime.new(2010, 6, 7, 19, 55, 25)
 +
date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010
 +
</syntaxhighlight>التابع <code>beginning_of_hour</code> هو اسم بديل للتابع <code>at_beginning_of_hour</code>.
 +
===== <code>beginning_of_minute</code> و <code>end_of_minute</code> =====
 +
يعيد التابع بصمة زمنية في بداية الدقيقة (hh:mm:00):<syntaxhighlight lang="rails">
 +
date = DateTime.new(2010, 6, 7, 19, 55, 25)
 +
date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010
 +
</syntaxhighlight>يعيد التابع <code>end_of_minute</code> بصمة زمنية في نهاية الدقيقة (hh:mm:59):<syntaxhighlight lang="rails">
 +
date = DateTime.new(2010, 6, 7, 19, 55, 25)
 +
date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010
 +
</syntaxhighlight>التابع <code>beginning_of_minute</code> هو اسم بديل للتابع <code>at_beginning_of_minute</code>.
 +
 +
'''تنبيه''': تنفَّذ التوابع <code>beginning_of_hour</code>، و <code>end_of_hour</code>، و <code>beginning_of_minute</code> و <code>end_of_minute</code> من أجل الكائن <code>[[Ruby/Time|Time]]</code> والكائن <code>DateTime</code> لكن ليس من أجل الكائن <code>Date</code> لأنه من غير المعقول أن يطلب بداية أو نهاية ساعة أو دقيقة على نسخة من الكائن <code>Date</code>.
 +
 +
===== <code>ago</code> و <code>since</code> =====
 +
يأخذ التابع <code>ago</code> عدد الثواني كوسيط ويعيد بصمة زمنية تحمل ذلك العدد من الثواني مطروحًا من منتصف الليل:<syntaxhighlight lang="rails">
 +
date = Date.current # => Fri, 11 Jun 2010
 +
date.ago(1)        # => Thu, 10 Jun 2010 23:59:59 EDT -04:00
 +
</syntaxhighlight>يسلك التابع <code>since</code> السلوك ذاته أيضًا ولكن مع التقديم للأمام:<syntaxhighlight lang="rails">
 +
date = Date.current # => Fri, 11 Jun 2010
 +
date.since(1)      # => Fri, 11 Jun 2010 00:00:01 EDT -04:00
 +
</syntaxhighlight>
 +
 +
== ملحقات للصنف <code>DateTime</code> ==
 +
'''تحذير''': لا يعي الكائن <code>DateTime</code> قواعد التوقيت الصيفي (DST)، ولبعض هذه التوابع حالات حدية (edge cases) عند تغيير التوقيت الصيفي. على سبيل المثال، قد لا يعيد التابع <code>seconds_since_midnight</code> التوقيت الحقيقي في مثل هذا اليوم.
 +
 +
=== الحسابات ===
 +
'''ملاحظة''': تُعرّف جميع التوابع التالية في active_support/core_ext/date_time/calculations.rb.
 +
 +
الصنف <code>DateTime</code> هو صنف فرعي من <code>Date</code>، لذا ترث عند تحميل active_support/core_ext/date/calculations.rb هذه التوابع وأسماءها البديلة، باستثناء أنها ستعيد دائمًا كائنًا من النوع <code>DateTime</code>.
 +
 +
يُعاد تعريف استخدام التوابع التالية بحيث لا تحتاج لتحميل active_support/core_ext/date/calculations.rb من أجلها:<syntaxhighlight lang="rails">
 +
beginning_of_day (midnight, at_midnight, at_beginning_of_day)
 +
end_of_day
 +
ago
 +
since (in)
 +
</syntaxhighlight>من ناحية أخرى، تجد أنّ <code>advance</code> و <code>change</code> مُعرّفان أيضًا ويدعمان المزيد من الخيارات، وهما موثّقان أدناه.
 +
 +
يعرّف استخدام التوابع التالية فقط في active_support/core_ext/date_time/calculations.rb حيث أن استخدامها منطقي فقط مع النسخة <code>DateTime</code>:<syntaxhighlight lang="rails">
 +
beginning_of_hour (at_beginning_of_hour)
 +
end_of_hour
 +
</syntaxhighlight>
 +
 +
==== التواريح والأوقات المسماة (Named Datetimes) ====
 +
 +
===== <code>DateTime.current</code> =====
 +
يعرّف [[Rails/active support|Active Support]] التابع <code>DateTime.current</code> ليكون مثل <code>Time.now.to_datetime</code> باستثناء أنه يحترم المنطقة الزمنية للمستخدم إن عُرّفت. كما أنه يعرّف <code>DateTime.yesterday</code> و <code>DateTime.tomorrow</code> وتوابع النسخة الخبرية (instance predicates) التاليين: <code>?past</code> و <code>?future</code> نسبةً إلى <code>DateTime.current</code>.
 +
 +
==== ملحقات أخرى ====
 +
 +
===== <code>seconds_since_midnight</code> =====
 +
يعيد <code>seconds_since_midnight</code> عدد الثواني منذ منتصف الليل:<syntaxhighlight lang="rails">
 +
now = DateTime.current    # => Mon, 07 Jun 2010 20:26:36 +0000
 +
now.seconds_since_midnight # => 73596
 +
</syntaxhighlight>
 +
 +
===== <code>utc</code> =====
 +
يمنحك التابع <code>utc</code> نفس التاريخ والوقت عند المستقبل ولكن بالتوقيت العالمي الموحد (UTC).<syntaxhighlight lang="rails">
 +
now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
 +
now.utc                # => Mon, 07 Jun 2010 23:27:52 +0000
 +
 +
</syntaxhighlight>يملك هذا التابع أيضًا اسمًا بديلًا هو <code>getutc</code>.
 +
 +
===== <code>?utc</code> =====
 +
يعلمنا التابع الخبري <code>?utc</code> إن كانت المنطقة الزمنية للمستقبل هي UTC:<syntaxhighlight lang="rails">
 +
now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
 +
now.utc?          # => false
 +
now.utc.utc?      # => true
 +
</syntaxhighlight>
 +
 +
===== <code>advance</code> =====
 +
الطريقة الأكثر بساطة للانتقال إلى تاريخ ووقت آخرين هو استعمال التابع <code>advance</code>. يأخذ هذا التابع [[Ruby/Hash|جدول Hash]] بالمفاتيح: <code>years:</code>، و <code>months:</code>، و <code>weeks:</code>، و <code>days:</code>، و <code>hours:</code>، و <code>minutes:</code>، و <code>seconds:</code> ويعيد تاريخ ووقت مُقدّم بقدر ما تشير إليه هذه المفاتيح.<syntaxhighlight lang="rails">
 +
d = DateTime.current
 +
# => Thu, 05 Aug 2010 11:33:31 +0000
 +
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
 +
# => Tue, 06 Sep 2011 12:34:32 +0000
 +
</syntaxhighlight>يحسب هذا التابع التاريخ المقصود أولًا عبر تمرير <code>years:</code> و <code>months:</code> و <code>weeks:</code> و <code>days:</code> إلى التابع <code>Date.advance</code> المذكور أعلاه. يضبط بعدها الوقت باستدعاء <code>since</code> مع عدد الثواني المراد تقديمها إلى الأمام. هذا الترتيب مهم إذ سيعطي أي ترتيب مختلف تواريخًا مختلفة في بعض الحالات الحدية. ينطبق المثال في <code>Date.advance</code> ويمكننا تمديده لاظهار مدى صلة الطلب ببتات (bits) الوقت.
 +
 +
إذا قمنا أولًا بتحريك بتات التاريخ (التي تملك أيضًا ترتيبًا نسبيًّا للمعالجة كما ذكر سابقًا) ثم بتات الوقت، نحصل على سبيل المثال على الحساب التالي:<syntaxhighlight lang="rails">
 +
d = DateTime.new(2010, 2, 28, 23, 59, 59)
 +
# => Sun, 28 Feb 2010 23:59:59 +0000
 +
d.advance(months: 1, seconds: 1)
 +
# => Mon, 29 Mar 2010 00:00:00 +0000
 +
 +
</syntaxhighlight>ولكن ستختلف النتيجة إن قمنا بحسابها بطريقة أخرى:<syntaxhighlight lang="rails">
 +
d.advance(seconds: 1).advance(months: 1)
 +
# => Thu, 01 Apr 2010 00:00:00 +0000
 +
 +
</syntaxhighlight>'''تحذير''': نظرًا لأن <code>DateTime</code> لا يعي التوقيت الصيفي (DST)، من الممكن أن تجد نفسك في نقطة غير موجودة بالوقت دون أي تحذير أو خطأ يخبرك بذلك.
 +
 +
==== تغيير المكونات ====
 +
يسمح لك التابع <code>change</code> بالحصول على تاريخ ووقت جديد مماثل للمستقبل باستثناء الخيارات المحدّدة والتي قد تتضمّن: <code>year:</code> و <code>month:</code> و <code>day:</code> و <code>hour:</code> و <code>min:</code> و <code>sec:</code> و <code>offset:</code> و <code>start:</code>:<syntaxhighlight lang="rails">
 +
now = DateTime.current
 +
# => Tue, 08 Jun 2010 01:56:22 +0000
 +
now.change(year: 2011, offset: Rational(-6, 24))
 +
# => Wed, 08 Jun 2011 01:56:22 -0600
 +
 +
</syntaxhighlight>تكون الدقائق والثواني صفرًا إن كانت الساعات صفرًا أيضًا (ما لم يُعطوا قيمًا):<syntaxhighlight lang="rails">
 +
now.change(hour: 0)
 +
# => Tue, 08 Jun 2010 00:00:00 +0000
 +
</syntaxhighlight>وبشكل مماثل، إن كانت الدقائق صفرًا، ينطبق نفس الشيء على الثواني أيضًا (ما لم تُعطَ قيمة):<syntaxhighlight lang="rails">
 +
now.change(min: 0)
 +
# => Tue, 08 Jun 2010 01:00:00 +0000
 +
</syntaxhighlight>لا يتقبّل هذا التابع التواريخ غير الموجودة، إذ يرمي الاستثناء <code>[[Ruby/ArgumentError|ArgumentError]]</code> إن لم يكن التغيير صحيحًا:<syntaxhighlight lang="rails">
 +
DateTime.current.change(month: 2, day: 30)
 +
# => ArgumentError: invalid date
 +
</syntaxhighlight>
 +
 +
==== الفترات ====
 +
يمكن إضافة الفترات وطرحها من التواريخ بالشكل التالي:<syntaxhighlight lang="rails">
 +
now = DateTime.current
 +
# => Mon, 09 Aug 2010 23:15:17 +0000
 +
now + 1.year
 +
# => Tue, 09 Aug 2011 23:15:17 +0000
 +
now - 1.week
 +
# => Mon, 02 Aug 2010 23:15:17 +0000
 +
 +
</syntaxhighlight>وهي تحول إلى نداءات إلى <code>since</code> أو <code>advance</code>. نحصل هنا على سبيل المثال على القفزة الصحيحة في إصلاح التقويم (calendar reform):<syntaxhighlight lang="rails">
 +
DateTime.new(1582, 10, 4, 23) + 1.hour
 +
# => Fri, 15 Oct 1582 00:00:00 +0000
 +
 +
</syntaxhighlight>
 +
 +
== ملحقات للوقت ==
 +
 +
=== الحسابات ===
 +
'''ملاحظة''': تُعرّف جميع التوابع التالية في active_support/core_ext/time/calculations.rb.<syntaxhighlight lang="rails">
 +
past?
 +
today?
 +
future?
 +
yesterday
 +
tomorrow
 +
seconds_since_midnight
 +
change
 +
advance
 +
ago
 +
since (in)
 +
prev_day
 +
next_day
 +
beginning_of_day (midnight, at_midnight, at_beginning_of_day)
 +
end_of_day
 +
beginning_of_hour (at_beginning_of_hour)
 +
end_of_hour
 +
beginning_of_week (at_beginning_of_week)
 +
end_of_week (at_end_of_week)
 +
monday
 +
sunday
 +
weeks_ago
 +
prev_week (last_week)
 +
next_week
 +
months_ago
 +
months_since
 +
beginning_of_month (at_beginning_of_month)
 +
end_of_month (at_end_of_month)
 +
prev_month
 +
next_month
 +
last_month
 +
beginning_of_quarter (at_beginning_of_quarter)
 +
end_of_quarter (at_end_of_quarter)
 +
beginning_of_year (at_beginning_of_year)
 +
end_of_year (at_end_of_year)
 +
years_ago
 +
years_since
 +
prev_year
 +
last_year
 +
next_year
 +
on_weekday?
 +
on_weekend?
 +
</syntaxhighlight>هذه التوابع متماثلة. يُرجى الرجوع إلى توثيق كلٍّ منهم أعلاه مع مراعاة الاختلافات التاليّة:
 +
* يقبل التابع <code>change</code> خيارًا إضافيًا هو <code>usec:</code>
 +
* يعي <code>Time</code> التوقيت الصيفي (DST) بحيث تحصل على حسابات التوقيت الصيفي الصحيحة
 +
<syntaxhighlight lang="rails">
 +
Time.zone_default
 +
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
 +
 +
# In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to DST.
 +
t = Time.local(2010, 3, 28, 1, 59, 59)
 +
# => Sun Mar 28 01:59:59 +0100 2010
 +
t.advance(seconds: 1)
 +
# => Sun Mar 28 03:00:00 +0200 2010
 +
</syntaxhighlight>
 +
* إن قفز التابع <code>since</code> أو التابع <code>ago</code> إلى وقت لا يمكن التعبير عنه مع <code>Time</code>، فسيعاد الكائن <code>DateTime</code> بدل ذلك.
 +
 +
==== <code>Time.current</code> ====
 +
يعرّف [[Rails/active support|Active Support]] التابع <code>Time.current</code> ليكون يومًا في المنطقة الزمنيّة الحاليّة. أي مثل <code>Time.now</code> باستثناء أنّه يحترم المنطقة الزمنية للمستخدم إن عُرّفت. كما يعرِّف توابع النسخة الخبرية <code>?past</code> و <code>?today</code> و <code>?future</code> نسبةً إلى <code>Time.current</code>.
 +
 +
عند إنشاء مقارنات للوقت <code>Time</code> باستعمال توابع تحترم المنطقة الزمنية للمستخدم، تأكد من استعمال <code>Time.current</code> عوضًا عن <code>Time.now</code>. هنالك حالات تقارن فيها المنطقة الزمنية للمستخدم في المستقبل مع المنطقة الزمنية للنظام، إذ يستخدم <code>Time.now</code> افتراضيًا. هذا يعني أن <code>Time.now.to_date</code> قد يساوي <code>Date.yesterday</code>.
 +
 +
==== <code>all_day</code> و <code>all_week</code> و <code>all_month</code> و <code>all_quarter</code> و <code>all_year</code> ====
 +
يعيد التابع <code>all_day</code> مجالًا يمثّل يومًا كاملًا من الوقت الحالي.<syntaxhighlight lang="rails">
 +
now = Time.current
 +
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
 +
now.all_day
 +
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:59:59 UTC +00:00
 +
</syntaxhighlight>وبشكل مماثل، تخدم التوابع <code>all_week</code> و <code>all_month</code> و <code>all_quarter</code> و <code>all_year</code> غرض توليد مجالاتٍ زمنيةٍ.<syntaxhighlight lang="rails">
 +
now = Time.current
 +
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
 +
now.all_week
 +
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
 +
now.all_week(:sunday)
 +
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
 +
now.all_month
 +
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
 +
now.all_quarter
 +
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00
 +
now.all_year
 +
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00
 +
</syntaxhighlight>
 +
 +
==== <code>prev_day</code> و <code>next_day</code> ====
 +
في الإصدار 1.9 من [[Ruby|روبي]]، يعيد التابعان <code>prev_day</code> و <code>next_day</code> التاريخ في اليوم السابق والتالي على التوالي:<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
 +
d.prev_day              # => Fri, 07 May 2010
 +
d.next_day              # => Sun, 09 May 2010
 +
</syntaxhighlight>
 +
 +
==== <code>prev_month</code> و <code>next_month</code> ====
 +
في الإصدار 1.9 من [[Ruby|روبي]]، يعيد التابعان <code>prev_month</code> و <code>next_month</code> التاريخ باليوم نفسه من الشهر الماضي والتالي على التوالي:<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
 +
d.prev_month            # => Thu, 08 Apr 2010
 +
d.next_month            # => Tue, 08 Jun 2010
 +
 +
</syntaxhighlight>إن لم يوجد مثل هذا اليوم، يعاد اليوم الأخير من الشهر المقابل:<syntaxhighlight lang="rails">
 +
Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000
 +
Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000
 +
Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000
 +
Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000
 +
</syntaxhighlight>
 +
 +
==== <code>prev_year</code> و <code>next_year</code> ====
 +
في الإصدار 1.9 من [[Ruby|روبي]]، يعيد التابعان <code>prev_year</code> و <code>next_year</code> التاريخ باليوم والشهر نفسه من السنة الماضية والتالية على التوالي:<syntaxhighlight lang="rails">
 +
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
 +
d.prev_year              # => Fri, 08 May 2009
 +
d.next_year              # => Sun, 08 May 2011
 +
 +
</syntaxhighlight>إن كان التاريخ 29 شباط من سنة كبيسة، ستحصل على التاريخ 28 شباط:<syntaxhighlight lang="rails">
 +
d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000
 +
d.prev_year              # => Sun, 28 Feb 1999
 +
d.next_year              # => Wed, 28 Feb 2001
 +
 +
</syntaxhighlight>
 +
 +
==== <code>prev_quarter</code> و <code>next_quarter</code> ====
 +
يعيد التابعان <code>prev_quarter</code> و <code>next_quarter</code> التاريخ نفسه باليوم نفسه في الربع السابق والتالي:<syntaxhighlight lang="rails">
 +
t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300
 +
t.prev_quarter            # => 2010-02-08 00:00:00 +0200
 +
t.next_quarter            # => 2010-08-08 00:00:00 +0300
 +
 +
</syntaxhighlight>إن لم يوجد مثل هذا اليوم، يعاد اليوم الأخير من الشهر المقابل:<syntaxhighlight lang="rails">
 +
Time.local(2000, 7, 31).prev_quarter  # => 2000-04-30 00:00:00 +0300
 +
Time.local(2000, 5, 31).prev_quarter  # => 2000-02-29 00:00:00 +0200
 +
Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300
 +
Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200
 +
</syntaxhighlight>التابع <code>prev_quarter</code> هو اسم بديل للتابع <code>last_quarter</code>.
 +
=== بانو الوقت (Time Constructors) ===
 +
يعرّف [[Rails/active support|Active Support]] التابع <code>Time.current</code> ليكون <code>Time.zone.now</code> إذا وُجد تعريف للمنطقة الزمنية للمستخدم، مع الرجوع إلى <code>Time.now</code>:<syntaxhighlight lang="rails">
 +
Time.zone_default
 +
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
 +
Time.current
 +
# => Fri, 06 Aug 2010 17:11:58 CEST +02:00
 +
</syntaxhighlight>يعمل التابعان الخبريان <code>?past</code> و <code>?future</code> نسبةً إلى <code>Time.current</code> مثل <code>DateTime</code>.
 +
 +
إن وقع الوقت المُراد بناؤه خارج النطاق المدعوم من قبل <code>Time</code> في منصّة التشغيل (runtime platform)، فيُتجاهل usecs ويعاد الكائن <code>DateTime</code> بدلًا من ذلك.
 +
 +
==== الفترات ====
 +
يمكن إضافة فترات وطرحها من كائنات الوقت <code>Time</code> بالشكل التالي:<syntaxhighlight lang="rails">
 +
now = Time.current
 +
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
 +
now + 1.year
 +
# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
 +
now - 1.week
 +
# => Mon, 02 Aug 2010 23:21:11 UTC +00:00
 +
 +
</syntaxhighlight>الفترات تحول إلى استدعاءات إلى <code>since</code> أو <code>advance</code>. نحصل على سبيل المثال على القفزة الصحيحة في إصلاح التقويم:<syntaxhighlight lang="rails">
 +
Time.utc(1582, 10, 3) + 5.days
 +
# => Mon Oct 18 00:00:00 UTC 1582
 +
 +
</syntaxhighlight>
 +
 +
== ملحقات للملفات ==
 +
 +
=== التابع <code>atomic_write</code> ===
 +
تستطيع باستخدام التابع <code>File.atomic_write</code> الكتابة على ملف بطريقة تمنع أي قارئ من رؤية نصف المحتوى المكتوب.
 +
 +
يُمرّر اسم الملف كوسيط وينتج التابع مقبض ملف (file handle) للكتابة. بمجرد الانتهاء من تنفيذ الكتلة، يغلق <code>atomic_write</code> مقبض الملف ويُكمل وظيفته.
 +
 +
يستخدم [[Rails/action pack|Action Pack]] على سبيل المثال التابع لكتابة ملّفات ذاكرة التخزين المؤقت للأصول (asset cache files) مثل <code>all.css</code>:<syntaxhighlight lang="rails">
 +
File.atomic_write(joined_asset_path) do |cache|
 +
  cache.write(join_asset_file_contents(asset_paths))
 +
end
 +
</syntaxhighlight>ولإنجاز هذا الأمر، يُنشئ <code>atomic_write</code> ملفًّا مؤقّتًا. هذا هو الملف الذي تكتب عليه الشيفرة في الواقع. يُعاد عند الانتهاء تسمية الملف المؤقّت، وهي عمليّة ذريّة (atomic operation) على أنظمة POSIX. إن وُجد الملف الهدف، يُعيد <code>atomic_write</code> تعريفه مع الابقاء على المالكين والأذونات (permissions).
 +
 +
ومع ذلك، تظل حالات قليلة لا يقدر <code>atomic_write</code> فيها على تغيير ملكيّة أو إذن الملف فيها؛ يُكتشف هذا الخطأ ويُتجاوَز بسبب الثقة في المستخدم/نظام الملفّات للتأكد من إمكانية الوصول إلى العمليّات (processes) التي يحتاج إليها.
 +
 +
'''ملاحظة''': إن احتوى الملف المُستهدف على ACL، سيُعاد احتسابه/تعديل هذا ACL بسبب العمليّة <code>chmod</code> التي يُنفذّها <code>atomic_write</code>.
 +
 +
'''تحذير''': لاحظ أنك لا تستطيع الإضافة (append) مع <code>atomic_write</code>.
 +
 +
يُكتب الملف الإضافي داخل مجلّد قياسي (standard directory) للملفّات المؤقّتة، ولكن تستطيع تمرير مجلّد من اختيارك كوسيط ثاني.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/file/atomic.rb active_support/core_ext/file/atomic.rb].
 +
 +
== ملحقات للوحدة <code>[[Ruby/Marshal|Marshal]]</code> ==
 +
 +
=== التابع <code>load</code> ===
 +
يضيف [[Rails/active support|Active Support]] دعم تحميل تلقائي ثابت (constant autoloading) إلى <code>load</code>.
 +
 +
يفكّ مخزن ذاكرة التخزين المؤقت على سبيل المثال السلسَلَة على هذا النحو:<syntaxhighlight lang="rails">
 +
File.open(file_name) { |f| Marshal.load(f) }
 +
</syntaxhighlight>إن أشارت البيانات المخزّنة مؤقّتًا لثابت غير معروف، تُشغَّل آليّة التحميل التلقائي بهذه المرحلة؛ وإن نجحت، تُعاد محاولة فكّ السَلسَلة بشفافيّة.
 +
 +
'''تحذير''': يجب، إن كان الوسيط كائنٌ من النوع <code>[[Ruby/IO|IO]]</code>، أن يستجيب إلى <code>rewind</code> كي يتمكّن من إعادة المحاولة. تستجيب الملفات العاديّة إلى <code>rewind</code>.
 +
 +
'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/marshal.rb active_support/core_ext/marshal.rb].
 +
 +
== ملحقات للاستثناء <code>[[Ruby/NameError|NameError]]</code> ==
 +
يضيف [[Rails/active support|Active Support]] التابع <code>?missing_name</code> للاستثناء <code>[[Ruby/NameError|NameError]]</code>، والذي يختبر ما إذا رُفع الاستثناء بسبب الاسم الذي مُرّر كوسيط.
 +
 +
قد يُعطى الاسم كرمز أو سلسلة نصيّة. يُختَبر الرمز مقابل اسم الثابت المجرّد (bare constant name) والسلسلة النصيّة مقابل اسم ثابت مؤهل بالكامل (bare constant name).
 +
 +
'''تنبيه''': يمكن أن يمثل الرّمز اسمًا ثابتًا مؤهّلًا بالكامل كما في: "ActiveRecord::Base" ، لذلك يُعرّف سلوك الرموز للتبسيط لا لوجوب كونه هكذا تقنيًا.
 +
 +
عند استدعاء إجراء من وحدة التحكم <code>ArticlesController</code> على سبيل المثال، يحاول ريلز بتفاؤل استخدام <code>ArticlesHelper</code>. لا مشكلة في عدم وجود الوحدة المساعدة؛ لذلك، إن رُفع استثناء لذلك الاسم الثابت، فيجب إسكاته. ولكن من الممكن أن يرفع articles_helper.rb الاستثناء <code>[[Ruby/NameError|NameError]]</code> بسبب ثابت غير معروف فعلًا. ذلك الاستثناء يجب إعادة رفعه. يوفّر التابع <code>?missing_name</code> طريقة للتمييز بين الحالتين:<syntaxhighlight lang="rails">
 +
def default_helper_module!
 +
  module_name = name.sub(/Controller$/, '')
 +
  module_path = module_name.underscore
 +
  helper module_path
 +
rescue LoadError => e
 +
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
 +
rescue NameError => e
 +
  raise e unless e.missing_name? "#{module_name}Helper"
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/name_error.rb active_support/core_ext/name_error.rb].
 +
== ملحقات للاستثناء <code>[[Ruby/LoadError|LoadError]]</code> ==
 +
يضيف [[Rails/active support|Active Support]] التابع <code>?is_missing</code> إلى <code>[[Ruby/LoadError|LoadError]]</code>.
 +
 +
يتحقق التابع <code>?is_missing</code> عندما يُعطى اسم مسار إن رُفع الاستثناء بسبب ذلك الملف المعيّن (باستثناء الملفات ذات الامتداد ".rb" ربّما).
 +
 +
عند استدعاء إجراء من وحدة التحكم <code>ArticlesController</code> مثلًا، يحاول ريلز تحميل articles_helper.rb، ولكن قد لا يوجد هذا الملف. هذا جيّد، فوحدة المُساعد ليست إلزامية كي يُسكت ريلز خطأَ تحميل. ولكن من الممكن أن توجد الوحدة المُساعدة وتطلّب بدورها مكتبة أخرى مفقودة. يجب في هذه الحالة على ريلز إعادة رفع الاستثناء. يوفّر التابع <code>?is_missing</code> طريقة للتمييز بين كلتا الحالتين:<syntaxhighlight lang="rails">
 +
def default_helper_module!
 +
  module_name = name.sub(/Controller$/, '')
 +
  module_path = module_name.underscore
 +
  helper module_path
 +
rescue LoadError => e
 +
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
 +
rescue NameError => e
 +
  raise e unless e.missing_name? "#{module_name}Helper"
 +
end
 +
</syntaxhighlight>'''ملاحظة''': مُعرّف في [https://github.com/rails/rails/tree/v5.2.2/activesupport/lib/active_support/core_ext/load_error.rb active_support/core_ext/load_error.rb].
 
== مصادر ==
 
== مصادر ==
* [https://guides.rubyonrails.org/active_support_core_extensions.html صفحة Active Support Core Extensions في توثيق Ruby On ريلز الرسمي.]
+
* [https://guides.rubyonrails.org/active_support_core_extensions.html صفحة Active Support Core Extensions في توثيق Ruby On Rails الرسمي.]

المراجعة الحالية بتاريخ 06:23، 25 مارس 2019

Active Support هو مكوّن ريلز المسؤول عن توفير ملحقات لغة روبي والأدوات المساعدة والأشياء الأخرى المعترضة.

كما يوفر حدًا أدنى من المعرفة الواسعة على مستوى اللغة يستهدف كل من تطوير تطبيقات ريلز، وتطوير ريلز نفسه. بعد قراءة هذا الدليل، ستتعلم:

  • ماهيّة الملحقات الأساسية
  • كيفية تحميل كل الملحقات.
  • كيفية انتقاء الملحقات التي تريدها فقط.
  • ماهيّ الملحقات التي يوفرها Active Support.

كيفية تحميل الملحقات الأساسية

Active Support المستقل

لا يحمّل Active Support أي شيء افتراضيًّا من أجل عدم شغل أية مساحة بل يُقسَّم لأجزاء صغيرة بحيث تستطيع تحميل ما تحتاجه فقط؛ كما أنه يملك بعض نقاط انطلاق ملائمة لتحميل الملحقات ذات الصلة مرّة واحدة أو حتى كل الملحقات.

يكون ذلك بعد استعمال الأمر require البسيط:

require 'active_support'

لا تستجيب الكائنات (objects) حتى مع ?blank. فلننظر لكيفيّة تحميله لتعريفه (definition).

انتقاء تعريف

أخف طريقة للحصول على ?blank هو انتقاء الملف الذي يُعرّفه.

من أجل كل تابع مُعرّف كملحق أساسي، يحتوي هذا الدليل على ملاحظة توضح أين عُرّف مثل هذا التابع. في حالة ?blank، تقول الملاحظة:

ملاحظة: عُرِّف في active_support/core_ext/object/blank.rb.

ممّا يعني أنك تستطيع طلبه على النحو التالي:

require 'active_support'
require 'active_support/core_ext/object/blank'

عُدّل "Active Support" بعناية بحيث لا يُحمّل انتقاء ملف (مثل المثال السابق) إلّا الاعتماديّات المطلوبة بشدّة إن وُجدَت.

تحميل الملحقات الأساسية المجمعة

المستوى التالي هو ببساطة تحميل جميع الملحقات إلى Object. كقاعدة عامة، تتوفّر الملحقات لـ SomeClass مرّة واحدة عن طريق تحميل active_support/core_ext/some_class.

وبالتالي تحميل جميع الملحقات لـ Object (بما في ذلك ?blank) يكون بالشكل التالي:

require 'active_support'
require 'active_support/core_ext/object'

تحميل جميع الملحقات الأساسية

قد تفضل ببساطة تحميل جميع الملحقات الأساسية وهناك ملف لذلك:

require 'active_support'
require 'active_support/core_ext'

تحميل جميع Active Support

وأخيرًا، إن رغبت في توفير Active Support كاملًا، ما عليك سوى كتابة:

require 'active_support/all'

هذا لا يضع فعلًا Active Support بأكمله في الذاكرة مسبقًا، بل تُعدُّ بعض الأشياء عن طريق autoload، لذلك تُحمّل فقط إن استُخدمت.

Active Support ضمن تطبيق ريلز

يحمّل تطبيق ريلز كل Active Support ما لم تكن قيمة config.active_support.bare مساويةً إلى true. سُيحمّل التطبيق في هذه الحالة ما ينتقيه الإطار نفسه لاحتياجاته الخاصة فقط، ويظل بإمكانه الانتقاء بنفسه على أي مستوى من مستويات التقسيم (granularity level)، كما وُضّح في القسم السابق.

ملحقات لجميع الكائنات

التابعان ?blank و ?present

تُعتبر القيم التالية فارغة في تطبيق ريلز:

تنويه: تستخدم التوابع الخبرية (predicate) للسلاسل النصيّة صنف الحرف الواعي بترميز اليونيكود [:space:]، لذلك يُعتبر U+2029 (فاصل الفقرات) مثلًا مسافة بيضاء.

تحذير: لاحظ عدم ذكر الأعداد فيما سبق، إذ لا يعد العدد 0 و 0.0 على وجه الخصوص قيمةً فارغةً.

على سبيل المثال، يستخدم هذا التابع من ActionController::HttpAuthentication::Token::ControllerMethods التابع ?blank للتحقّق من وجود أي شيء (token):

def authenticate(controller, &login_procedure)
  token, options = token_and_options(controller.request)
  unless token.blank?
    login_procedure.call(token, options)
  end
end

يُعادل التابع ?present التابع ?blank!. هذا المثال مُقتطف من ActionDispatch::Http::Cache::Response:

def set_conditional_cache_control!
  return if self["Cache-Control"].present?
  ...
end

ملاحظة: مُعرّف في active_support/core_ext/object/blank.rb.

التابع presence

يعيد التابع presence مُستقبِله في حالة تحقق ?present أو يعيد nil في ما عدا ذلك. وهو مفيد بالتعابير مثل هذه:

host = config[:host].presence || 'localhost'

ملاحظة: مُعرّف في active_support/core_ext/object/blank.rb.

التابع ?duplicable

يمكن تكرار معظم الكائنات عبر dup أو clone في روبي 2.4 باستثناء التوابع وأرقام معينة. على الرغم من أن الإصدار 2.2 و 2.3 من روبي لا يقدران على تكرار nil، و false، و true والرموز إضافةً لنُسخ (instances) مثل Float و Fixnum و Bignum.

"foo".dup           # => "foo"
"".dup              # => ""
1.method(:+).dup    # => TypeError: allocator undefined for Method
Complex(0).dup      # => TypeError: can't copy Complex

يُوفّر Active Support التابع ?duplicable لاستعلام كائن حول هذا:

"foo".duplicable?           # => true
"".duplicable?              # => true
Rational(1).duplicable?     # => false
Complex(1).duplicable?      # => false
1.method(:+).duplicable?    # => false

يطابق التابع ?duplicable التابع dup، حسب إصدار روبي المتوفر. لذلك نجد في الإصدار 2.4:

nil.dup                 # => nil
:my_symbol.dup          # => :my_symbol
1.dup                   # => 1
 
nil.duplicable?         # => true
:my_symbol.duplicable?  # => true
1.duplicable?           # => true

بينما نجد في الإصدار 2.2 والإصدار 2.3:

nil.dup                 # => TypeError: can't dup NilClass
:my_symbol.dup          # => TypeError: can't dup Symbol
1.dup                   # => TypeError: can't dup Fixnum
 
nil.duplicable?         # => false
:my_symbol.duplicable?  # => false
1.duplicable?           # => false

تحذير: يقدر أي صنف على منع التكرار عبر حذف التابع dup والتابع clone منه أو إطلاق استثناء منهما. وبالتالي، فقط التابع rescue يقدر على معرفة ما إذا كان كائن ما قابلًا للتكرار. يعتمد التابع ?duplicable على القائمة الثابتة (hard-coded list) أعلاه لكنه أسرع بكثير من rescue. استخدمه فقط إن تيقنت أن القائمة الثابتة كافية في حالة استخدامك.

ملاحظة: مُعرّف في active_support/core_ext/object/duplicable.rb.

التابع deep_dup

يعيد التابع deep_dup نسخة عميقة (deep copy) من كائن معين. عادةً، عندما تستخدم dup على كائن يحتوي على كائنات أخرى، لا يمتد التأثير لهم، لذلك ينشئ روبي نسخة ضحلة (shallow copy) من الكائن. على سبيل المثال، إن كانت لديك مصفوفة تحتوي على سلسلة نصيّة، فستبدو كالتالي:

array     = ['string']
duplicate = array.dup
 
duplicate.push 'another-string'
 
# the object was duplicated, so the element was added only to the duplicate
array     # => ['string']
duplicate # => ['string', 'another-string']
 
duplicate.first.gsub!('string', 'foo')
 
# first element was not duplicated, it will be changed in both arrays
array     # => ['foo']
duplicate # => ['foo', 'another-string']

حصلنا كما ترى على كائن آخر بعد تكرار نسخة Array بحيث يمكننا تعديلها بينما يظل الكائن الأصلي بدون تغيير. هذا لا ينطبق على عناصر المصفوفة. بما أن التابع dup لا ينشئ نسخة عميقة، تظل السلسلة داخل المصفوفة نفس الكائن. إن احتجت لنسخة عميقة من كائن ما، عليك استخدام التابع deep_dup. على سبيل المثال:

array     = ['string']
duplicate = array.deep_dup
 
duplicate.first.gsub!('string', 'foo')
 
array     # => ['string']
duplicate # => ['foo']

سيعيد التابع deep_dup الكائن كما هو إن كان غير قابل للتكرار:

number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id   # => true

ملاحظة: مُعرّف في active_support/core_ext/object/deep_dup.rb.

التابع try

أبسط طريقة لمناداة تابع على كائن ليس nil هي بالعبارات الشرطية لكنها تضيف فوضى غير ضرورية. البديل هو استخدام التابع try. التابع try يماثل Object.send إلا أنه يعيد القيمة nil إن أُرسل إلى nil.

على سبيل المثال:

# try بدون
unless @number.nil?
  @number.next
end
 
# try مع
@number.try(:next)

مثال آخر هو هذه التعليمات البرمجيّة من ActiveRecord::ConnectionAdapters::AbstractAdapter حيث يمكن أن تكون قيمة ‎@logger هي nil. تستطيع أن ترى أن التعليمات البرمجيّة تستخدم التابع try وتتجنب بذلك إجراء أي اختبار غير ضروري:

def log_info(sql, name, ms)
  if @logger.try(:debug?)
    name = '%s (%.1fms)' % [name || 'SQL', ms]
    @logger.debug(format_log_entry(name, sql.squeeze(' ')))
  end
end

يمكن أيضًا مناداة التابع try بدون أي وسيط (arguments) لكن مع كتلة (block) تُنفّذ فقط إن لم يكن الكائن هو nil:

@person.try { |p| "#{p.first_name} #{p.last_name}" }

لاحظ كيف أن التابع try "سيبتلع" أخطاء غياب التابع (no-method errors) مع إعادة nil عوض ذلك. إن كنت تريد الحماية من الأخطاء المطبعية، فاستخدم التابع try!‎:

@number.try(:nest)  # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer

ملاحظة: مُعرّف في active_support/core_ext/object/try.rb.

التابع class_eval(*args, &block)‎

تستطيع تقييم التعليمة البرمجيّة في سياق أي صنف منفرد (singleton class) لأي كائن باستخدام التابع class_eval:

class Proc
  def bind(object)
    block, time = self, Time.current
    object.class_eval do
      method_name = "__bind_#{time.to_i}_#{time.usec}"
      define_method(method_name, &block)
      method = instance_method(method_name)
      remove_method(method_name)
      method
    end.bind(object)
  end
end

ملاحظة: مُعرّف في active_support/core_ext/kernel/singleton_class.rb.

التابع (acts_like?(duck

يوفر التابع ?acts_like طريقةً للتحقق من كون صنف ما يعمل مثل صنف آخر بناءً على عُرفٍ بسيط هو: يعرف صنفٌ يُوفّر نفس الواجهة التي يوفرها String بالشكل:

def acts_like_string?
end

وهي مجرّد علامة، إذ شكله والقيمة التي يعيدها غير ذي صلة. بعد ذلك، تستطيع تعليمات العميل البرمجيّة (client code) الاستعلام للتحقّق من أمان النوع (duck type safeness) بهذه الطريقة:

some_klass.acts_like?(:string)

ملاحظة: مُعرّف في active_support/core_ext/object/acts_like.rb.

التابع to_param

تستجيب كل الكائنات في ريلز للتابع to_param المُصمّم لإعادة شيء يمثلهم كقيم في سلسلة استعلام أو كأجزاء URL.

يستدعي التابع to_param التابع to_s افتراضيًّا:

7.to_param # => "7"

لا يجب تهريب القيمة التي يعيدها التابع to_param:

"Tom & Jerry".to_param # => "Tom & Jerry"

تعيد عدّة أصناف في ريلز تعريف هذا التابع. على سبيل المثال، تعيد الكائنات nil و true و false أنفسها. يستدعي Array.to_param التابع to_param على العناصر ثم يجمع النتائج مع المحرف "/":

[0, true, String].to_param # => "0/true/String"

الجدير بالذكر هو أن نظام توجيه إطار العمل ريلز يستدعي التابع to_param على النماذج (models) للحصول على قيمة المحتوى النائب (placeholder). يعيد ActiveRecord::Base.to_param مُعرّف النموذج (id) لكن بإمكانك تغيير سلوك ذاك التابع في نماذجك. مثلًا انطلاقًا من:

class User
  def to_param
    "#{id}-#{name.parameterize}"
  end
end

نتحصّل على:

user_path(@user) # => "/users/357-john-smith"

تحذير: يجب أن تكون وحدات التحكّم على دراية بأية إعادة تعريف للتابع to_param لأنه عند قدوم طلب مثل المثال السابق، ستكون السلسلة "‎357-john-smith" هي قيمة params[:id]‎.

ملاحظة: مُعرّف في active_support/core_ext/object/to_param.rb.

التابع to_query

باستثناء الجداول Hash، عندما تعطي هذا التابع القيمة key دون تهريب (unescaped)، يبني جزءًا من سلسلة الاستعلام النصيّة التي ستعيِّن المفتاح (key) إلى ما يعيده التابع to_param. مثلًا، انطلاقًا من:

class User
  def to_param
    "#{id}-#{name.parameterize}"
  end
end

نتحصّل على:

current_user.to_query('user') # => "user=357-john-smith"

يُهرّب هذا التابع ما يحتاجه من كلا المفتاح (key) والقيمة (value):

account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"

بحيث يكون خَرجُها (output) جاهزًا للاستخدام في سلسلة استعلام نصيّة. تعيد المصفوفات نتيجة تطبيق to_query على كل عنصر باستخدام []key كمفتاح ثم تجمع النتيجة مع "&":

[3.4, -45.6].to_query('sample')
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"

كما تستجيب الجداول Hash إلى to_query ولكن مع توقيع مختلف. إذا لم يتم تمرير أي وسيط، تقوم إحدى الاستدعاءات بتوليد سلسلة مرتَّبة من تعيينات المفاتيح/القيم التي تستدعي to_query(key)‎ على قيمها. بعد ذلك، تَجمَع النتيجة مع "&":

{c: 3, b: 2, a: 1}.to_query # => "a=1&b=2&c=3"

يقبل التابع Hash.to_query مجال اسم اختياري للمفاتيح:

{id: 89, name: "John Smith"}.to_query('user')
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"

ملاحظة: مُعرّف في active_support/core_ext/object/to_query.rb.

التابع With_options

يُوفّر التابع with_options طريقة لاحتساب الخيارات الشائعة في سلسلة من استدعاءات التابع.

يمنح with_options كائنًا وكيلًا (proxy object) إلى كتلة لمّا يُعطى جدول hash للخيارات الافتراضية. وداخل الكتلة، يُعاد توجيه التوابع التي استدعيت على الوكيل إلى المُسقبِل مع دمج خياراته. وهكذا تتخلّص مثلًا من التكرارات في:

class Account < ApplicationRecord
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
  has_many :invoices,  dependent: :destroy
  has_many :expenses,  dependent: :destroy
end

بهذه الطريقة:

class Account < ApplicationRecord
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end

قد يُعبّر هذا المصطلح عن "التجميع" (grouping) للقارئ أيضًا. لنفترض مثلًا أنك تريد إرسال رسالة إخبارية تعتمد لُغتها على لغة المستخدم. تستطيع بموضع ما من المٌرسِل (mailer) تجميع أجزاء مُعتمدة على الإعدادات المحلية على النحو التالي:

I18n.with_options locale: user.locale, scope: "newsletter" do |i18n|
  subject i18n.t :subject
  body    i18n.t :body, user_name: user.name
end

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

ملاحظة: مُعرّف في active_support/core_ext/object/with_options.rb.

دعم JSON

يُوفّر Active Support تعريف استخدام (implementation) أفضل للتابع to_json من جوهرة json (أي json gem) التي تُوفّر عادةً كائنات روبي. وذلك لأن بعض الأصناف، مثل Hash و OrderedHash و Process::Status تحتاج لمعالجة خاصّة كي تُوفّر تمثيل JSON مناسب.

ملاحظة: مُعرّف في active_support/core_ext/object/json.rb.

متغيرات النسخة (Instance Variables)

يُوفّر Active Support عدّة توابع لتيسير الوصول إلى متغيرات النسخة.

Instance_values

يعيد التابع instance_values جدول Hash يعين أسماء مُتغّيرات النسخة بدون "@" إلى قيمها المقابلة. المفاتيح هي سلاسل نصيّة:

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end
 
C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}

ملاحظة: مُعرّف في active_support/core_ext/object/instance_variables.rb.

Instance_variable_names

يعيد التابع instance_variable_names مصفوفة. يتضمّن كل اسم فيها العلامة "@".

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end
 
C.new(0, 1).instance_variable_names # => ["@x", "@y"]

ملاحظة: مُعرّف في active_support/core_ext/object/instance_variables.rb.

إسكات التحذيرات والاستثناءات

يُغيّر التابعان silence_warnings و enable_warnings قيمة VERBOSE$ كما يُناسب خلال فترة الكتلة الخاصة بهما ثم يعيدان ضبطها (reset) بعد ذلك:

silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }

إسكات الاستثناءات مُمكن أيضًا مع suppress. يتلقى هذا التابع عددًا عشوائيًّا من أصناف الاستثناءات. إن رُفع استثناءٌ أثناء تنفيذ الكتلة وكان ?kind_of أيًّا من الوسائط، فسيلتقطها suppress ويعيد بصمت. خلا ذلك، لن يُلتقَط الاستثناء:

# ًإن قُفِل المُستخدم يُفقد مُعامل الزيادة لكن هذا ليس مشكلة
suppress(ActiveRecord::StaleObjectError) do
 current_user.increment! :visits
end

ملاحظة: مُعرّف في active_support/core_ext/kernel/reporting.rb.

التابع ?in

يختبر التابع الخبري ?in (أي predicate) كون كائن ما داخل كائن آخر. سيطلق الاستثناء ArgumentError إن لم يستجب الوسيط المُمرّر لـ ?include.

إليك المثال التالي:

1.in?([1,2])        # => true
"lo".in?("hello")   # => true
25.in?(30..50)      # => false
1.in?(1)            # => ArgumentError

ملاحظة: مُعرّف في active_support/core_ext/object/inclusion.rb.

ملحقات للوحدات

الخاصيات (Attributes)

التابع Alias_attribute

تمتلك خاصيّات النموذج قارئًا، وكاتبًا، وتابعًا خبريًّا (predicate). تستطيع إنشاء اسم بديل لخاصيّة نموذج تملك هذه التوابع الثلاثة المعرَّفة من أجلك مرّةً واحدةً. كما هو الحال في توابع التسمية البديلة الأخرى، يكون الاسم الجديد هو الوسيط الأول والاسم القديم هو الثاني (يمكنك تذكّر ترتيبها بتذكّر ترتيب أي عمليّة إسناد):

class User < ApplicationRecord
 # "login" تستطيع الاشارة إلى حقل البريد الإلكتروني باسم
 # قد يكون هذا ذا معنى بالنسبة لشيفرة المصادقة
 alias_attribute :login, :email
end

ملاحظة: مُعرّف في active_support/core_ext/module/aliasing.rb.

الخاصيّات الداخلية

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

يُعرّف Active Support وحدات الماكرو attr_internal_reader و attr_internal_writer و attr_internal_accessor. وهم يتصرّفون مثل نظرائهم *_attr في روبي باستثناء أن تسميتهم لمُتغيّر النسخة الضمنيّة تجعل التضارب أقلّ احتمالًا.

يُعد الماكرو attr_internal مرادفًا لـ attr_internal_accessor:

# الكتبة
class ThirdPartyLibrary::Crawler
  attr_internal :log_level
end
 
# شيفرة العميل
class MyCrawler < ThirdPartyLibrary::Crawler
  attr_accessor :log_level
end

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

يُسمّى متغير النسخة الداخلي باستخدام العلامة "@" في أولّه مثل log_level_@ في المثال أعلاه. وهذا قابل للإعداد (configurable) عبر Module.attr_internal_naming_format. تستطيع مع ذلك تمرير أي سلسلة تنسيق شبيهة بالمستعملة في التابع sprintf مع البادئة @ و ‎%s في موضع ما حيث سيوضع الاسم. الافتراضي هو "s%_@".

يستخدم ريلز الخاصيّات الداخلية في بعض المواضع مثلًا بالعروض:

module ActionView
  class Base
    attr_internal :captures
    attr_internal :request, :layout
    attr_internal :controller, :template
  end
end

ملاحظة: مُعرّف في active_support/core_ext/module/attr_internal.rb.

خصائص الوحدة

وحدات الماكرو mattr_reader و mattr_writer و mattr_accessor هي نفس وحدات الماكرو *_cattr المُعرّفة للصنف. في الواقع وحدات الماكرو *_cattr هي ببساطة الأسماء البديلة للماكرو *_mattr. تحقق من خاصيّات الصنف في الأسفل.

آليات الاعتماديّات مثلًا تستخدمهم:

module ActiveSupport
  module Dependencies
    mattr_accessor :warnings_on_first_load
    mattr_accessor :history
    mattr_accessor :loaded
    mattr_accessor :mechanism
    mattr_accessor :load_paths
    mattr_accessor :load_once_paths
    mattr_accessor :autoloaded_constants
    mattr_accessor :explicitly_unloadable_constants
    mattr_accessor :constant_watch_stack
    mattr_accessor :constant_watch_stack_mutex
  end
end

ملاحظة: مُعرّف في active_support/core_ext/module/attribute_accessors.rb.

الآباء

التابع parent

يعيد التابع parent مع وحدة مُسمّاة متشعبة (nested named module) الوحدة التي تحتوي على الثابت المقابل الخاص بها:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z
 
X::Y::Z.parent # => X::Y
M.parent       # => X::Y

إن كانت الوحدة مجهولة الاسم أو تنتمي إلى المستوى الأعلى، فيعيد التابع parent الكائن Object.

تحذير: لاحظ أنه في هذه الحالة، يعيد parent_name القيمة nil.

ملاحظة: مُعرّف في active_support/core_ext/module/introspection.rb.

التابع parent_name

يعيد التابع parent_name في وحدة مُسمّاة متشعبة (nested named module) الاسم المؤهل الكامل للوحدة التي تحتوي على الثابت المقابل الخاص بها:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z
 
X::Y::Z.parent_name # => "X::Y"
M.parent_name       # => "X::Y"

يعيد التابع parent_name بالنسبة للوحدات مجهولة الاسم (anonymous modules) أو ذات المستوى العالي (top-level modules) القيمة nil.

تحذير: لاحظ أن التابع parent في هذه الحالة يعيد Object.

ملاحظة: مُعرّف في active_support/core_ext/module/introspection.rb.

التابع parents

يستدعي التابع parents التابع parent على المُستقبل وحتى يصل صعودًا إلى Object. تعاد السلسلة في مصفوفة وتتوضع من الأسفل إلى الأعلى:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z
 
X::Y::Z.parents # => [X::Y, X, Object]
M.parents       # => [X::Y, X, Object]

ملاحظة: مُعرّف في active_support/core_ext/module/introspection.rb.

الوحدات المجهولة

قد أو قد لا تحتوي وحدةٌ (module) على اسم:

module M
end
M.name # => "M"
 
N = Module.new
N.name # => "N"
 
Module.new.name # => nil

تستطيع التحقّق من امتلاك وحدة لاسم بفضل التابع الخبري ?anonymous:

module M
end
M.anonymous? # => false
 
Module.new.anonymous? # => true

لاحظ أن كون الوحدة غير قابلة للوصول لا يعني أنها بلا اسم:

module M
end
 
m = Object.send(:remove_const, :M)
 
m.anonymous? # => false

رغم أن الوحدة المجهولة هي وحدة مستحيلة الوصول حسب التعريف.

ملاحظة: مُعرّف في active_support/core_ext/module/anonymous.rb.

تفويض التوابع

delegate

يوفّر الماكرو delegate طريقةً سهلةً لإعادة توجيه التوابع.

فلنتخيّل مثلًا أنّ المستخدمين في تطبيق ما يملكون معلومات تسجيل دخول في النموذج User ولكن الاسم والبيانات الأخرى في نموذج منفصل هو Profile:

class User < ApplicationRecord
  has_one :profile
end

ستحصل على اسم المستخدم عبر ملفه الشخصي بتلك الاعدادات، user.profile.name، ولكن من المفيد الوصول إلى مثل هذه الخاصيّة مباشرة:

class User < ApplicationRecord
  has_one :profile
 
  def name
    profile.name
  end
end

هذا ما يفعله التابع delegate:

class User < ApplicationRecord
  has_one :profile
 
  delegate :name, to: :profile
end

هذه الطريقة أقصر وأوضح.

يجب أن يكون هذا التابع عامًّا (public) في الهدف.

يقبل الماكرو delegate عدّة توابع:

delegate :name, :age, :address, :twitter, to: :profile

يجب أن يُصبح الخيار to: تعبيرًا يُساوي الكائن الذي فُوّض التابع إليه. عادةً، سلسلة نصية أو رمز. تُقيَّمُ مثل هذه التعابير في سياق المستقبل (receiver):

# Rails التفويض إلى الثابت
delegate :logger, to: :Rails
 
# التفويض إلى صنف المستقبل
delegate :table_name, to: :class

تحذير: إذا كانت قيمة الخيار :prefix هي true وهو أقل شيوعًا، انظر أدناه. ينتشر الاستثناء افتراضيًّا إن اطلق التفويض NoMethodError مع كون الهدف nil. تستطيع أن تطلب إرجاع nil بدلًا من ذلك بفضل الخيار allow_nil::

delegate :name, to: :profile, allow_nil: true

مع allow_nil:، يعيد الاستدعاء user.name القيمة nil إن لم يملك المستخدم ملفًّا شخصيًّا. يضيف الخيار prefix: بادئة (prefix) إلى اسم التابع المُولّد. قد يكون هذا مفيدًا للحصول على اسم أفضل مثلًا:

delegate :street, to: :address, prefix: true

المثال السابق يولّد address_street بدلًا من street.

تحذير: نظرًا لأنَّ اسم التابع المُولَّد في هذه الحالة يتكوّن من أسماء الكائن الهدف والتابع الهدف، فيجب أن يكون الخيار to: اسم تابع.

يمكن أيضًا إعداد بادئة مُخصّصة:

delegate :size, to: :attachment, prefix: :avatar

في المثال السابق يُنشئ الماكرو avatar_size بدلًا من size.

ملاحظة: مُعرّف في active_support/core_ext/module/delegation.rb.

delegate_missing_to

فلنفترض أنك تريد تفويض (delegate) كل ما هو مفقود من الكائن User إلى الكائن Profile. يتيح لك الماكرو delegate_missing_to تنفيذ ذلك بسهولة:

class User < ApplicationRecord
  has_one :profile
 
  delegate_missing_to :profile
end

يمكن أن يكون الهدف أي شيء قابل للنداء داخل الكائن، على سبيل المثال متغيرات النسخة (instance)، والتوابع، والثوابت (constants)، إلخ. تُفوّض توابع الهدف العامّة فقط.

ملاحظة: مُعرّف في active_support/core_ext/module/delegation.rb.

إعادة تعريف التوابع

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

يمنع التابع redefine_method مثل هذا التحذير المحتمل مع إزالة التابع الحالي مسبقًا إن لزم الأمر.

تستطيع أيضًا استخدام silence_redefinition_of_method إن كنت بحاجة لتعريف التابع البديل بنفسك (لأنك تستخدم delegate على سبيل المثال).

ملاحظة: مُعرّف في active_support/core_ext/module/redefine_method.rb.

ملحقات للأصناف

خاصيات الصنف

التابع class_attribute

يُصرّح (declare) التابع class_attribute عن واحدة أو أكثر من خاصيّات الصنف القابلة للوراثة التي يمكن إعادة تعريفها (override) في أي مستوى بالتسلسل الهرمي.

class A
  class_attribute :x
end
 
class B < A; end
 
class C < B; end
 
A.x = :a
B.x # => :a
C.x # => :a
 
B.x = :b
A.x # => :a
C.x # => :b
 
C.x = :c
A.x # => :a
B.x # => :b

على سبيل المثال، يُعرّف ActionMailer::Base:

class_attribute :default_params
self.default_params = {
  mime_version: "1.0",
  charset: "UTF-8",
  content_type: "text/plain",
  parts_order: [ "text/plain", "text/enriched", "text/html" ]
}.freeze

كما يمكن الوصول إليها وإعادة تعريفها على مستوى النسخة (instance).

A.x = 1
 
a1 = A.new
a2 = A.new
a2.x = 2
 
a1.x # => 1, comes from A
a2.x # => 2, overridden in a2

يمكن منع توليد تابع نسخة الكاتب (writer instance) بضبط الخيار instance_writer: إلى القيمة false.

module ActiveRecord
  class Base
    class_attribute :table_name_prefix, instance_writer: false, default: "my"
  end
end

قد يجد نموذجٌ ما هذا الخيار مفيدًا كطريقة لمنع الإسناد الجماعي (mass-assignment) من تعيين الخاصيّة. يمكن منع توليد تابع نسخة القارئ (writer instance) بضبط الخيار instance_writer: إلى القيمة false.

class A
  class_attribute :x, instance_reader: false
end
 
A.new.x = 1
A.new.x # NoMethodError

للتبسيط، يُعرّف class_attribute أيضًا تابع نُسخة خبري (instance predicate) وهو النفي المزدوج لما يعيده نسخة القارئ. في الأمثلة المذكورة أعلاه سوف، أطلقنا عليه ?x.

عندما تكون قيمة الخيار instance_reader: القيمة false، يعيد تابع النسخة الخبري الخطأ NoMethodError مثل تابع القارئ تمامّا.

إن لم ترغب في استعمال تابع النسخة الخبري، مرّر instance_predicate: false ولن يُعرَّف آنذاك.

ملاحظة: مُعرّف في active_support/core_ext/class/attribute.rb.

التوابع cattr_reader، و cattr_writer، و cattr_accessor

تُماثل توابع الماكرو cattr_reader و cattr_writer و cattr_accessor نظيراتها *_attr ولكن للأصناف. ويهيؤون (initialize) متغيّر صنف بالقيمة nil إن لم يكن موجودًا بالفعل ويُولّدون تابع الصنف المناسب للوصول إليه:

class MysqlAdapter < AbstractAdapter
  # @@emulate_booleans توليد توابع صنف للوصول إلى
  cattr_accessor :emulate_booleans, default: true
end

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

module ActionView
  class Base
    cattr_accessor :field_error_proc, default: Proc.new { ... }
  end
end

يمكننا الوصول إلى field_error_proc في العروض (views).

class MysqlAdapter < AbstractAdapter
  # true مع القيمة الافتراضية @@emulate_booleans توليد توابع صنف للوصول إلى
  cattr_accessor :emulate_booleans, default: true
end

يمكن منع توليد تابع نسخة القارئ عبر ضبط instance_reader: إلى القيمة false ومنع توليد تابع نسخة الكاتب عبر ضبط instance_writer: إلى القيمة false. يمكن منع توليد كلا التابعين عبر ضبط instance_accessor: إلى القيمة false. يجب أن تكون القيمة false تحديدًا في جميع الحالات وليس أي قيمة خطأ تقيَّم إلى false.

module A
  class B
    # first_name لن تولد نسخة القارئ
    cattr_accessor :first_name, instance_reader: false
    # last_name= لن تولد نسخة الكاتب
    cattr_accessor :last_name, instance_writer: false
    # surname= أو نسخة الكاتبsurname لن تولد نسخة القارئ
    cattr_accessor :surname, instance_accessor: false
  end
end

قد يجد النموذج أنه من المفيد ضبط instance_accessor: إلى القيمة false كطريقة لمنع الإسناد الجماعي (mass-assignment) من تعيين الخاصيّة.

ملاحظة: مُعرّف في active_support/core_ext/module/attribute_accessors.rb.

الأصناف الفرعية والسليلة

التابع subclasses

يعيد التابع subclasses الأصناف الفرعية للمستقبل:

class C; end
C.subclasses # => []
 
class B < C; end
C.subclasses # => [B]
 
class A < B; end
C.subclasses # => [B]
 
class D < C; end
C.subclasses # => [B, D]

الترتيب الذي تعاد وفقه هذه الأصناف غير محدّد.

ملاحظة: مُعرّف في active_support/core_ext/class/subclasses.rb.

التابع descendants

يعيد التابع descendants جميع الأصناف المنحدرة (>) من المستقبل:

class C; end
C.descendants # => []
 
class B < C; end
C.descendants # => [B]
 
class A < B; end
C.descendants # => [B, A]
 
class D < C; end
C.descendants # => [B, A, D]

الترتيب الذي تعاد وفقه هذه الأصناف غير محدّد.

ملاحظة: مُعرّف في active_support/core_ext/class/subclasses.rb.

ملحقات للسلاسل النصية

سلامة الخَرج (Output Safety)

الدافع (Motivation)

يحتاج إدخال البيانات إلى قوالب HTML عنايةً إضافيّة. لا تستطيع مثلًا حشر review.title@ حرفيًا بصفحة HTML. فلو كان العنوان مثلًا "!Flanagan & Matz rules"، لن تكون تركيبة الخَرج صحيحة لأنه من الضروري تهريب حرف العطف & بهذا الشكل ";amp&". زيادةً على ذلك، قد يكون هذا ثغرة أمنيّة كبيرة لأنه من الممكن للمُستخدمين حقن شيفرة HTML خبيثة تضع عنوانًا آخر للصفحة.

ألقِ نظرة على القسم "البرمجة عبر المواقع" في دليل تأمين تطبيقات ريلز لمزيد من المعلومات حول المخاطر.

السلاسل النصية الآمنة

يمتلك Active Support مفهوم السلاسل الآمنة (html). السلسلة الآمنة هي التي تُعلَّم على أنها قابلة للإدراج في شيفرة HTML كما هي، إذ هي موثوقة بغض النظر عما إذا كانت مُهرّبة أم لا.

تعتبر السلاسل النصيّة غير آمنة افتراضيًّا:

"".html_safe? # => false

تستطيع الحصول على سلسلة آمنة من أخرى عاديّة باستعمال التابع html_safe:

s = "".html_safe
s.html_safe? # => true

من المهم أن نفهم أن html_safe لا يُهرّب أي شيء على الإطلاق بل هو مجرد تأكيد:

s = "<script>...</script>".html_safe
s.html_safe? # => true
s            # => "<script>...</script>"

تقع على عاتقك مسؤولية التأكد من أن استدعاء html_safe على سلسلة معيّنة أمر جيّد ولا يحمل أية مخاطر. إن ألحقت (append) شيئًا بسلسلة آمنة إمّا بمكان مُحدّد مع >>/concat أو مع +، فإن النتيجة هي سلسلة آمنة. تُهرّب الوسائط غير الآمنة:

"".html_safe + "<" # => "&lt;"

تُلحق الوسائط الآمنة مباشرة:

"".html_safe + "<".html_safe # => "<"

يجب عدم استخدام هذه التوابع في العروض العادية. تُهرّب القيم غير الآمنة تلقائيًا:

<%= @review.title %> <%# fine, escaped if needed %>

لإدراج شيء ما حرفيًا، استخدم المُساعد raw بدل استدعاء html_safe:

<%= raw @cms.current_template %> <%# inserts @cms.current_template as is %>

أو بطريقة أخرى مكافئة، استخدم ‎<%==‎:

<%== @cms.current_template %> <%# inserts @cms.current_template as is %>

يستدعي المُساعد raw التابع html_safe بدلًا منك:

def raw(stringish)
  stringish.to_s.html_safe
end

ملاحظة: مُعرّف في active_support/core_ext/string/output_safety.rb.

التحوّل (Transformation)

كقاعدة عامة وباستثناء عملية الدمج (concatenation) كما هو موضّح أعلاه، أي تابع يستطيع تغيير سلسلة نصيّة يعطيك سلسلة غير آمنة. وهم downcase، و gsub، و strip، و chomp، و underscore ...إلخ.

في حالة التحوّلات في نفس المكان مثل !gsub، يصبح المستقبل نفسه غير آمن.

تنويه: يُفقد بت (bit) السلامة دائمًا بغض النظر عن تغيير التحويل لأي شيء أم لا.

التحويل والتحويل بالإكراه (Conversion and Coercion)

التحويل باستدعاء to_s على سلسلة نصيّة آمنة يعيد سلسلة نصيّة آمنة ولكن التحويل بالإكراه عبر التابع to_str يعيد سلسلة غير آمنة.

النسخ

استدعاء dup أو clone على سلاسل آمنة ينتج سلاسل آمنة.

التابع remove

يحذف التابع remove جميع تكرارات النمط:

"Hello World".remove(/Hello /) # => "World"

توجد أيضًا النسخة المدمرة من هذا التابع هي !String.remove.

ملاحظة: مُعرّف في active_support/core_ext/string/filters.rb.

التابع squish

يزيل التابع squish المسافات البيضاء الزائدة السابقة واللاحقة ويستبدل المسافات البيضاء بين الكلمات بفراغ واحدة:

" \n  foo\n\r \t bar \n".squish # => "foo bar"

توجد أيضًا نسخة مدمرة هي !String.squish.

لاحظ أنه يُعالج كل من مسافات ASCII و Unicode البيضاء.

ملاحظة: مُعرّف في active_support/core_ext/string/filters.rb.

التابع truncate

يعيد التابع truncate نسخة (copy) مقطوعة (truncated) من مستقبلها بعد طول معيّن (يحدد بالمعامل length):

"Oh dear! Oh dear! I shall be late!".truncate(20)
# => "Oh dear! Oh dear!..."

يمكن تخصيص النقط الثلاث (Ellipsis) التي توضع بدل الكلام المقتطع باستخدام الخيار omission::

"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '&hellip;')
# => "Oh dear! Oh &hellip;"

لاحظ خاصّة أنَّ الاقتطاع يأخذ طول سلسلة الحذف (omission string) في الحُسبان. مرّر الخيار separator: لاقتطاع السلسلة في فاصل طبيعي:

"Oh dear! Oh dear! I shall be late!".truncate(18)
# => "Oh dear! Oh dea..."
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: ' ')
# => "Oh dear! Oh..."

يمكن للخيار separator: أن يكون تعبيرًا نمطيًّا:

"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
# => "Oh dear! Oh..."

في الأمثلة أعلاه تُقطع "dear" في البداية ثم يمنع separator: ذلك.

ملاحظة: مُعرّف في active_support/core_ext/string/filters.rb.

التابع truncate_words

يعيد التابع truncate_words نسخة من مستقبله بعد اقتطاعه (truncated) عند عدد معيّن من الكلمات:

"Oh dear! Oh dear! I shall be late!".truncate_words(4)
# => "Oh dear! Oh dear!..."

يمكن تخصيص النقط الثلاث (Ellipsis) التي توضع بدل الكلام المقتطع باستخدام الخيار omission::

"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '&hellip;')
# => "Oh dear! Oh dear!&hellip;"

مرّر الخيار separator: لاقتطاع السلسلة عند فاصل طبيعي:

"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!')
# => "Oh dear! Oh dear! I shall be late..."

يمكن للخيار separator: أن يكون تعبيرًا نمطيًّا:

"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
# => "Oh dear! Oh dear!..."

ملاحظة: مُعرّف في active_support/core_ext/string/filters.rb.

التابع Inquiry

يُحوّل التابع inquiry سلسلة نصيّة إلى كائن من النوع StringInquirer مما يجعل التحقّق من المساواة أجمل.

production".inquiry.production? # => true
"active".inquiry.inactive?       # => false

التابعان ?starts_with و ?ends_with

يعرّف Active Support أسماء ضمير الغائب البديلة من ?String.start_with و ?String#end_with:

"foo".starts_with?("f") # => true
"foo".ends_with?("o")   # => true

ملاحظة: مُعرّف في active_support/core_ext/string/starts_ends_with.rb.

التابع strip_heredoc

يزيل التابع strip_heredoc المسافات البادئة (indentation) في الصيغة heredoc.

مثلًا:

if options[:usage]
  puts <<-USAGE.strip_heredoc
    This command does such and such.
 
    Supported options are:
      -h         This message
      ...
  USAGE
end

سيرى المستخدم رسالة الاستخدام حذو الهامش الأيسر.

تقنيًّا، يبحث التابع عن الخط الأقل إزاحة (least indented) في السلسلة بأكملها ويزيل ذاك المقدار من المسافة البيضاء الزائدة.

ملاحظة: مُعرّف في active_support/core_ext/string/strip.rb.

التابع Indent

يضيف مسافة بادئة للأسطر في المستقبل:

<<EOS.indent(2)
def some_method
  some_code
end
EOS
# =>
  def some_method
    some_code
  end

يحدد الوسيط الثاني indent_string أي مسافة بادئة يُراد استخدامها. القيمة الافتراضيّة هي nil وهي تُخبر التابع أن يُخمّن عبر النظر في أوّل السطر مُزاح بمسافة بادئة والرجوع بمسافة فارغة إن لم يوجد.

"  foo".indent(2)        # => "    foo"
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
"foo".indent(2, "\t")    # => "\t\tfoo"

بينما يكون indent_string عادةً مسافة واحدة أو مسافة جدولة (tab)، يظل من الممكن أن تكون أي سلسلة. الوسيط الثالث indent_empty_lines هي علامة تشير إلى ما إذا وجب وضع مسافات بادئة للسطور الفارغة. قيمتها الافتراضيّة هي false.

"foo\n\nbar".indent(2)            # => "  foo\n\n  bar"
"foo\n\nbar".indent(2, nil, true) # => "  foo\n  \n  bar"

يضع التابع !indent المسافة البادئة في في السلسلة نفسها عبر تعديل المستقبل.

ملاحظة: مُعرّف في active_support/core_ext/string/indent.rb.

الوصول (Access)

التابع at(position)‎

يعيد محرفًا ذا موضع محدَّد (في الموضع position) في السلسلة النصية:

"hello".at(0)  # => "h"
"hello".at(4)  # => "o"
"hello".at(-1) # => "o"
"hello".at(10) # => nil

ملاحظة: مُعرّف في active_support/core_ext/string/access.rb.

التابع from(position)‎

يعيد السلسلة الفرعية من السلسلة النصية المعطاة التي تبدأ من الموضع position:

"hello".from(0)  # => "hello"
"hello".from(2)  # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil

ملاحظة: مُعرّف في active_support/core_ext/string/access.rb.

التابع to(position)‎

يعيد السلسلة الفرعية للسلسلة النصية المعطاة حتى الموضع position:

"hello".to(0)  # => "h"
"hello".to(2)  # => "hel"
"hello".to(-2) # => "hell"
"hello".to(10) # => "hello"

ملاحظة: مُعرّف في active_support/core_ext/string/access.rb.

التابع first(limit = 1)‎

يُماثل استدعاء التابع str.first(n)‎ الاستدعاء str.to(n-1)‎ إن كانت n > 0 ويعيد سلسلة فارغة عندما تكون n == 0.

ملاحظة: مُعرّف في active_support/core_ext/string/access.rb.

التابع last(limit = 1)‎

يُماثل استدعاء التابع str.last(n)‎ الاستدعاء str.last(n)‎ إن كانت n > 0 ويعيد سلسلة فارغة لمّا n == 0.

ملاحظة: مُعرّف في active_support/core_ext/string/access.rb.

المشتقات (Inflections)

التابع pluralize

يعيد التابع pluralize صيغة جمع (plural) للمستقبل:

"table".pluralize     # => "tables"
"ruby".pluralize      # => "rubies"
"equipment".pluralize # => "equipment"

كما ترى، يعرف Active Support بعض صيغ الجمع الشاذة (irregular plurals) والأسماء غير المعدودة (uncountable nouns). يمكن توسيع القواعد والمفردات في config/initializers/inflections.rb. يُنشَأ هذا الملف بواسطة الأمر rails وبه إرشادات في التعليقات. يستطيع pluralize أيضًا أن يأخذ معاملًا (parameter) اختياريًا هو count. إن كان count == 1، فستُعاد الصيغة المفردة. بالنسبة لأية قيمة أخرى للمعامل count، ستعاد صيغة الجمع:

"dude".pluralize(0) # => "dudes"
"dude".pluralize(1) # => "dude"
"dude".pluralize(2) # => "dudes"

يستخدم Active Record هذه التابع لحساب اسم الجدول الافتراضي الذي يوافق النموذج:

# active_record/model_schema.rb
def undecorated_table_name(class_name = base_class.name)
  table_name = class_name.to_s.demodulize.underscore
  pluralize_table_names ? table_name.pluralize : table_name
end

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع singularize

يسلك هذا التابع سلوكًا معاكسًا للتابع pluralize:

tables".singularize    # => "table"
"rubies".singularize    # => "ruby"
"equipment".singularize # => "equipment"

تحسب الارتباطات (Associations) اسم الصنف الافتراضي المرتبط باستخدام هذا التابع:

# active_record/reflection.rb
def derive_class_name
  class_name = name.to_s.camelize
  class_name = class_name.singularize if collection?
  class_name
end

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع camelize

يعيد التابع camelize المستقبل في نمط سنام الجمل (CamelCase):

"product".camelize    # => "Product"
"admin_user".camelize # => "AdminUser"

تستطيع التفكير في هذه التابع باعتباره التابع التي يحوّل المسارات (paths) إلى صنف روبي أو أسماء وحدات (module) حيث تقوم الشرطات المائلة (slashes) بفصل مجالات الأسماء:

"backoffice/session".camelize # => "Backoffice::Session"

يستخدم Action Pack مثلًا هذا التابع لتحميل الصنف الذي يوفّر مخزن جلسة معيّن:

# action_controller/metal/session_management.rb
def session_store=(store)
  @@session_store = store.is_a?(Symbol) ?
    ActionDispatch::Session.const_get(store.to_s.camelize) :
    store
end

يأخذ التابع camelize وسيطًا اختياريًّا يمكن أن يكون upper: (الافتراضي) أو lower:. مع القيمة الأخيرة للوسيط، يُصبح الحرف الأول صغيرًا:

"visual_effect".camelize(:lower) # => "visualEffect"

قد يكون هذا مفيدًا لحساب أسماء التابع باللغات التي تتبع هذا العرف مثل لغة جافاسكربت.

تنويه: تستطيع عمومًا التفكير في camelize كعكس underscore على الرغم من وجود حالات تخالف ذلك:

"SSLError".underscore.camelize يعيد "SslError". لدعم  مثل هذه حالات، يسمح لك Active Support بتحديد الاختصارات (acronyms) في config/initializers/inflections.rb:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym 'SSL'
end
 
"SSLError".underscore.camelize # => "SSLError"

التابع camelcase هو اسم بديل للتابع camelize.

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع underscore

يعمل التابع underscore عكس التابع camelcase إذ يأخذ سلسلة مكتوبة بنمط سنام الجمل ويحولها إلى مسار ويفصل الكلمات بشرطة سفلية:

"Product".underscore   # => "product"
"AdminUser".underscore # => "admin_user"

كما يحوّل "::" أيضًا إلى "/":

"Backoffice::Session".underscore # => "backoffice/session"

ويفهم السلاسل النصيّة التي تبدأ بالحروف الصغيرة:

"visualEffect".underscore # => "visual_effect"

لا يقبل underscore أي وسيط رغم ذلك. يستخدم التحميل التلقائي لأصناف ريلز ووحداته التابع underscore للاستدلال على المسار النسبي بدون امتداد ملف (file extension) والذي من شأنه تحديد ثابت مفقود محدّد:

# active_support/dependencies.rb
def load_missing_constant(from_mod, const_name)
  ...
  qualified_name = qualified_name_for from_mod, const_name
  path_suffix = qualified_name.underscore
  ...
end

تستطيع افتراض أنَّ التابع underscore كعكس camelize كقاعدة عامة على الرغم من وجود حالات مخالفة لذلك. مثلًا "SSLError".underscore.camelize يعيد "SslError".

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع titleize

يحوّل التابع titleize الحروف لحروف كبيرة في المستقبل:

"alice in wonderland".titleize # => "Alice In Wonderland"
"fermat's enigma".titleize     # => "Fermat's Enigma"

التابع titlecase هو اسم بديل للتابع titleize.

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع dasherize

يبدل التابع dasherize الشرطات العادية في المستقبل مكان الشرطات السفلية:

"name".dasherize         # => "name"
"contact_data".dasherize # => "contact-data"

يستخدم مُسَلسِل XML للنماذج (XML serializer of models) هذا التابع لتطبيقه على أسماء العُقد (node names):

# active_model/serializers/xml.rb
def reformat_name(name)
  name = name.camelize if camelize?
  dasherize? ? name.dasherize : name
end

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع demodulize

في حالة الحصول على سلسلة ذات اسم ثابت مُميّز (qualified constant name)، يعيد demodulize اسم الثابت تحديدًا، أي جزأه الأيمن:

"Product".demodulize                        # => "Product"
"Backoffice::UsersController".demodulize    # => "UsersController"
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
"::Inflections".demodulize                  # => "Inflections"
"".demodulize                               # => ""

يستخدم Active Record مثلًا هذا التابع لحساب اسم حقل عدّاد مؤقت (counter cache column):

# active_record/reflection.rb
def counter_cache_column
  if options[:counter_cache] == true
    "#{active_record.name.demodulize.underscore.pluralize}_count"
  elsif options[:counter_cache]
    options[:counter_cache]
  end
end

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع deconstantize

في حالة الحصول على سلسلة ذات اسم ثابت مُميّز (qualified constant)، يحذف التابع deconstantize جزئه الأيمن تاركًا، عادةً، اسم حاوي الثابت:

"Product".deconstantize                        # => ""
"Backoffice::UsersController".deconstantize    # => "Backoffice"
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع parameterize

يُطبّع التابع parameterize المستقبل بحيث يمكن استخدامه في عناوين URL جميلة.

"John Smith".parameterize # => "john-smith"
"Kurt Gödel".parameterize # => "kurt-godel"

للحفاظ على حالة السلسلة، اضبط الوسيط preserve_case إلى القيمة true. قيمة preserve_case هي false افتراضيًّا.

"John Smith".parameterize(preserve_case: true) # => "John-Smith"
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"

أعد تعريف (override) الوسيط separator لاستخدام فاصل مخصّص.

"John Smith".parameterize(separator: "_") # => "john\_smith"
"Kurt Gödel".parameterize(separator: "_") # => "kurt\_godel"

تُلَف السلسلة النصية الناتجة في نسخة من ActiveSupport::Multibyte::Chars.

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع tableize

التابع tableize هو underscore يليه pluralize.

"Person".tableize      # => "people"
"Invoice".tableize     # => "invoices"
"InvoiceLine".tableize # => "invoice_lines"

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

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع classify

التابع classify هو عكس tableize، إذ يعطيك اسم الصنف الموافق لاسم الجدول:

"people".classify        # => "Person"
"invoices".classify      # => "Invoice"
"invoice_lines".classify # => "InvoiceLine"

يفهم التابع أسماء الجداول المُميّزة:

"highrise_production.companies".classify # => "Company"

لاحظ أن classify يعيد اسم صنف كسلسلة نصية. تستطيع الحصول على كائن الصنف الفعلي باستدعاء constantize عليه، كما سيوضّح لاحقًا.

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع constantize

يستبين (resolves) التابع constantize تعبير مرجع الثابت (constant reference expression) في المستقبل:

"Integer".constantize # => Integer
 
module M
  X = 1
end
"M::X".constantize # => 1

يرفع constantize الخطأ NameError إن قُيِّمَت السلسلة النصيّة إلى ثابت غير معروف أو إن لم يكن محتواها اسم ثابت صالح. يبدأ استبيان اسم الثابت بواسطة constantize دائمًا في الكائن Object ذي المستوى الأعلى حتى إن لم توجد البادئة "::".

X = :in_Object
module M
  X = :in_M
 
  X                 # => :in_M
  "::X".constantize # => :in_Object
  "X".constantize   # => :in_Object (!)
end

لذلك، لا تعادل هذه العمليّة بشكل عام ما كان ليفعله روبي في نفس الموقف لو قُيّم ثابت حقيقي. تحصل حالات اختبار Mailer على مُرسل البريد تحت الاختبار من اسم صنف الاختبار باستخدام constantize:

# action_mailer/test_case.rb
def determine_default_mailer(name)
  name.sub(/Test$/, '').constantize
rescue NameError => e
  raise NonInferrableMailerError.new(name)
end

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع humanize

يُعدّل التابع humanize اسم خاصيّة لعرضها للمستخدمين النهائيين. وتحديدًا ينفّذ هذه التحويلات:

  • تطبيق قواعد تصريف بشرية على الوسيط.
  • حذف الشرطات السفلية الأماميّة إن وجدت.
  • إزالة اللاحقة "‎_id" إذا كانت موجودة.
  • استبدال الشرطات السفلية بمسافات فارغة إن وجدت.
  • يُصغّر كل الحروف ما عدا المختصرات (acronyms).
  • تكبير حروف أوّل كلمة.

يمكن إيقاف تكبير حروف الكلمة الأولى عبر ضبط الخيار capitalize: إلى القيمة false (القيمة الافتراضية هي true).

"name".humanize                         # => "Name"
"author_id".humanize                    # => "Author"
"author_id".humanize(capitalize: false) # => "author"
"comments_count".humanize               # => "Comments count"
"_id".humanize                          # => "Id"

إذا عُرفّت "SSL" كاختصار:

'ssl_error'.humanize # => "SSL error"

يستخدم التابع المساعد full_messages التابع humanize كخطّة احتياطيّة لتضمين أسماء الخاصيّات:

def full_messages
  map { |attribute, message| full_message(attribute, message) }
end
 
def full_message
  ...
  attr_name = attribute.to_s.tr('.', '_').humanize
  attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
  ...
end

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التابع foreign_key

يعطي التابع foreign_key اسم حقل مفتاح خارجي (foreign key column name) من اسم صنف. لذلك، يحذف الوحدة (demodulizes) ويضيف شرطة سفليّة (underscores) ويضيف "‎_id":

"User".foreign_key           # => "user_id"
"InvoiceLine".foreign_key    # => "invoice_line_id"
"Admin::Session".foreign_key # => "session_id"

مرّر الوسيط false إن لم تُرد شرطة سفليّة في "id_":

"User".foreign_key(false) # => "userid"

تستخدم الارتباطات (Association) هذا التابع لاستنتاج المفاتيح الخارجية، إذ يفعل الارتباط has_one والارتباط has_many مثلًا هذا:

# active_record/associations.rb
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key

ملاحظة: مُعرّف في active_support/core_ext/string/inflections.rb.

التحويلات (Conversions)

التوابع to_date، و to_time، و to_datetime

تُعدّ التوابع to_date و to_time و to_datetime كأغلفة ملائمة حول Date._parse:

"2010-07-27".to_date              # => Tue, 27 Jul 2010
"2010-07-27 23:37:00".to_time     # => 2010-07-27 23:37:00 +0200
"2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000

يتلقّى to_time وسيطًا اختياريًّا هو utc: أو local: للإشارة إلى المنطقة الزمنية التي تريد التوقيت حسبها:

"2010-07-27 23:42:00".to_time(:utc)   # => 2010-07-27 23:42:00 UTC
"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200

الوسيط الافتراضي هو local:.

يرجى الرجوع إلى توثيق Date._parse لمزيد من التفاصيل.

تنويه: تعيد التوابع الثلاثة القيمة nil في حالة كون المستقبل فارغًا.

ملاحظة: مُعرّف في active_support/core_ext/string/conversions.rb.

ملحقات للأعداد

البايتات (Bytes)

تستجيب جميع الأرقام لهذه التوابع:

bytes
kilobytes
megabytes
gigabytes
terabytes
petabytes
exabytes

يُرجعون الكمية المقابلة من البايتات باستخدام عامل تحويل يبلغ 1024:

2.kilobytes   # => 2048
3.megabytes   # => 3145728
3.5.gigabytes # => 3758096384
-4.exabytes   # => -4611686018427387904

تتخذ الصيغ الفردية أسماءً بديلة، لذلك تستطيع قول:

1.megabyte # => 1048576

ملاحظة: مُعرّف في active_support/core_ext/numeric/bytes.rb.

الوقت

يُمكّن Time من استخدام حسابات الوقت والتصريحات، مثل ‎45.minutes + 2.hours + 4.weeks.

تستخدم هذه التوابع Time.advance لحساب التواريخ بدقّة عند استخدام from_now، و ago ...إلخ. بالإضافة إلى إضافة أو طرح نتائجها من كائن وقت Time. مثلًا:

# Time.current.advance(months: 1) يكافئ
1.month.from_now
 
# Time.current.advance(weeks: 2) يكافئ
2.weeks.from_now
 
# Time.current.advance(months: 4, weeks: 5) يكافئ
(4.months + 5.weeks).from_now

تحذير: لفترات وأوقات أخرى، يرجى الاطلاع على قسم ملحقات العدد الصحيح في الأسفل.

ملاحظة: مُعرّف في active_support/core_ext/numeric/time.rb.

التنسيق

يُمكّن من تنسيق الأرقام بطرق متنوعة منها:

  • إنتاج سلسلة نصيّة تمثِّل رقمًا كرقم هاتف:
5551234.to_s(:phone)
# => 555-1234
1235551234.to_s(:phone)
# => 123-555-1234
1235551234.to_s(:phone, area_code: true)
# => (123) 555-1234
1235551234.to_s(:phone, delimiter: " ")
# => 123 555 1234
1235551234.to_s(:phone, area_code: true, extension: 555)
# => (123) 555-1234 x 555
1235551234.to_s(:phone, country_code: 1)
# => +1-123-555-1234
  • إنتاج سلسلة نصية تمثِّل رقمًا كعملة:
1234567890.50.to_s(:currency)                 # => $1,234,567,890.50
1234567890.506.to_s(:currency)                # => $1,234,567,890.51
1234567890.506.to_s(:currency, precision: 3)  # => $1,234,567,890.506
  • إنتاج سلسلة نصية تمثِّل رقمًا كنسبة مئوية:
100.to_s(:percentage)
# => 100.000%
100.to_s(:percentage, precision: 0)
# => 100%
1000.to_s(:percentage, delimiter: '.', separator: ',')
# => 1.000,000%
302.24398923423.to_s(:percentage, precision: 5)
# => 302.24399%
  • إنتاج سلسلة نصية تمثِّل رقمًا في شكل محدّد (delimited form):
12345678.to_s(:delimited)                     # => 12,345,678
12345678.05.to_s(:delimited)                  # => 12,345,678.05
12345678.to_s(:delimited, delimiter: ".")     # => 12.345.678
12345678.to_s(:delimited, delimiter: ",")     # => 12,345,678
12345678.05.to_s(:delimited, separator: " ")  # => 12,345,678 05
  • إنتاج سلسلة نصية تمثِّل عددًا مقربًا (number rounded) إلى دقة محدَّدة:
111.2345.to_s(:rounded)                     # => 111.235
111.2345.to_s(:rounded, precision: 2)       # => 111.23
13.to_s(:rounded, precision: 5)             # => 13.00000
389.32314.to_s(:rounded, precision: 0)      # => 389
111.2345.to_s(:rounded, significant: true)  # => 111
  • إنتاج سلسلة نصية تمثِّل رقمًا كرقم يمكن قرائته بسهولة:
123.to_s(:human_size)                  # => 123 Bytes
1234.to_s(:human_size)                 # => 1.21 KB
12345.to_s(:human_size)                # => 12.1 KB
1234567.to_s(:human_size)              # => 1.18 MB
1234567890.to_s(:human_size)           # => 1.15 GB
1234567890123.to_s(:human_size)        # => 1.12 TB
1234567890123456.to_s(:human_size)     # => 1.1 PB
1234567890123456789.to_s(:human_size)  # => 1.07 EB
  • إنتاج سلسلة نصيّة تمثِّل رقمًا في كلمات يمكن للإنسان قراءتها:
123.to_s(:human)               # => "123"
1234.to_s(:human)              # => "1.23 Thousand"
12345.to_s(:human)             # => "12.3 Thousand"
1234567.to_s(:human)           # => "1.23 Million"
1234567890.to_s(:human)        # => "1.23 Billion"
1234567890123.to_s(:human)     # => "1.23 Trillion"
1234567890123456.to_s(:human)  # => "1.23 Quadrillion"

ملاحظة: معرّف في active_support/core_ext/numeric/conversions.rb.

ملحقات للأعداد الصحيحة

التابع ?multiple_of

يختبر التابع ?multiple_of ما إذا كان العدد الصحيح من مضاعفات الوسيط المُمرَّر إليه:

2.multiple_of?(1) # => true
1.multiple_of?(2) # => false

ملاحظة: مُعرّف في active_support/core_ext/integer/multiple.rb.

التابع ordinal

يعيد التابع ordinal سلسلة نصيّة لاحقة ترتيبية (ordinal suffix string) تقابل العدد الصحيح للمستقبل:

1.ordinal    # => "st"
2.ordinal    # => "nd"
53.ordinal   # => "rd"
2009.ordinal # => "th"
-21.ordinal  # => "st"
-134.ordinal # => "th"

ملاحظة: مُعرّف في active_support/core_ext/integer/inflections.rb.

التابع ordinalize

يعيد التابع ordinalize السلسلة الترتيبية المقابلة للعدد الصحيح للمستقبل. بالموازنة بين هذ االتابع والتابع ordinal، لاحظ أن التابع ordinal يعيد اللاحقة النصيّة فقط.

1.ordinalize    # => "1st"
2.ordinalize    # => "2nd"
53.ordinalize   # => "53rd"
2009.ordinalize # => "2009th"
-21.ordinalize  # => "-21st"
-134.ordinalize # => "-134th"

ملاحظة: مُعرّف في active_support/core_ext/integer/inflections.rb.

الوقت (Time)

يُمكّن الكائن Time من استخدام حسابات وتصريحات الوقت، مثل ‎4.months + 5.years.

تستخدم هذه التوابع Time.advance لحساب التواريخ بدقّة عند استخدام from_now، و ago، ...إلخ. بالإضافة إلى إضافة أو طرح نتائجها من الكائن Time. مثلًا:

# Time.current.advance(months: 1) يكافئ
1.month.from_now
 
# Time.current.advance(years: 2) يكافئ
2.years.from_now
 
# Time.current.advance(months: 4, years: 5) يكافئ
(4.months + 5.years).from_now

تحذير: لفترات أخرى، يرجى الرجوع إلى ملحقات الوقت للأعداد في الأعلى.

ملاحظة: مُعرّف في active_support/core_ext/numeric/time.rb.

ملحقات للأعداد العشرية الكبيرة (BigDecimal)

التابع to_s

يُوفّر التابع to_s محدّدًّا افتراضيًّا (default specifier) لـ "F". هذا يعني أن استدعاءً بسيطًا على to_s سيؤدي إلى تمثيل الفاصلة المُتحرّكة (floating point) بدلًا من التدوين الهندسيّة:

BigDecimal(5.00, 6).to_s       # => "5.0"

وتُدعم أيضًا مُحدّدات الرمز (symbol specifiers):

BigDecimal(5.00, 6).to_s(:db)  # => "5.0"

لا يزال التدوين الهندسيّ (engineering notation) مدعومًا:

BigDecimal(5.00, 6).to_s("e")  # => "0.5E1"

ملحقات للكائنات القابلة للتعداد (Enumerable)

التابع sum

يضيف التابع sum عناصر قابلة للتعداد:

[1, 2, 3].sum # => 6
(1..100).sum  # => 5050

تفترض عملية الجمع أن العناصر تستجيب لمعامل الجمع +:

[[1, 2], [2, 3], [3, 4]].sum    # => [1, 2, 2, 3, 3, 4]
%w(foo bar baz).sum             # => "foobarbaz"
{a: 1, b: 2, c: 3}.sum          # => [:b, 2, :c, 3, :a, 1]

مجموعُ مجموعةٍ فارغة (empty collection) هو صفر افتراضيًّا ولكن هذا قابل للتخصيص:

[].sum    # => 0
[].sum(1) # => 1

يصبح sum مُكرِّرًا (iterator) إن أعطيت كتلة إذ يمرِّر عناصر المجموعة إليها ثم يجمع القيم التي تعيدها:

(1..5).sum {|n| n * 2 } # => 30
[2, 4, 6, 8, 10].sum    # => 30

يُمكن تخصيص مجموع مستقبل فارغ بهذا الشكل أيضًا:

[].sum(1) {|n| n**3} # => 1

ملاحظة: مُعرّف في active_support/core_ext/enumerable.rb.

التابع index_by

يُولّد التابع index_by جدول hash لعناصر كائن قابل للتعداد مُفهرسة عبر مفتاح ما.

يتكرّر التابع خلال المجموعة ويُمرّر كل عنصر إلى كتلة. سيعيَّن مفتاح العنصر إلى القيمة التي تعيدها الكتلة:

invoices.index_by(&:number)
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}

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

ملاحظة: مُعرّف في active_support/core_ext/enumerable.rb.

التابع ?many

التابع ?many هو اختزال للاستدعاء collection.size > 1:

<% if pages.many? %>
  <%= pagination_links %>
<% end %>

يأخذ ?many في الحسبان فقط العناصر التي تعيد القيمة true إن أُعطيَت كتلة اختيارية:

@see_more = videos.many? {|video| video.category == params[:category]}

ملاحظة: مُعرّف في active_support/core_ext/enumerable.rb.

التابع ?exclude

يختبر التابع ?exclude الخبري ما إذا كان كائنٌ ما لا ينتمي للمجموعة، إذ هو نفي للتابع ?include:

to_visit << node if visited.exclude?(node)

ملاحظة: مُعرّف في active_support/core_ext/enumerable.rb.

التابع without

يعيد التابع without نسخة (copy) من كائن قابل للتعداد مع إزالة عناصر محدّدة:

["David", "Rafael", "Aaron", "Todd"].without("Aaron", "Todd") # => ["David", "Rafael"]

ملاحظة: مُعرّف في active_support/core_ext/enumerable.rb.

التابع pluck

يعيد التابع pluck مصفوفةً بناءًا على مفتاح معيّن:

[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]

ملاحظة: مُعرّف في active_support/core_ext/enumerable.rb.

ملحقات للمصفوفات

الوصول (Accessing)

يُحسّن Active Support من الواجهة البرمجية للمصفوفات لتيسير طرق معيّنة للوصول إليها. على سبيل المثال، يعيد to مصفوفة فرعيّة (subarray) من العناصر حتّى عنصر ذي فهرس محدَّد:

%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7)          # => []

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

%w(a b c d).from(2)  # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0)           # => []

تعيد التوابع second و third و fourth و fifth العنصر المقابل للمعنى الحرفي لها كما يفعل التابعان second_to_last و third_to_last (التابعان first و last مُدمجان [built-in]) نفس الأمر أيضًا. بفضل الحكمة الاجتماعية (social wisdom) والعمل البنّاء الإيجابي في كل مكان، صار التابع forty_two متوافّرًا أيضًا.

%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil

ملاحظة: مُعرّف في active_support/core_ext/array/access.rb.

إضافة عناصر

التابع prepend

هذا التابع هو اسم بديل للتابع Array.unshift.

%w(a b c d).prepend('e')  # => ["e", "a", "b", "c", "d"]
[].prepend(10)            # => [10]

ملاحظة: مُعرّف في active_support/core_ext/array/prepend_and_append.rb.

التابع append

هذا التابع هو اسم بديل للمعامل >>#Array.

%w(a b c d).append('e')  # => ["a", "b", "c", "d", "e"]
[].append([1,2])         # => [[1, 2]]

ملاحظة: مُعرّف في active_support/core_ext/array/prepend_and_append.rb.

استخراج الخيارات

يسمح لك روبي بحذف الأقواس المربعة عندما يكون الوسيط الأخير في استدعاء تابع جدول Hash باستثناء حالة الوسيط block&:

User.exists?(email: params[:email])

يستخدم هذا التحسين في الصياغة كثيرًا في ريلز لتجنب الوسائط الموضعيّة حيث تكثر عن اللازم بكثير. ويقدّم بدل ذلك واجهات تحاكي المُعاملات المسماة (named parameters). من المفيد جدًا استخدام جدول Hash زائد (trailing hash) للخيارات.

ولكن إن كان التابع يتوقّع عددًا متغيرًا من الوسائط ويستخدم * في تصريحه، فيصبح مثل الجدول Hash من الخيارات هذا في النهاية عنصرًا من مصفوفة الوسائط حيث يفقد دوره.

تستطيع في هذه الحالات إعطاء جدول الخيارات Hash معاملة مميّزة مع !extract_options. يتحقق هذا التابع من نوع العنصر الأخير في المصفوفة. فإن كان جدول Hash، يستخرجه ويعيده، وإلا يعيد جدولًا فارغًا.

فلنرى على سبيل المثال تعريف ماكرو وحدة التحكّم caches_action:

def caches_action(*actions)
  return unless cache_configured?
  options = actions.extract_options!
  ...
end

يتَلقّى هذا التابع عددًا عشوائيًا من أسماء الإجراءات وجدول Hash اختياري من الخيارات كوسيط أخير. تتحصّل باستدعاء !extract_options على جدول Hash من الخيارات وتزيله من actions بطريقة بسيطة وواضحة.

ملاحظة: مُعرّف في active_support/core_ext/array/extract_options.rb.

التحويلات

التابع to_sentence

يحوّل التابع to_sentence مصفوفةً لسلسلة نصيّة تحتوي على جملة تُعدِّد (enumerates) عناصرها:

%w().to_sentence                # => ""
%w(Earth).to_sentence           # => "Earth"
%w(Earth Wind).to_sentence      # => "Earth and Wind"
%w(Earth Wind Fire).to_sentence # => "Earth, Wind, and Fire"

يقبل هذا التابع ثلاثة خيارات:

  • :two_words_connector: ما يُستخدم للمصفوفات ذات الطول 2. الكلمة الافتراضية هي "and".
  • :words_connector: ما يُستخدم لضم (join) عناصر مصفوفة مكونة من 3 عناصر أو أكثر باستثناء آخر عنصرين. الفاصل الافتراضي هو ",".
  • :last_word_connector: ما يُستخدم لضمّ العناصر الأخيرة لمصفوفة مكونة من 3 عناصر أو أكثر. القيمة الافتراضية هي " and ,".

يمكن تخصيص الإعدادات الافتراضية لهذه الخيارات بحسب المحلية، إذ مفاتيحها هي:

الخيار المفتاح I18n
two_words_connector:‎ support.array.two_words_connector
words_connector:‎ support.array.words_connector
last_word_connector:‎ support.array.last_word_connector

ملاحظة: مُعرّف في active_support/core_ext/array/conversions.rb.

التابع to_formatted_s

يتصرّف التابع to_formatted_s مثل to_s افتراضيًّا.

ولكن إن احتوت المصفوفة على عناصر تستجيب للمعرِّف id، يمكن تمرير الرمز db: كوسيط. يستخدم ذلك عادةً مع مجموعات كائنات Active Record. السلاسل النصيّة المعادة هي:

[].to_formatted_s(:db)            # => "null"
[user].to_formatted_s(:db)        # => "8456"
invoice.lines.to_formatted_s(:db) # => "23,567,556,12"

من المفترض أن تأتي الأعداد الصحيحة في المثال أعلاه من الاستدعاءات المعنيّة إلى id.

ملاحظة: مُعرّف في active_support/core_ext/array/conversions.rb.

التابع to_xml

يعيد التابع to_xml سلسلة نصية تحتوي على تمثيل XML للمستقبل المعطى:

Contributor.limit(2).order(:rank).to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors type="array">
#   <contributor>
#     <id type="integer">4356</id>
#     <name>Jeremy Kemper</name>
#     <rank type="integer">1</rank>
#     <url-id>jeremy-kemper</url-id>
#   </contributor>
#   <contributor>
#     <id type="integer">4404</id>
#     <name>David Heinemeier Hansson</name>
#     <rank type="integer">2</rank>
#     <url-id>david-heinemeier-hansson</url-id>
#   </contributor>
# </contributors>

للقيام بذلك، يرسل to_xml إلى كل عنصر بدوره ويجمع النتائج تحت عقدة جذر (root node). يجب أن تستجيب كل العناصر إلى to_xml ويُرفَع استثناء فيما عدا ذلك.

افتراضيًّا اسم العنصر الجذر هو صيغة الجمع عبر الشرطة السفلية (underscored) والشرطة العادية (dasherized) من اسم صنف العنصر الأول، شريطة أن تنتمي بقية العناصر لنفس النوع (يُتحقَّق منها عبر ?is_a) وأن لا يكونوا جداول Hash. في المثال أعلاه ذاك هو "contributors".

إن كان هناك أي عنصر لا ينتمي للنوع الأول، تصبح العقدة الجذرية "objects":

[Contributor.first, Commit.first].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
#   <object>
#     <id type="integer">4583</id>
#     <name>Aaron Batalion</name>
#     <rank type="integer">53</rank>
#     <url-id>aaron-batalion</url-id>
#   </object>
#   <object>
#     <author>Joshua Peek</author>
#     <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp>
#     <branch>origin/master</branch>
#     <committed-timestamp type="datetime">2009-09-02T16:44:36Z</committed-timestamp>
#     <committer>Joshua Peek</committer>
#     <git-show nil="true"></git-show>
#     <id type="integer">190316</id>
#     <imported-from-svn type="boolean">false</imported-from-svn>
#     <message>Kill AMo observing wrap_with_notifications since ARes was only using it</message>
#     <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
#   </object>
# </objects>

يُعتبر أيضًا عنصر الجذر افتراضيَّا "objects" إن كان المستقبل مصفوفة من جداول Hash:

[{a: 1, b: 2}, {c: 3}].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
#   <object>
#     <b type="integer">2</b>
#     <a type="integer">1</a>
#   </object>
#   <object>
#     <c type="integer">3</c>
#   </object>
# </objects>

تحذير: إذا كانت المجموعة فارغةً، يكون العنصر الجذر افتراضيًا هو "nil-classes" (أصناف عدمية). على سبيل المثال، لم يكن العنصر الجذر ليكون في قائمة المساهمين أعلاه "contributors" إذا كانت المجموعة فارغةً لكن كانت لتكون "nil-classes" (أصناف عدمية). تستطيع استخدام الخيار root: لضمان عنصر جذر ثابت.

اسم العقد الأبناء (children nodes) هو افتراضيًّا صيغة المفرد من اسم عقدة الجذر. رأينا في الأمثلة أعلاه "contributor" و "object". يسمح لك الخيار children: بتعيين أسماء العقد.

الباني XML الافتراضي هو نسخة (instance) جديدة من Builder::XmlMarkup. تستطيع إعداد الباني الخاص بك عبر الخيار builder:. يقبل التابع أيضًا خيارات مثل الخيار dasherize: ورفقائه، وتُوجَّه إلى الباني:

Contributor.limit(2).order(:rank).to_xml(skip_types: true)
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors>
#   <contributor>
#     <id>4356</id>
#     <name>Jeremy Kemper</name>
#     <rank>1</rank>
#     <url-id>jeremy-kemper</url-id>
#   </contributor>
#   <contributor>
#     <id>4404</id>
#     <name>David Heinemeier Hansson</name>
#     <rank>2</rank>
#     <url-id>david-heinemeier-hansson</url-id>
#   </contributor>
# </contributors>

ملاحظة: مُعرّف في active_support/core_ext/array/conversions.rb.

التغليف

يلف التابع Array.wrap وسائطه بمصفوفة إلا إذا كانت بالفعل في مصفوفة (أو شبه مصفوفة).

تحديدًا:

  • إن كان الوسيط هو nil، فتعاد مصفوفة فارغة.
  • في ما عدا ذلك، يستدعى to_ary إن كان الوسيط يستجيب له، ويعيد قيمة إن كانت تخالف nil.
  • في ما عدا ذلك، تعاد مصفوفة مع الوسيط كعنصر واحد.
Array.wrap(nil)       # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0)         # => [0]

يشابه هذا التابع في الغرض التابع Kernel.Array لكن هناك بعض الاختلافات:

  • إن استجاب الوسيط للتابع to_ary، فيُستدعَى التابع. ينتقل Kernel.Array لتجربة to_a إن كانت القيمة المعادة nil لكن يعيد Array.wrap مصفوفة مع الوسيط كعنصرها الوحيد فورًا.
  • يرمي التابع Kernel.Array استثناءً إن لم تكن القيمة المعادة من to_ary هي nil ولا هي مصفوفة، في حين أن Array.wrap لا ترمي أي استثناء، بل فقط تعيد القيمة.
  • لا يستدعي to_a مع الوسيط؛ فإن لم يستجب الوسيط للتابع to_ary، فيعيد مصفوفة مع الوسيط كعنصره الوحيد.

النقطة الأخيرة هي أهمية الموازنة لبعض الكائنات القابلة للتعداد (enumerables):

Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar)      # => [[:foo, :bar]]

يوجد أيضًا مصطلح ذو علاقة يستخدم المعامل *:

[*object]

والذي في الإصدار 1.8 من روبي يعيد [nil] من أجل nil ويستدعي إلى Array(object)‎ في ما عدا ذلك.

وبالتالي، يختلف في هذه الحالة السلوك عن nil والاختلافات مع Kernel.Array الموضّحة أعلاه تنطبق على بقية الكائنات object.

ملاحظة: مُعرّف في active_support/core_ext/array/wrap.rb.

التكرار

يكرِّر التابع Array.deep_dup نفسه وكافة الكائنات داخله بشكل عَودي (recursively) مع التابع Object.deep_dup الذي يخص Active Support. وتعمل مثل Array.map مع إرسال التابع deep_dup لكل كائن بالداخل.

array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil   # => true

ملاحظة: مُعرّف في active_support/core_ext/object/deep_dup.rb.

التجميع

التابع in_groups_of(number, fill_with = nil)‎

يقسّم التابع in_groups_of مصفوفة إلى مجموعات متتالية ذات حجم معين. ثم يعيد مصفوفة من المجموعات:

[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]

أو يمرِّرها إلى الكتلة إن أعطيت:

<% sample.in_groups_of(3) do |a, b, c| %>
  <tr>
    <td><%= a %></td>
    <td><%= b %></td>
    <td><%= c %></td>
  </tr>
<% end %>

يعرض المثال الأول in_groups_of المجموعة الأخيرة بعدد عناصر من القيمة nil حسب الحاجة للحصول على الحجم المطلوب. تستطيع تغيير قيمة هذه الحشوة (padding) باستخدام الوسيط الاختياري الثاني:

[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]

وتستطيع أمر التابع بعدم ملء آخر مجموعة بالقيمة nil عبر تمرير القيمة false:

[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]

نتيجةً لذلك، لا يمكن استخدام false كحشوة (padding) له.

ملاحظة: مُعرّف في active_support/core_ext/array/grouping.rb.

التابع in_groups(number, fill_with = nil)‎

يقسّم التابع in_groups المصفوفة إلى عدد معيّن من المجموعات. يعيد التابع مصفوفة من المجموعات:

%w(1 2 3 4 5 6 7).in_groups(3)
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]

أو يمرِّرها إلى كتلة إن أعطيت:

%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
["1", "2", "3"]
["4", "5", nil]
["6", "7", nil]

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

%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]

وتستطيع أن تأمر التابع بعدم حشو المجموعات الأصغر عبر تمرير القيمة false:

%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]

وكنتيجة، لا يمكن استخدام false كحشوة (padding) بدل العناصر الناقصة.

ملاحظة: مُعرّف في active_support/core_ext/array/grouping.rb.

التابع split(value = nil)‎

يقسم التابع split المصفوفة بفاصل ويعيد القطع الناتجة.

إذا مُرَّرت كتلة، فالفواصل هي عناصر المصفوفة التي تعيد الكتلة القيمة true معها:

(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]

خلا ذلك، تكون القيمة المستلمة كوسيط فاصلًا وهي افتراضيًّا nil:

[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]

لاحظ في المثال السابق أن الفواصل المتتالية تُنتج مصفوفات فارغة.

ملاحظة: مُعرّف في active_support/core_ext/array/grouping.rb.

ملحقات للجداول Hash

التحويلات

التابع to_xml

يعيد التابع to_xml سلسلة نصيّة تحتوي على تمثيل XML للمستقبل المعطى:

{"foo" => 1, "bar" => 2}.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
#   <foo type="integer">1</foo>
#   <bar type="integer">2</bar>
# </hash>

يصنع التابع حلقات تكراريّة حول الأزواج ويبني العُقد التي تعتمد على "القيم" (values). انطلاقًا من زوج من مفتاح key/قيمة value:

  • يوجد نداء عودي مع المفتاح key كجذر root: إن كانت القيمة value جدول Hash.
  • إن كانت القيمة value مصفوفة، يوجد نداء عودي مع المفتاح key كجذر root: ومُفرد المفتاح key كأبناء children:.
  • إن كانت value كائنًا قابلًا للاستدعاء، فيجب أن تتوقع وسيطًا واحدًا أو وسيطين. يستدعى الكائن القابل للاستدعاء (callable) اعتمادًا على arity مع options ذي النوع Hash كوسيط أوّل مع key كجذر root: ومُفرد key كوسيط ثاني. تصبح القيمة المعادة له عقدةً جديدةً.
  • يستدعَى التابع مع key كجذر ‎:root إن كانت value تستجيب إلى to_xml.
  • في ما عدا ذلك، تُنشَأ عقدة مع key كوسم (tag) مع تمثيل سلسلة من value كعقدة نصية. إذا كانت value ذات قيمة nil، تُضاف إليها الخاصيّة "nil" قيمتها "true". ما لم يكن الخيار skip_types: ومضبوطًا إلى القيمة "true"، تُضاف الخاصيّة "type" أيضًا وفقًا للتعين التالي:
XML_TYPE_NAMES = {
  "Symbol"     => "symbol",
  "Integer"    => "integer",
  "BigDecimal" => "decimal",
  "Float"      => "float",
  "TrueClass"  => "boolean",
  "FalseClass" => "boolean",
  "Date"       => "date",
  "DateTime"   => "datetime",
  "Time"       => "datetime"
}

تكون العقدة الجذرية هي "hash" افتراضيًّا ولكن هذا قابلة للضبط (configurable) عبر الخيار root:.

باني XML الافتراضي هو نسخة (instance) جديدة من Builder::XmlMarkup. تستطيع إعداد الباني الخاص بك عبر الخيار builder:. يقبل التابع أيضًا خيارات مثل الخيار dasherize: ورفقائه، وتُوجّه إلى الباني:

ملاحظة: مُعرّف في active_support/core_ext/hash/conversions.rb.

الدمج

يحتوي روبي على التابع Hash.merge الضمني الذي يدمج كائنين من النوع Hash:

{a: 1, b: 1}.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}

يحدّد Active Support عدة طرق أخرى لدمج الجداول Hash التي قد تكون مريحة.

التابعان reverse_merge!‎ و reverse_merge

في حالة الصدام، يفوز المفتاح في الجدول Hash للوسيط في merge. تستطيع دعم الجداول Hash للخيارات مع القيم الافتراضية بطرق مختصرة باستخدام هذا المصطلح:

options = {length: 30, omission: "..."}.merge(options)

يعرّف "Active Support" التابع reverse_merge في حال كنت تفضّل هذا الترميز التدوين:

options = options.reverse_merge(length: 30, omission: "...")

والإصدار المُغيِّر (bang version) للتابع !reverse_merge الذي يعدِّل على المستقبل نفسه:

options.reverse_merge!(length: 30, omission: "...")

تحذير: خذ بالحسبان أنَّ !reverse_merge قد يغيّر الجدول Hash في المستدعي وهذا قد أو قد لا يكون فكرة جيدة.

ملاحظة: مُعرّف في active_support/core_ext/hash/reverse_merge.rb.

التابع reverse_update

التابع reverse_update هو اسم بديل للتابع !reverse_merge، كما وضّح أعلاه.

تحذير: لاحظ أنَّ التابع reverse_update لا يملك تابعًا مغيّرًا (bang).

ملاحظة: مُعرّف في active_support/core_ext/hash/reverse_merge.rb.

التابعان deep_merge و deep_merge!‎

كما ترى في المثال السابق، إن عُثر على مفتاح في كلا جدولي Hash، ستفوز القيمة الموجودة في الوسيط.

يعرّف "Active Support" التابع Hash.deep_merge. إن عُثر في عملية دمج عميقة على مفتاح في كلا الجدولين وكانت قيمة كل منهما جدول Hash أيضًا، يصبح دمجهما (merge) القيمة في الجدول Hash الناتج:

{a: {b: 1}}.deep_merge(a: {c: 2})
# => {:a=>{:b=>1, :c=>2}}

ينفذّ التابع deep_merge!‎ عملية دمج عميقة في الموضع.

ملاحظة: مُعرّف في active_support/core_ext/hash/deep_merge.rb.

التكرار العميق

يكرّر التابع Hash.deep_dup نفسه كل المفاتيح والقيم داخله بشكل عودي مع التابع Object.deep_dup الذي يخص Active Support. ويعمل مثل Enumerator.each_with_object بإرسال التابع deep_dup لكل زوج داخله.

hash = { a: 1, b: { c: 2, d: [3, 4] } }
 
dup = hash.deep_dup
dup[:b][:e] = 5
dup[:b][:d] << 5
 
hash[:b][:e] == nil      # => true
hash[:b][:d] == [3, 4]   # => true

ملاحظة: مُعرّف في active_support/core_ext/object/deep_dup.rb.

العمل مع المفاتيح

التابعان except و !except

يعيد التابع except جدول Hash بعد حذف قائمة المفاتيح المُمرَّرة إليه من الجدول Hash الذي استدعي معه:

{a: 1, b: 2}.except(:a) # => {:b=>2}

إن كان المستقبل يستجيب للتابع convert_key، يُستدعَى التابع على كلّ وسيط من الوسائط. يسمح هذا للتابع except بالتوافق مع الجداول Hash مع وصول بلا مشاكل للنسخة:

{a: 1}.with_indifferent_access.except(:a)  # => {}
{a: 1}.with_indifferent_access.except("a") # => {}

يوجد أيضًا بديل مُغيّر هو !except يزيل المفاتيح من المستقبل نفسه.

ملاحظة: مُعرّف في active_support/core_ext/hash/except.rb.

التابعان transform_keys و !transform_keys

يقبل التابع transform_keys كتلة ويعيد جدول Hash المفاتيح فيه نتجت عن تطبيق الكتلة على مفاتيح المستقبل:

{nil => nil, 1 => 1, a: :a}.transform_keys { |key| key.to_s.upcase }
# => {"" => nil, "1" => 1, "A" => :a}

في حالة صدام المفاتيح، ستُختَار واحدة من القيم. قد لا تكون القيمة المختارة ذاتها كلّ مرّة عند اعطاء الجدول Hash نفسه:

{"a" => 1, a: 2}.transform_keys { |key| key.to_s.upcase }
# The result could either be
# => {"A"=>2}
# or
# => {"A"=>1}

قد يكون هذا التابع مفيدًا على سبيل المثال ببناء تحويلات متخصّصة. على سبيل المثال يستخدم التابعان stringify_keys و symbolize_keys التابع transform_keys لإجراء تحويلات على مفاتيحهم:

def stringify_keys
  transform_keys { |key| key.to_s }
end
...
def symbolize_keys
  transform_keys { |key| key.to_sym rescue key }
end

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

{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_transform_keys { |key| key.to_s.upcase }
# => {""=>nil, "1"=>1, "NESTED"=>{"A"=>3, "5"=>5}}

ملاحظة: مُعرّف في active_support/core_ext/hash/keys.rb.

التابعان stringify_keys و !stringify_keys

يعيد التابع stringify_keys جدول Hash يحتوي على نسخة في شكل سلسلة نصيّة من مفاتيح المستقبل. ويفعل ذلك عن طريق إرسال to_s لهم:

{nil => nil, 1 => 1, a: :a}.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}

في حالة صدام المفاتيح، ستُختار واحدة من القيم. قد لا تكون القيمة المختارة ذاتها كلّ مرّة عند إعطاء جدول Hash نفسه:

{"a" => 1, a: 2}.stringify_keys
# The result could either be
# => {"a"=>2}
# or
# => {"a"=>1}

قد يكون هذا التابع مفيدًا مثلًا لقبول كل من الرموز والسلاسل النصيّة كخيارات بسهولة. على سبيل المثال يعرّف ActionView::Helpers::FormHelper:

def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
  options = options.stringify_keys
  options["type"] = "checkbox"
  ...
end

يمكن للسطر الثاني الوصول بأمان إلى المفتاح "type"، والسماح للمستخدم بتمرير إمّا type: أو "type".

يوجد أيضًا البديل المُغيّر وهو !stringify_keys الذي يعمل على تحويل المفاتيح لسلاسل نصيّة في المستقبل ذاته.

يمكن للمرء علاوة على ذلك استخدام deep_stringify_keys و !deep_stringify_keys لتحويل جميع المفاتيح لسلسة نصيّة في الجدول Hash المُعطى وكل الجداول المتداخلة داخله. هذا مثال على النتيجة:

{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_stringify_keys
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}

ملاحظة: مُعرّف في active_support/core_ext/hash/keys.rb.

التابعان symbolize_keys و !symbolize_keys

يعيد التابع symbolize_keys جدول Hash يحتوي على نسخة رمزية من مفاتيح المستقبل حيثما أمكن ذلك وذلك عن طريق تطبيق to_sym عليهم:

{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}

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

{"a" => 1, a: 2}.symbolize_keys
# The result could either be
# => {:a=>2}
# or
# => {:a=>1}

قد يكون هذا التابع مفيدًا على سبيل المثال بقبول كل من الرموز والسلاسل النصيّة كخيارات بسهولة. على سبيل المثال يُعرّف ActionController::UrlRewriter:

def rewrite_path(options)
  options = options.symbolize_keys
  options.update(options[:params].symbolize_keys) if options[:params]
  ...
end

يمكن للسطر الثاني الوصول بأمان إلى المفتاح params: والسماح للمستخدم بتمرير params: أو "params".

يوجد أيضًا التابع المغيّر !symbolize_keys لهذا التابع الذي يُرمّز مفاتيح المستقبل نفسه.

يمكن للمرء عدا ذلك استخدام deep_symbolize_keys و !deep_symbolize_keys لترميز جميع المفاتيح في جدول Hash معيّن وكلّ الجداول المتداخلة ضمنه. مثال على النتيجة:

{nil => nil, 1 => 1, "nested" => {"a" => 3, 5 => 5}}.deep_symbolize_keys
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}

ملاحظة: مُعرّف في active_support/core_ext/hash/keys.rb.

التابعان to_options و !to_options

التابعان to_options و !to_options هما اسمان بديلان للتابعين symbolize_keys و !symbolize_keys على التوالي.

ملاحظة: مُعرّف في active_support/core_ext/hash/keys.rb.

التابع assert_valid_keys

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

{a: 1}.assert_valid_keys(:a)  # passes
{a: 1}.assert_valid_keys("a") # ArgumentError

لا يقبل Active Record خيارات غير معروفة عند إنشاء الارتباطات مثلًا. ينفّذ هذا التحكّم عبر assert_valid_keys.

ملاحظة: مُعرّف في active_support/core_ext/hash/keys.rb.

العمل مع القيم

التابعان transform_values و !transform_values

يقبل التابع transform_values كتلة ويعيد جدول Hash القيم فيه ناتجة عن تطبيق الكتلة على كل قيمة من قيم المستقبل.

{ nil => nil, 1 => 1, :x => :a }.transform_values { |value| value.to_s.upcase }
# => {nil=>"", 1=>"1", :x=>"A"}

يوجد أيضًا إصدار مغيِّر لهذا التابع هو !transform_values الذي يطبّق الكتلة على القيم في المستقبل ذاته ويستبدلها.

ملاحظة: مُعرّف في active_support/core_ext/hash/transform_values.rb.

التقطيع

يحتوي روبي على دعم مدمج لاقتطاع قطعة خارج السلاسل النصيّة والمصفوفات. يمتد Active Support ليشمل جداول Hash أيضًا:

{a: 1, b: 2, c: 3}.slice(:a, :c)
# => {:a=>1, :c=>3}
 
{a: 1, b: 2, c: 3}.slice(:b, :X)
# => {:b=>2} # المفاتيح غير الموجودة يجري تجاهلها

توَّحد (normalize) المفاتيح إن استجاب المستقبل للتابع convert_key:

{a: 1, b: 2}.with_indifferent_access.slice("a")
# => {:a=>1}

ملاحظة: قد يكون التقطيع مفيدًا لتعقيم خيارات الجداول Hash باستخدام قائمة بيضاء من المفاتيح. هناك أيضًا الإصدار المغير !slice الذي - بالإضافة إلى التعديل على المستقبل نفسه - يعيد ما اقتطعه:

hash = {a: 1, b: 2}
rest = hash.slice!(:a) # => {:b=>2}
hash                   # => {:a=>1}

ملاحظة: مُعرّف في active_support/core_ext/hash/slice.rb.

الاستخراج

يزيل التابع !extract أزواج المفاتيح/القيم المطابقة للمفاتيح المعطاة ويعيدها.

hash = {a: 1, b: 2}
rest = hash.extract!(:a) # => {:a=>1}
hash                     # => {:b=>2}

يعيد التابع !extract نفس صنف المستقبل الفرعي من الجدول Hash.

hash = {a: 1, b: 2}.with_indifferent_access
rest = hash.extract!(:a).class
# => ActiveSupport::HashWithIndifferentAccess

ملاحظة: مُعرّف في active_support/core_ext/hash/slice.rb.

الوصول غير المبال (Indifferent Access)

التابع with_indifferent_access يعيد ActiveSupport::HashWithIndifferentAccess من مستقبله:

{a: 1}.with_indifferent_access["a"] # => 1

ملاحظة: مُعرّف في active_support/core_ext/hash/indifferent_access.rb.

الضغط (Compacting)

يعيد التابعان compact و !compact جدول Hash بدون عناصر قيمتها هي nil.

{a: 1, b: 2, c: nil}.compact # => {a: 1, b: 2}

ملاحظة: مُعرّف في active_support/core_ext/hash/compact.rb.

ملحقات للتعابير النمطية

التابع ?multiline

يعلمنا التابع ?multiline بما إذا كان تعبير نمطي عيَّن الراية m/ أي ما إذا تطابق النقطة سطورًا جديدةً.

%r{.}.multiline?  # => false
%r{.}m.multiline? # => true
 
Regexp.new('.').multiline?                    # => false
Regexp.new('.', Regexp::MULTILINE).multiline? # => true

يستخدم ريلز هذا التابع في مكان واحد وهو أيضًا في تعليمات التوجيه البرمجيّة. التعابير النمطية متعددة الأسطر غير مسموح بها لمتطلّبات توجيه المسار (route requirements) وتسهّل هذه الراية فرض هذا القيد.

def assign_route_options(segments, defaults, requirements)
  ...
  if requirement.multiline?
    raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
  end
  ...
end

ملاحظة: مُعرّف في active_support/core_ext/regexp.rb.

التابع ?match

يعرّف ريلز استخدام ?Regexp.match في إصدارات روبي قبل 2.4:

/oo/.match?('foo')    # => true
/oo/.match?('bar')    # => false
/oo/.match?('foo', 1) # => true

يملك الحمل العكسي (backport) نفس الواجهة ونفس غياب الآثار الجانبيّة عند المستدعي مثل عدم تعيين 1$ ورفقائه ولكنه لا يتمتّع بمزايا السرعة. الغرض منه هو القدرة على كتابة تعليمات برمجيّة توافق الإصدار 2.4. مثلًا، يستخدم ريلز نفسه هذا التابع الخبري داخليًا.

يُعرّف Active Support التابع ?Regexp.match فقط إذا لم يكن موجودًا بحيث تعمل التعليمات البرمجيّة من قبل الإصدار 2.4 أو أحدث حسب الإصدار الأصلي وتستفيد من تحسين الأداء.

ملحقات للمجالات

التابع to_s

يوسِّع Active Support التابع Range.to_s حتى يفهم وسيط تنسيق اختياري. حتى كتابة هذه السطور، التنسيق غير الافتراضي الوحيد المدعوم هو db::

(Date.today..Date.tomorrow).to_s
# => "2009-10-25..2009-10-26"
 
(Date.today..Date.tomorrow).to_s(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"

ينشئ تنسيق db:، كما يوضح المثال، العبارة BETWEEN في SQL. ويُستخدم من طرف Active Record في دعمه لقيم المجال تحت شروط.

ملاحظة: مُعرّف في active_support/core_ext/range/conversions.rb.

التابع ?include

يعلمنا التابعان ?Range.include و ===.Range إن وقعت بعض القيم بين نهايات نسخة (instance) معيّنة:

(2..3).include?(Math::E) # => true

يوسِّع Active Support هذه التوابع بحيث يكون الوسيط نطاقًا آخر بدوره. نختبر في هذه الحالة ما إذا انتمت نهايات مجال الوسيط للمستقبل نفسه:

(1..10).include?(3..7)  # => true
(1..10).include?(0..7)  # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9)  # => false
 
(1..10) === (3..7)  # => true
(1..10) === (0..7)  # => false
(1..10) === (3..11) # => false
(1...9) === (3..9)  # => false

ملاحظة: مُعرّف في active_support/core_ext/range/include_range.rb.

التابع ?overlaps

يُعلمنا التابع ?Range.overlaps إن وُجد تقاطع غير خالي (non-void intersection) بين أي مجالين محددين:

(1..10).overlaps?(7..11)  # => true
(1..10).overlaps?(0..7)   # => true
(1..10).overlaps?(11..27) # => false

ملاحظة: مُعرّف في active_support/core_ext/range/overlaps.rb.

ملحقات للتاريخ (Date)

الحسابات

ملاحظة: تُعرّف كل التوابع التالية في active_support/core_ext/date/calculations.rb.

yesterday
tomorrow
beginning_of_week (at_beginning_of_week)
end_of_week (at_end_of_week)
monday
sunday
weeks_ago
prev_week (last_week)
next_week
months_ago
months_since
beginning_of_month (at_beginning_of_month)
end_of_month (at_end_of_month)
last_month
beginning_of_quarter (at_beginning_of_quarter)
end_of_quarter (at_end_of_quarter)
beginning_of_year (at_beginning_of_year)
end_of_year (at_end_of_year)
years_ago
years_since
last_year
on_weekday?
on_weekend?

تنبيه: تملك توابع الحساب التالية حالات حديِّة (edge cases) في تشرين الأول 1582 وبما أنّ الأيّام 5..14 غير موجودة. لا يُوثّق هذا الدليل سلوكهم في تلك الأيام للإيجاز ولكن يكفي القول أنهم يتصرّفون مثلما تتوقّع تمامًا. أي أنّ Date.new(1582, 10, 4).tomorrow يعيد Date.new(1582, 10, 15).tomorrow وهكذا دواليك. الرجاء التحقّق من test/core_ext/date_ext_test.rb في مجموعة اختبار Active Support من أجل الإطلاع على السلوك المتوقّع.

الخاصية Date.current

يعرّف "Active Support" الخاصية Date.current لتمثِّل اليوم في المنطقة الزمنية الحاليّة. أي مثل Date.today إلّا أنه يحترم المنطقة الزمنية للمستخدم إن حدِّدت. ويعرّف أيضًا Date.yesterday و Date.tomorrow وتوابع نسخة خبرية (instance predicates) مثل ?past و ?today و ?future، و ?on_weekday و ?on_weekend، كل منهم نسبةً إلى Date.current.

تأكّد من استخدام Date.current وليس Date.today عند إجراء مقارنات تاريخ باستخدام التوابع التي تحترم المنطقة الزمنية للمستخدم. هناك حالات قد تكون فيها المنطقة الزمنية للمستخدم في المستقبل مقارنة بالمنطقة الزمنية للنظام والتي يستخدمها Date.today افتراضيًّا. هذا يعني Date.today قد تساوي Date.yesterday.

التواريخ المسماة (Named dates)

beginning_of_week و end_of_week

تعيد التوابع beginning_of_week و end_of_week مواعيد بداية ونهاية الأسبوع على التوالي. من المفترض أن الأسابيع تبدأ يوم الاثنين ولكن يمكن تغيير ذلك من خلال تمرير وسيط أو ضبط الخيط المحلّي Date.beginning_of_week أو config.beginning_of_week.

d = Date.new(2010, 5, 8)     # => Sat, 08 May 2010
d.beginning_of_week          # => Mon, 03 May 2010
d.beginning_of_week(:sunday) # => Sun, 02 May 2010
d.end_of_week                # => Sun, 09 May 2010
d.end_of_week(:sunday)       # => Sat, 08 May 2010

التابع at_beginning_of_week هو اسم بديل للتابع beginning_of_week والتابع at_end_of_week هو اسم بديل للتابع end_of_week.

sunday و monday

يعيد التابعان monday و sunday تواريخ الإثنين والسبت السابقين على التوالي.

d = Date.new(2010, 5, 8)     # => Sat, 08 May 2010
d.monday                     # => Mon, 03 May 2010
d.sunday                     # => Sun, 09 May 2010
 
d = Date.new(2012, 9, 10)    # => Mon, 10 Sep 2012
d.monday                     # => Mon, 10 Sep 2012
 
d = Date.new(2012, 9, 16)    # => Sun, 16 Sep 2012
d.sunday                     # => Sun, 16 Sep 2012
prev_week و next_week

يأخذ التابع next_week رمزًا باسم يوم باللغة الإنجليزية (الافتراضي هو مؤشّر محلية الخيط Date.beginning_of_week، أو config.beginning_of_week، أو monday:) ويعيد التاريخ المقابل لذلك اليوم.

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.next_week              # => Mon, 10 May 2010
d.next_week(:saturday)   # => Sat, 15 May 2010

التابع prev_week مشابه (analogous):

d.prev_week              # => Mon, 26 Apr 2010
d.prev_week(:saturday)   # => Sat, 01 May 2010
d.prev_week(:friday)     # => Fri, 30 Apr 2010

التابع last_week هو اسم بديل للتابع prev_week.

يعمل التابعين next_week و prev_week كلاهما كما هو متوقع عند ضبط Date.beginning_of_week أو config.beginning_of_week.

beginning_of_month و end_of_month

يعيد التابعان beginning_of_month و end_of_month مواعيد بداية ونهاية الشهر:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_month     # => Sat, 01 May 2010
d.end_of_month           # => Mon, 31 May 2010

التابع beginning_of_month هو اسم بديل للتابع at_beginning_of_month والتابع end_of_month هو اسم بديل للتابع at_end_of_month.

beginning_of_quarter و end_of_quarter

يعيد التابعان beginning_of_quarter و end_of_quarter مواعيد بداية ونهاية ربع السنة التقويمية للمستقبل:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_quarter   # => Thu, 01 Apr 2010
d.end_of_quarter         # => Wed, 30 Jun 2010

التابع beginning_of_quarter هو اسم بديل للتابع at_beginning_of_quarter والتابع end_of_quarter هو اسم بديل للتابع at_end_of_quarter.

beginning_of_year و end_of_year

يعيد التابعان beginning_of_year و end_of_year مواعيد بداية ونهاية العام:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_year      # => Fri, 01 Jan 2010
d.end_of_year            # => Fri, 31 Dec 2010

التابع beginning_of_year هو اسم بديل للتابع at_beginning_of_year والتابع end_of_year هو اسم بديل للتابع at_end_of_year.

حسابات تاريخ أخرى

years_ago و years_since

يُستدعَى التابع years_ago مع تاريخ ويعيد التاريخ المقابل قبل السنوات المحدَّدة المُمرَّرة إليه:

date = Date.new(2010, 6, 7)
date.years_ago(10) # => Wed, 07 Jun 2000

بشكل مشابه، التابع years_since يعيد التاريخ المقابل بعد السنوات المحدَّدة المُمرَّرة إليه:

date = Date.new(2010, 6, 7)
date.years_since(10) # => Sun, 07 Jun 2020

إن لم يوجد مثل هذا اليوم، فسيعاد اليوم الأخير من الشهر المقابل:

Date.new(2012, 2, 29).years_ago(3)     # => Sat, 28 Feb 2009
Date.new(2012, 2, 29).years_since(3)   # => Sat, 28 Feb 2015

التابع last_year هو اختصار للتابع (1)years_ago..

months_ago و months_since

يسلك التابعان months_ago و months_since نفس سلوك التابعين السابقين ولكن مع الأشهر:

Date.new(2010, 4, 30).months_ago(2)   # => Sun, 28 Feb 2010
Date.new(2010, 4, 30).months_since(2) # => Wed, 30 Jun 2010

إن لم يوجد مثل هذا اليوم، فسيعاد اليوم الأخير من الشهر المقابل:

Date.new(2010, 4, 30).months_ago(2)    # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010

التابع last_month هو اختصار للتابع (1)months_ago..

weeks_ago

يعمل التابع weeks_ago بشكل مماثل للتابعين السابقين ولكن بالنسبة للأسابيع:

Date.new(2010, 5, 24).weeks_ago(1)    # => Mon, 17 May 2010
Date.new(2010, 5, 24).weeks_ago(2)    # => Mon, 10 May 2010
advance

الطريقة الأكثر بساطة للقفز لأيام أخرى هي عبر استعمال التابع advance. يتلقّى هذا التابع جدول Hash مع المفاتيح: :years و :months و :weeks و :days ويعيد تاريخًا لاحقًا بقدر ما تشير إليه المفاتيح الحالية:

date = Date.new(2010, 6, 6)
date.advance(years: 1, weeks: 2)  # => Mon, 20 Jun 2011
date.advance(months: 2, days: -2) # => Wed, 04 Aug 2010

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

Date.new(2010, 2, 28).advance(months: 1, days: 1)
# => Sun, 29 Mar 2010

بينما لو قلبت الترتيب ستختلف النتيجة:

Date.new(2010, 2, 28).advance(days: 1).advance(months: 1)
# => Thu, 01 Apr 2010

تغيير المكونات (Changing Components)

يسمح لك التابع change بالحصول على تاريخ جديد وهو نفس تاريخ المستقبل باستثناء السنة أو الشهر أو اليوم المحدّد:

Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => Wed, 23 Nov 2011

لا يتسامح هذا التابع مع التواريخ غير الموجودة إذ يرفع الاستثناء ArgumentError إن كان التغيير غير صالح:

Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: invalid date

الفترات (Durations)

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

d = Date.current
# => Mon, 09 Aug 2010
d + 1.year
# => Tue, 09 Aug 2011
d - 3.hours
# => Sun, 08 Aug 2010 21:00:00 UTC +00:00

الفترات تحول إلى استدعاءات إلى التابع since أو التابع advance. نحصل على سبيل المثال على القفزة الصحيحة في إصلاح التقويم:

Date.new(1582, 10, 4) + 1.day
# => Fri, 15 Oct 1582

البصمات الزمنية (Timestamps)

تنبيه: تعيد التوابع التالية كائنًا من النوع Time إن أمكن وإلا تعيد DateTime. إن ضُبطَت، فإنها تحترم المنطقة الزمنيّة للمستخدم.

beginning_of_day و end_of_day

يعيد التابع beginning_of_day بصمة زمنيًّا في بداية اليوم (00:00:00):

date = Date.new(2010, 6, 7)
date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010

يعيد التابع end_of_day بصمة زمنية بنهاية اليوم (23:59:59):

ate = Date.new(2010, 6, 7)
date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010

التابع beginning_of_day هو اسم بديل للتابع at_beginning_of_day والتابع midnight والتابع at_midnight.

beginning_of_hour و end_of_hour

يعيد التابع beginning_of_hour بصمة زمنية في بداية الساعة (hh:00:00):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010

يعيد التابع end_of_hour بصمة زمنية في نهاية الساعة (hh:59:59):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010

التابع beginning_of_hour هو اسم بديل للتابع at_beginning_of_hour.

beginning_of_minute و end_of_minute

يعيد التابع بصمة زمنية في بداية الدقيقة (hh:mm:00):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010

يعيد التابع end_of_minute بصمة زمنية في نهاية الدقيقة (hh:mm:59):

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010

التابع beginning_of_minute هو اسم بديل للتابع at_beginning_of_minute.

تنبيه: تنفَّذ التوابع beginning_of_hour، و end_of_hour، و beginning_of_minute و end_of_minute من أجل الكائن Time والكائن DateTime لكن ليس من أجل الكائن Date لأنه من غير المعقول أن يطلب بداية أو نهاية ساعة أو دقيقة على نسخة من الكائن Date.

ago و since

يأخذ التابع ago عدد الثواني كوسيط ويعيد بصمة زمنية تحمل ذلك العدد من الثواني مطروحًا من منتصف الليل:

date = Date.current # => Fri, 11 Jun 2010
date.ago(1)         # => Thu, 10 Jun 2010 23:59:59 EDT -04:00

يسلك التابع since السلوك ذاته أيضًا ولكن مع التقديم للأمام:

date = Date.current # => Fri, 11 Jun 2010
date.since(1)       # => Fri, 11 Jun 2010 00:00:01 EDT -04:00

ملحقات للصنف DateTime

تحذير: لا يعي الكائن DateTime قواعد التوقيت الصيفي (DST)، ولبعض هذه التوابع حالات حدية (edge cases) عند تغيير التوقيت الصيفي. على سبيل المثال، قد لا يعيد التابع seconds_since_midnight التوقيت الحقيقي في مثل هذا اليوم.

الحسابات

ملاحظة: تُعرّف جميع التوابع التالية في active_support/core_ext/date_time/calculations.rb.

الصنف DateTime هو صنف فرعي من Date، لذا ترث عند تحميل active_support/core_ext/date/calculations.rb هذه التوابع وأسماءها البديلة، باستثناء أنها ستعيد دائمًا كائنًا من النوع DateTime.

يُعاد تعريف استخدام التوابع التالية بحيث لا تحتاج لتحميل active_support/core_ext/date/calculations.rb من أجلها:

beginning_of_day (midnight, at_midnight, at_beginning_of_day)
end_of_day
ago
since (in)

من ناحية أخرى، تجد أنّ advance و change مُعرّفان أيضًا ويدعمان المزيد من الخيارات، وهما موثّقان أدناه. يعرّف استخدام التوابع التالية فقط في active_support/core_ext/date_time/calculations.rb حيث أن استخدامها منطقي فقط مع النسخة DateTime:

beginning_of_hour (at_beginning_of_hour)
end_of_hour

التواريح والأوقات المسماة (Named Datetimes)

DateTime.current

يعرّف Active Support التابع DateTime.current ليكون مثل Time.now.to_datetime باستثناء أنه يحترم المنطقة الزمنية للمستخدم إن عُرّفت. كما أنه يعرّف DateTime.yesterday و DateTime.tomorrow وتوابع النسخة الخبرية (instance predicates) التاليين: ?past و ?future نسبةً إلى DateTime.current.

ملحقات أخرى

seconds_since_midnight

يعيد seconds_since_midnight عدد الثواني منذ منتصف الليل:

now = DateTime.current     # => Mon, 07 Jun 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596
utc

يمنحك التابع utc نفس التاريخ والوقت عند المستقبل ولكن بالتوقيت العالمي الموحد (UTC).

now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
now.utc                # => Mon, 07 Jun 2010 23:27:52 +0000

يملك هذا التابع أيضًا اسمًا بديلًا هو getutc.

?utc

يعلمنا التابع الخبري ?utc إن كانت المنطقة الزمنية للمستقبل هي UTC:

now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
now.utc?           # => false
now.utc.utc?       # => true
advance

الطريقة الأكثر بساطة للانتقال إلى تاريخ ووقت آخرين هو استعمال التابع advance. يأخذ هذا التابع جدول Hash بالمفاتيح: years:، و months:، و weeks:، و days:، و hours:، و minutes:، و seconds: ويعيد تاريخ ووقت مُقدّم بقدر ما تشير إليه هذه المفاتيح.

d = DateTime.current
# => Thu, 05 Aug 2010 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
# => Tue, 06 Sep 2011 12:34:32 +0000

يحسب هذا التابع التاريخ المقصود أولًا عبر تمرير years: و months: و weeks: و days: إلى التابع Date.advance المذكور أعلاه. يضبط بعدها الوقت باستدعاء since مع عدد الثواني المراد تقديمها إلى الأمام. هذا الترتيب مهم إذ سيعطي أي ترتيب مختلف تواريخًا مختلفة في بعض الحالات الحدية. ينطبق المثال في Date.advance ويمكننا تمديده لاظهار مدى صلة الطلب ببتات (bits) الوقت. إذا قمنا أولًا بتحريك بتات التاريخ (التي تملك أيضًا ترتيبًا نسبيًّا للمعالجة كما ذكر سابقًا) ثم بتات الوقت، نحصل على سبيل المثال على الحساب التالي:

d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => Sun, 28 Feb 2010 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => Mon, 29 Mar 2010 00:00:00 +0000

ولكن ستختلف النتيجة إن قمنا بحسابها بطريقة أخرى:

d.advance(seconds: 1).advance(months: 1)
# => Thu, 01 Apr 2010 00:00:00 +0000

تحذير: نظرًا لأن DateTime لا يعي التوقيت الصيفي (DST)، من الممكن أن تجد نفسك في نقطة غير موجودة بالوقت دون أي تحذير أو خطأ يخبرك بذلك.

تغيير المكونات

يسمح لك التابع change بالحصول على تاريخ ووقت جديد مماثل للمستقبل باستثناء الخيارات المحدّدة والتي قد تتضمّن: year: و month: و day: و hour: و min: و sec: و offset: و start::

now = DateTime.current
# => Tue, 08 Jun 2010 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => Wed, 08 Jun 2011 01:56:22 -0600

تكون الدقائق والثواني صفرًا إن كانت الساعات صفرًا أيضًا (ما لم يُعطوا قيمًا):

now.change(hour: 0)
# => Tue, 08 Jun 2010 00:00:00 +0000

وبشكل مماثل، إن كانت الدقائق صفرًا، ينطبق نفس الشيء على الثواني أيضًا (ما لم تُعطَ قيمة):

now.change(min: 0)
# => Tue, 08 Jun 2010 01:00:00 +0000

لا يتقبّل هذا التابع التواريخ غير الموجودة، إذ يرمي الاستثناء ArgumentError إن لم يكن التغيير صحيحًا:

DateTime.current.change(month: 2, day: 30)
# => ArgumentError: invalid date

الفترات

يمكن إضافة الفترات وطرحها من التواريخ بالشكل التالي:

now = DateTime.current
# => Mon, 09 Aug 2010 23:15:17 +0000
now + 1.year
# => Tue, 09 Aug 2011 23:15:17 +0000
now - 1.week
# => Mon, 02 Aug 2010 23:15:17 +0000

وهي تحول إلى نداءات إلى since أو advance. نحصل هنا على سبيل المثال على القفزة الصحيحة في إصلاح التقويم (calendar reform):

DateTime.new(1582, 10, 4, 23) + 1.hour
# => Fri, 15 Oct 1582 00:00:00 +0000

ملحقات للوقت

الحسابات

ملاحظة: تُعرّف جميع التوابع التالية في active_support/core_ext/time/calculations.rb.

past?
today?
future?
yesterday
tomorrow
seconds_since_midnight
change
advance
ago
since (in)
prev_day
next_day
beginning_of_day (midnight, at_midnight, at_beginning_of_day)
end_of_day
beginning_of_hour (at_beginning_of_hour)
end_of_hour
beginning_of_week (at_beginning_of_week)
end_of_week (at_end_of_week)
monday
sunday
weeks_ago
prev_week (last_week)
next_week
months_ago
months_since
beginning_of_month (at_beginning_of_month)
end_of_month (at_end_of_month)
prev_month
next_month
last_month
beginning_of_quarter (at_beginning_of_quarter)
end_of_quarter (at_end_of_quarter)
beginning_of_year (at_beginning_of_year)
end_of_year (at_end_of_year)
years_ago
years_since
prev_year
last_year
next_year
on_weekday?
on_weekend?

هذه التوابع متماثلة. يُرجى الرجوع إلى توثيق كلٍّ منهم أعلاه مع مراعاة الاختلافات التاليّة:

  • يقبل التابع change خيارًا إضافيًا هو usec:
  • يعي Time التوقيت الصيفي (DST) بحيث تحصل على حسابات التوقيت الصيفي الصحيحة
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
 
# In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to DST.
t = Time.local(2010, 3, 28, 1, 59, 59)
# => Sun Mar 28 01:59:59 +0100 2010
t.advance(seconds: 1)
# => Sun Mar 28 03:00:00 +0200 2010
  • إن قفز التابع since أو التابع ago إلى وقت لا يمكن التعبير عنه مع Time، فسيعاد الكائن DateTime بدل ذلك.

Time.current

يعرّف Active Support التابع Time.current ليكون يومًا في المنطقة الزمنيّة الحاليّة. أي مثل Time.now باستثناء أنّه يحترم المنطقة الزمنية للمستخدم إن عُرّفت. كما يعرِّف توابع النسخة الخبرية ?past و ?today و ?future نسبةً إلى Time.current.

عند إنشاء مقارنات للوقت Time باستعمال توابع تحترم المنطقة الزمنية للمستخدم، تأكد من استعمال Time.current عوضًا عن Time.now. هنالك حالات تقارن فيها المنطقة الزمنية للمستخدم في المستقبل مع المنطقة الزمنية للنظام، إذ يستخدم Time.now افتراضيًا. هذا يعني أن Time.now.to_date قد يساوي Date.yesterday.

all_day و all_week و all_month و all_quarter و all_year

يعيد التابع all_day مجالًا يمثّل يومًا كاملًا من الوقت الحالي.

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_day
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:59:59 UTC +00:00

وبشكل مماثل، تخدم التوابع all_week و all_month و all_quarter و all_year غرض توليد مجالاتٍ زمنيةٍ.

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
now.all_week(:sunday)
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00
now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00

prev_day و next_day

في الإصدار 1.9 من روبي، يعيد التابعان prev_day و next_day التاريخ في اليوم السابق والتالي على التوالي:

d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.prev_day               # => Fri, 07 May 2010
d.next_day               # => Sun, 09 May 2010

prev_month و next_month

في الإصدار 1.9 من روبي، يعيد التابعان prev_month و next_month التاريخ باليوم نفسه من الشهر الماضي والتالي على التوالي:

d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.prev_month             # => Thu, 08 Apr 2010
d.next_month             # => Tue, 08 Jun 2010

إن لم يوجد مثل هذا اليوم، يعاد اليوم الأخير من الشهر المقابل:

Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000
Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000
Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000
Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000

prev_year و next_year

في الإصدار 1.9 من روبي، يعيد التابعان prev_year و next_year التاريخ باليوم والشهر نفسه من السنة الماضية والتالية على التوالي:

d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.prev_year              # => Fri, 08 May 2009
d.next_year              # => Sun, 08 May 2011

إن كان التاريخ 29 شباط من سنة كبيسة، ستحصل على التاريخ 28 شباط:

d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000
d.prev_year               # => Sun, 28 Feb 1999
d.next_year               # => Wed, 28 Feb 2001

prev_quarter و next_quarter

يعيد التابعان prev_quarter و next_quarter التاريخ نفسه باليوم نفسه في الربع السابق والتالي:

t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300
t.prev_quarter             # => 2010-02-08 00:00:00 +0200
t.next_quarter             # => 2010-08-08 00:00:00 +0300

إن لم يوجد مثل هذا اليوم، يعاد اليوم الأخير من الشهر المقابل:

Time.local(2000, 7, 31).prev_quarter  # => 2000-04-30 00:00:00 +0300
Time.local(2000, 5, 31).prev_quarter  # => 2000-02-29 00:00:00 +0200
Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300
Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200

التابع prev_quarter هو اسم بديل للتابع last_quarter.

بانو الوقت (Time Constructors)

يعرّف Active Support التابع Time.current ليكون Time.zone.now إذا وُجد تعريف للمنطقة الزمنية للمستخدم، مع الرجوع إلى Time.now:

Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
Time.current
# => Fri, 06 Aug 2010 17:11:58 CEST +02:00

يعمل التابعان الخبريان ?past و ?future نسبةً إلى Time.current مثل DateTime.

إن وقع الوقت المُراد بناؤه خارج النطاق المدعوم من قبل Time في منصّة التشغيل (runtime platform)، فيُتجاهل usecs ويعاد الكائن DateTime بدلًا من ذلك.

الفترات

يمكن إضافة فترات وطرحها من كائنات الوقت Time بالشكل التالي:

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now + 1.year
# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
now - 1.week
# => Mon, 02 Aug 2010 23:21:11 UTC +00:00

الفترات تحول إلى استدعاءات إلى since أو advance. نحصل على سبيل المثال على القفزة الصحيحة في إصلاح التقويم:

Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582

ملحقات للملفات

التابع atomic_write

تستطيع باستخدام التابع File.atomic_write الكتابة على ملف بطريقة تمنع أي قارئ من رؤية نصف المحتوى المكتوب.

يُمرّر اسم الملف كوسيط وينتج التابع مقبض ملف (file handle) للكتابة. بمجرد الانتهاء من تنفيذ الكتلة، يغلق atomic_write مقبض الملف ويُكمل وظيفته.

يستخدم Action Pack على سبيل المثال التابع لكتابة ملّفات ذاكرة التخزين المؤقت للأصول (asset cache files) مثل all.css:

File.atomic_write(joined_asset_path) do |cache|
  cache.write(join_asset_file_contents(asset_paths))
end

ولإنجاز هذا الأمر، يُنشئ atomic_write ملفًّا مؤقّتًا. هذا هو الملف الذي تكتب عليه الشيفرة في الواقع. يُعاد عند الانتهاء تسمية الملف المؤقّت، وهي عمليّة ذريّة (atomic operation) على أنظمة POSIX. إن وُجد الملف الهدف، يُعيد atomic_write تعريفه مع الابقاء على المالكين والأذونات (permissions).

ومع ذلك، تظل حالات قليلة لا يقدر atomic_write فيها على تغيير ملكيّة أو إذن الملف فيها؛ يُكتشف هذا الخطأ ويُتجاوَز بسبب الثقة في المستخدم/نظام الملفّات للتأكد من إمكانية الوصول إلى العمليّات (processes) التي يحتاج إليها.

ملاحظة: إن احتوى الملف المُستهدف على ACL، سيُعاد احتسابه/تعديل هذا ACL بسبب العمليّة chmod التي يُنفذّها atomic_write.

تحذير: لاحظ أنك لا تستطيع الإضافة (append) مع atomic_write.

يُكتب الملف الإضافي داخل مجلّد قياسي (standard directory) للملفّات المؤقّتة، ولكن تستطيع تمرير مجلّد من اختيارك كوسيط ثاني.

ملاحظة: مُعرّف في active_support/core_ext/file/atomic.rb.

ملحقات للوحدة Marshal

التابع load

يضيف Active Support دعم تحميل تلقائي ثابت (constant autoloading) إلى load.

يفكّ مخزن ذاكرة التخزين المؤقت على سبيل المثال السلسَلَة على هذا النحو:

File.open(file_name) { |f| Marshal.load(f) }

إن أشارت البيانات المخزّنة مؤقّتًا لثابت غير معروف، تُشغَّل آليّة التحميل التلقائي بهذه المرحلة؛ وإن نجحت، تُعاد محاولة فكّ السَلسَلة بشفافيّة.

تحذير: يجب، إن كان الوسيط كائنٌ من النوع IO، أن يستجيب إلى rewind كي يتمكّن من إعادة المحاولة. تستجيب الملفات العاديّة إلى rewind.

ملاحظة: مُعرّف في active_support/core_ext/marshal.rb.

ملحقات للاستثناء NameError

يضيف Active Support التابع ?missing_name للاستثناء NameError، والذي يختبر ما إذا رُفع الاستثناء بسبب الاسم الذي مُرّر كوسيط.

قد يُعطى الاسم كرمز أو سلسلة نصيّة. يُختَبر الرمز مقابل اسم الثابت المجرّد (bare constant name) والسلسلة النصيّة مقابل اسم ثابت مؤهل بالكامل (bare constant name).

تنبيه: يمكن أن يمثل الرّمز اسمًا ثابتًا مؤهّلًا بالكامل كما في: "ActiveRecord::Base" ، لذلك يُعرّف سلوك الرموز للتبسيط لا لوجوب كونه هكذا تقنيًا.

عند استدعاء إجراء من وحدة التحكم ArticlesController على سبيل المثال، يحاول ريلز بتفاؤل استخدام ArticlesHelper. لا مشكلة في عدم وجود الوحدة المساعدة؛ لذلك، إن رُفع استثناء لذلك الاسم الثابت، فيجب إسكاته. ولكن من الممكن أن يرفع articles_helper.rb الاستثناء NameError بسبب ثابت غير معروف فعلًا. ذلك الاستثناء يجب إعادة رفعه. يوفّر التابع ?missing_name طريقة للتمييز بين الحالتين:

def default_helper_module!
  module_name = name.sub(/Controller$/, '')
  module_path = module_name.underscore
  helper module_path
rescue LoadError => e
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
  raise e unless e.missing_name? "#{module_name}Helper"
end

ملاحظة: مُعرّف في active_support/core_ext/name_error.rb.

ملحقات للاستثناء LoadError

يضيف Active Support التابع ?is_missing إلى LoadError.

يتحقق التابع ?is_missing عندما يُعطى اسم مسار إن رُفع الاستثناء بسبب ذلك الملف المعيّن (باستثناء الملفات ذات الامتداد ".rb" ربّما).

عند استدعاء إجراء من وحدة التحكم ArticlesController مثلًا، يحاول ريلز تحميل articles_helper.rb، ولكن قد لا يوجد هذا الملف. هذا جيّد، فوحدة المُساعد ليست إلزامية كي يُسكت ريلز خطأَ تحميل. ولكن من الممكن أن توجد الوحدة المُساعدة وتطلّب بدورها مكتبة أخرى مفقودة. يجب في هذه الحالة على ريلز إعادة رفع الاستثناء. يوفّر التابع ?is_missing طريقة للتمييز بين كلتا الحالتين:

def default_helper_module!
  module_name = name.sub(/Controller$/, '')
  module_path = module_name.underscore
  helper module_path
rescue LoadError => e
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
  raise e unless e.missing_name? "#{module_name}Helper"
end

ملاحظة: مُعرّف في active_support/core_ext/load_error.rb.

مصادر