الفرق بين المراجعتين ل"Ruby/refinements"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
ط (تنسيقات بسيطة)
ط (مراجعة وتدقيق.)
 
(5 مراجعات متوسطة بواسطة مستخدم واحد آخر غير معروضة)
سطر 1: سطر 1:
= التحسينات = 
+
إن ميّزة الأصناف المفتوحة في لغة روبي تسمح لك بإعادة تعريف أو إضافة وظائف إلى أصناف معرّفة مسبقًا. وهذا ما يسمى بمصطلح "ترقيع القرد" (monkey patch). المشكلة هنا أنَّ تعديلات من هذا النوع تكون مرئيّة على المستوى العام (globalوبالتالي جميع مستخدمي الصنف المرقّع قادرون على رؤية هذه التغييرات، ممّا قد يسبّب تأثيرات جانبيّة غير محسوبة أو حتى عطب في البرامج.
إن ميّزة الأصناف المفتوحة في لغة روبي تسمح لك بإعادة تعريف أو إضافة وظائف إلى أصناف معرّفة مسبقًا. وهذا ما يسمى بـ "ترقيع القرد" أو monkey patch. المشكلة هنا أن تعديلات من هذا النوع تكون مرئيّة على المستوى العام global وبالتالي جميع مستخدمي الصنف المرقّع قادرون على رؤية هذه التغييرات، ممّا قد يسبّب تأثيرات جانبيّة غير محسوبة أو حتى عطب في البرامج.
+
 
تأتي التحسينات هنا لتقلّل أثر ترقيع القرد على مستخدمي الصنف الآخرين، إذ تقدّم طريقة لتوسيع الصنف محليًّا. وإليك مثال على تحسين بسيط:
+
تأتي التحسينات هنا لتقلّل أثر ترقيع القرد على مستخدمي الصنف الآخرين، إذ تقدّم طريقة لتوسيع الصنف محليًّا. وإليك مثال على تحسين بسيط:<syntaxhighlight lang="ruby">
<syntaxhighlight lang="ruby">
 
 
class C
 
class C
 
 def foo
 
 def foo
سطر 15: سطر 14:
 
 end
 
 end
 
end 
 
end 
</syntaxhighlight>
+
</syntaxhighlight>في البداية يُعرّف الصنف <code>C</code>، ثم يُجرى له تحسينًا باستخدام التابع <code>[[Ruby/Module/refine|Module.refine]]</code>. وبما أنّ التحسينات تعدّل أصنافًا فقط ولا تعدّل وحدات، فيجب أن يكون الوسيط الممرر إليه صنفًا.
في البداية يُعرّف الصنف ثم يُجرى له تحسينًا باستخدام Module#refine. وبما أنّ التحسينات تعدّل أصنافًا فقط ولا تعدّل كينونات فيجب أن يكون المعامل الممرر لها صنفًا.
+
 
يقوم Module#refine بعدئذ بإنشاء كينونة ليست ذات اسم anonymous  وإنّما تحوي التعديلات أو التحسينات المطبّقة على هذا الصنف (في مثالنا الصنف C). واستخدام self أو Module#module_eval سيشير إلى هذه الكينونة الجديدة.
+
ينشئ <code>[[Ruby/Module/refine|Module.refine]]</code> بعدئذٍ وحدة مجهولة (anonymous) تحوي التعديلات أو التحسينات المطبّقة على هذا الصنف (في مثالنا الصنف C). واستخدام <code>self</code> أو <code>[[Ruby/Module/module eval|Module.module_eval]]</code> سيشير إلى هذه الوحدة الجديدة.
أمّا تفعيل هذا التحسين فيكون باستخدام using:
+
 
<syntaxhighlight lang="ruby">
+
أمّا تفعيل هذا التحسين فيكون باستخدام <code>[[Ruby/Module/using|using]]</code>:<syntaxhighlight lang="ruby">
 
using M
 
using M
 
c = C.new
 
c = C.new
 
c.foo # "C#foo in M" تطبع
 
c.foo # "C#foo in M" تطبع
 
</syntaxhighlight>
 
</syntaxhighlight>
== النطاق ==
+
==النطاق==
يمكنك تفعيل التحسينات في المستوى الأعلى من البرنامج، أو ضمن الأصناف والكينونات، لكن لا يمكنك حصرها ضمن نطاق تابع معيّن، إذ أنها تبقى مفعّلة حتى نهاية تعريف الصنف أو الكينونة الحاليّة، و في حال تفعيلها في المستوى الأعلى من البرنامج تبقى مفعّلة حتى نهاية الملف الحالي.
+
يمكنك تفعيل التحسينات في المستوى الأعلى من البرنامج، أو ضمن الأصناف والوحدات، لكن لا يمكنك حصرها ضمن نطاق تابع معيّن، إذ أنها تبقى مفعّلة حتى نهاية تعريف الصنف أو الوحدة الحاليّة. و في حال تفعيلها في المستوى الأعلى من البرنامج، تبقى مفعّلة حتى نهاية الملف الحالي.
كما بإمكانك تفعيل التحسينات في سلسلة أوامر ممرّرة إلى Kernel#eval، وحينها تبقى فعالة حتى نهاية هذه السلسلة.
+
 
تبدأ صلاحية التحسينات ضمن نطاق معيّن عند استدعاء using، وبالتالي فإنّ أيّ سطر برمجيّ يسبق هذا الاستدعاء لن يكون للتحسينات أثر عليه. 
+
كما بإمكانك تفعيل التحسينات في سلسلة أوامر ممرّرة إلى التابع <code>[[Ruby/Kernel/eval|Kernel.eval]]</code>، وحينها تبقى فعالة حتى نهاية هذه السلسلة.
وحين ينتقل التحكّم إلى خارج النّطاق يُلغى تفعيل التحسينات تلقائيًّا، وهذا يعني أنّك إن قمت بطلب أو تحميل ملفّ، أو أجريت استدعاءً لتابع معرّف خارج النّطاق الحاليّ، فسيلغى تفعيل التحسينات تلقائيًّا:
+
 
<syntaxhighlight lang="ruby">
+
تبدأ صلاحية التحسينات ضمن نطاق معيّن عند استدعاء [[Ruby/Module/using|<code>using</code>]]، وبالتالي فإنّ أيّ سطر برمجيّ يسبق هذا الاستدعاء لن يكون للتحسينات أثر عليه. 
 +
 
 +
وحين ينتقل التحكّم إلى خارج النّطاق، يُلغى تفعيل التحسينات تلقائيًّا؛ وهذا يعني أنّك إن قمت بطلب أو تحميل ملفّ، أو أجريت استدعاءً لتابع معرّف خارج النّطاق الحاليّ، فسيلغى تفعيل التحسينات تلقائيًّا:<syntaxhighlight lang="ruby">
 
class C
 
class C
 
end
 
end
سطر 46: سطر 47:
 
x.foo       # "C#foo in M" يطبع  
 
x.foo       # "C#foo in M" يطبع  
 
call_foo(x) #=> NoMethodError يسبب  
 
call_foo(x) #=> NoMethodError يسبب  
</syntaxhighlight>
+
</syntaxhighlight>إذا عُرّف تابع في نطاق كانت فيه التحسينات مفعّلة، فستبقى هذه التحسينات على حالة تفعيلها عند استدعاء هذا التابع. إليك المثال التالي الذي يمتدّ على ملفّات عدّة:
إذا عُرّف تابع في نطاق كانت فيه التحسينات مفعّلة، فستبقى هذه التحسينات على حالة تفعيلها عند استدعاء هذا التابع، إليك المثال التالي الذي يمتدّ على ملفّات عدّة:
+
 
c.rb:
+
الملف c.rb:<syntaxhighlight lang="ruby">
<syntaxhighlight lang="ruby">
 
 
class C
 
class C
 
end
 
end
m.rb:
+
 
 +
</syntaxhighlight>الملف m.rb:<syntaxhighlight lang="ruby">
 
require "c"
 
require "c"
 +
 
module M
 
module M
 refine C do
+
  refine C do
   def foo
+
    def foo
     puts "C#foo in M"
+
      puts "C#foo in M"
   end
+
    end
 end
+
  end
 
end
 
end
</syntaxhighlight>
+
</syntaxhighlight>الملف m_user.rb:<syntaxhighlight lang="ruby">
m_user.rb:
 
<syntaxhighlight lang="ruby">
 
 
require "m"
 
require "m"
 
using M
 
using M
سطر 71: سطر 71:
 
 end
 
 end
 
end
 
end
</syntaxhighlight>
+
</syntaxhighlight>الملف main.rb:<syntaxhighlight lang="ruby">
main.rb:
 
<syntaxhighlight lang="ruby">
 
 
require "m_user"
 
require "m_user"
 
x = C.new
 
x = C.new
سطر 79: سطر 77:
 
m_user.call_foo(x) # "C#foo in M" يطبع
 
m_user.call_foo(x) # "C#foo in M" يطبع
 
x.foo              #=> NoMethodError يسبب
 
x.foo              #=> NoMethodError يسبب
</syntaxhighlight>
+
</syntaxhighlight>بما أنّ التحسين <code>M</code> فعال في الملف m_user.rb حيث تعريف التابع <code>MUser.call_foo</code>، فسيبقى هذا التحسين فعالًا عندما يُستدعى التابع <code>call_foo</code> في الملف main.rb.
بما أنّ التحسين M فعال في الملف m_user.rb حيث تعريف التابع MUser#call_foo فسيبقى هذا التحسين فعالًا عندما يُستدعى التابع call_foo في الملف main.rb
+
 
وبما أنّ using هو تابع، فستفعّل التحسينات فقط عند استدعائه، وإليك أمثلة حيث يكون التحسين M مفعلًا أو غير مفعّل.
+
وبما أنّ [[Ruby/Module/using|<code>using</code>]] هو تابع، فستفعّل التحسينات فقط عند استدعائه، وإليك أمثلة حيث يكون التحسين <code>M</code> مفعلًا أو غير مفعّل.
في ملف:
+
 
<syntaxhighlight lang="ruby">
+
في ملف:<syntaxhighlight lang="ruby">
 
# غير فعال هنا
 
# غير فعال هنا
 
using M
 
using M
سطر 95: سطر 93:
 
end
 
end
 
# فعال هنا
 
# فعال هنا
</syntaxhighlight>
+
</syntaxhighlight>في صنف:<syntaxhighlight lang="ruby">
في صنف:
 
<syntaxhighlight lang="ruby">
 
 
# غير فعال هنا
 
# غير فعال هنا
 
class Foo
 
class Foo
سطر 112: سطر 108:
 
end
 
end
 
# غير فعال هنا
 
# غير فعال هنا
</syntaxhighlight>
+
</syntaxhighlight>لاحظ أنّ التحسينات <code>M</code> '''لا''' تفعّل تلقائيًّا في حال أُعيد فتح الصنف لاحقًا.
لاحظ أنّ التحسينات M لا تفعّل تلقائيًّا في حال أُعيد فتح الصنف لاحقًا.
+
 
في eval:
+
في <code>[[Ruby/Kernel/eval|eval]]</code>:<syntaxhighlight lang="ruby">
<syntaxhighlight lang="ruby">
 
 
# غير فعال هنا
 
# غير فعال هنا
 
eval <<EOF
 
eval <<EOF
سطر 123: سطر 118:
 
EOF
 
EOF
 
# غير فعال هنا
 
# غير فعال هنا
</syntaxhighlight>
+
</syntaxhighlight>عند عدم تحقّق التنفيذ:<syntaxhighlight lang="ruby">
عند عدم تحقّق التنفيذ:<syntaxhighlight lang="ruby">
 
 
# غير فعال هنا
 
# غير فعال هنا
 
if false
 
if false
سطر 130: سطر 124:
 
end
 
end
 
# غير فعال هنا
 
# غير فعال هنا
</syntaxhighlight>
+
</syntaxhighlight>عند تعريف عدة تحسينات في نفس الوحدة داخل عدّة كتل برمجيّة محسِّنة، فكلّ التحسينات في هذه الوحدة تصبح فعالة عند استدعاء تابع محسَّن (أيّ من توابع <code>to_json</code> في المثال أدناه) :<syntaxhighlight lang="ruby">
 
 
عند تعريف عدة تحسينات في نفس الكينونة داخل عدّة كتل برمجيّة محسِّنة، فكلّ التحسينات في هذه الكينونة تصبح فعالة عند استدعاء تابع محسَّن (أيّ من توابع to_json في المثال أدناه) :
 
<syntaxhighlight lang="ruby">
 
 
module ToJSON
 
module ToJSON
 
 refine Integer do
 
 refine Integer do
سطر 154: سطر 145:
 
p [{1=>2}, {3=>4}].to_json # "[{\"1\":2},{\"3\":4}]" تطبع
 
p [{1=>2}, {3=>4}].to_json # "[{\"1\":2},{\"3\":4}]" تطبع
 
</syntaxhighlight>
 
</syntaxhighlight>
== البحث عن التوابع ==
+
==البحث عن التوابع==
إذا أراد مفسر لغة روبي  البحث عن تابع لكائن من الصنف C فإنّه يسير وفق الترتيب التالي:
+
إذا أراد مفسر لغة روبي  البحث عن تابع لكائن من الصنف <code>C</code> فإنّه يسير وفق الترتيب التالي:
* إذا وُجدت تحسينات فعالة للصنف C فيتمّ البحث بعكس ترتيب تفعيلها:
+
*إذا وُجدت تحسينات فعالة للصنف <code>C</code> فيتمّ البحث بعكس ترتيب تفعيلها:
** يبدأ بالكينونات المضمّنة باستخدام prepend في تحسينات الصنف C
+
**يبدأ بالوحدات المضمّنة باستخدام [[Ruby/Module/prepend|<code>prepend</code>]] في تحسينات الصنف <code>C</code>
** ثمّ تحسينات الصنف
+
**ثمّ تحسينات الصنف
** ثم الكينونات  المضمّنة باستخدام include في تحسينات الصنف C
+
**ثم الوحدات  المضمّنة باستخدام <code>[[Ruby/Module/include|include]]</code> في تحسينات الصنف <code>C</code>
* ثم الكينونات المضمّنة في الصنف باستخدام prepend
+
*ثم الوحدات المضمّنة في الصنف باستخدام <code>[[Ruby/Module/prepend|prepend]]</code>
* ثم الصنف نفسه
+
*ثم الصنف نفسه
* ثم الكينونات المضمّنة في الصنف باستخدام include
+
*ثم الوحدات المضمّنة في الصنف باستخدام [[Ruby/Module/include|<code>include</code>]]
وإذا لم يجد المفسّر أيّ تابع مطابق في أيّ مرحلة من هذه المراحل فإنّ عملية البحث تكرّر بنفس الترتيب لكن مع الصنف الأب للصنف C.
+
وإذا لم يجد المفسّر أيّ تابع مطابق في أيّ مرحلة من هذه المراحل، فإنّ عملية البحث تكرّر بنفس الترتيب لكن مع الصنف الأب للصنف <code>C</code>.
لاحظ أنّ أولوية التوابع في الصنف الابن أعلى من التحسينات في الصنف الأب، فعلى سبيل المثال لو عرّفنا التابع / في تحسين للصنف Numeric فإنّ العملية 1/2 تستدعي التابع المقابل في الصنف Integer بسبب كون الأخير صنفًا فرعيًا عن Numeric فيسبقه في عمليّة البحث، وبما أنّ التابع موجود في الصنف الابن فسيتوقف البحث بمجرّد العثور على هذا التابع فيه.
+
 
وبالمقابل لو عرّفنا التابع foo في الصنف Numeric ضمن تحسين ما، فإنّ كتابة 1.foo ستستدعي هذا التابع من الصنف Numeric لكونه غير معرّف في الصنف Integer.
+
لاحظ أنّ أولوية التوابع في الصنف الابن أعلى من التحسينات في الصنف الأب؛ فعلى سبيل المثال، لو عرّفنا التابع <code>/</code> في تحسين للصنف <code>[[Ruby/Numeric|Numeric]]</code>، فإنّ العملية <code>1/2</code> تستدعي التابع المقابل في الصنف <code>[[Ruby/Integer|Integer]]</code> بسبب كون الأخير صنفًا فرعيًا عن <code>[[Ruby/Numeric|Numeric]]</code> فيسبقه في عمليّة البحث، وبما أنّ التابع موجود في الصنف الابن فسيتوقف البحث بمجرّد العثور على هذا التابع فيه.
== super ==
+
 
إليك الترتيب الذي يسير وفقه المفسر عند استدعاء super:
+
وبالمقابل لو عرّفنا التابع <code>foo</code> في الصنف <code>[[Ruby/Numeric|Numeric]]</code> ضمن تحسين ما، فإنّ كتابة <code>1.foo</code> ستستدعي هذا التابع من الصنف <code>[[Ruby/Numeric|Numeric]]</code> لكونه غير معرّف في الصنف <code>[[Ruby/Integer|Integer]]</code>.
* الكينونات المضمّنة في الصنف الحاليّ، مع ملاحظة أنّ الصنف الحاليّ يمكن أن يكون تحسينًا.
+
==استعمال <code>super</code>==
* في حال كان الصنف الحاليّ تحسينًا فإنّ عملية البحث تسير وفق الترتيب المذكور في الفقرة السابقة "البحث عن التوابع"
+
إليك الترتيب الذي يسير وفقه المفسر عند استدعاء <code>super</code>:
* وفق حال كان للصنف الحاليّ أب مباشر فستسير عمليّة البحث كما في الفقرة السابقة وإنّما على الصنف الأب.
+
*الوحدات المضمّنة في الصنف الحاليّ، مع ملاحظة أنّ الصنف الحاليّ يمكن أن يكون تحسينًا.
لاحظ أنّ استدعاء super في تابع ضمن تحسين صنفٍ ما سيستدعي هذا التابع من الصنف الذي يُحسَّن، حتى ولو سبق ذلك تفعيل تحسين آخر ضمن نفس السياق.
+
*في حال كان الصنف الحاليّ تحسينًا، فإنّ عملية البحث تسير وفق الترتيب المذكور في الفقرة السابقة "البحث عن التوابع".
== الاستدعاء غير المباشر للتوابع == 
+
*وفي حال كان للصنف الحاليّ أب مباشر، فستسير عمليّة البحث كما في الفقرة السابقة وإنّما على الصنف الأب.
إذا استدعي تابع بشكل غير مباشر كما هو الحال عند استخدام Kernel#send أو Kernel#method أو Kernel#respond_to? فحينها لا تُعطى التحسينات أولويّة أثناء عمليّة البحث. إلّا أنّ هذا السّلوك قد يتغيّر في المستقبل.
+
لاحظ أنّ استدعاء <code>super</code> في تابع ضمن تحسين صنف ما سيستدعي هذا التابع من الصنف الذي يُحسَّن، حتى ولو سبق ذلك تفعيل تحسين آخر ضمن نفس السياق.
== وراثة التحسينات باستخدام Module#include == 
+
==الاستدعاء غير المباشر للتوابع==
إذا ضُمّنت الكينونة X في الكينونة Y فإنّ الثانية ترث التحسينات المطبّقة في الأولى. ففي المثال التالي ترث C تحسينات من A و B:
+
إذا استدعي تابع بشكل غير مباشر كما هو الحال عند استخدام <code>[[Ruby/Kernel/send|Kernel.send]]</code> أو <code>[[Ruby/Kernel/method|Kernel.method]]</code> أو <code>[https://wiki.hsoub.com/?Ruby/Kernel/respond%20to ?Kernel.respond_to]</code>، فحينها لا تُعطى التحسينات أولويّة أثناء عمليّة البحث. إلّا أنّ هذا السّلوك قد يتغيّر في المستقبل.
<syntaxhighlight lang="ruby">
+
==وراثة التحسينات باستخدام <code>[[Ruby/Module/include|Module.include]]</code>==
 +
إذا ضُمّنت الوحدة <code>X</code> في الوحدة <code>Y</code>، فإنّ الثانية ترث التحسينات المطبّقة في الأولى. ففي المثال التالي ترث <code>C</code> تحسينات من <code>A</code> و <code>B</code>:<syntaxhighlight lang="ruby">
 
module A
 
module A
 
 refine X do ... end
 
 refine X do ... end
سطر 189: سطر 181:
 
end
 
end
 
using C
 
using C
# التحسينات من A و B فعالة هنا.
+
# فعالة هنا B و A التحسينات من
</syntaxhighlight>
+
</syntaxhighlight>وتحسينات الأبناء لها أولوية أعلى من تحسينات الآباء.
وتحسينات الأبناء لها أولوية أعلى من تحسينات الآباء.
+
==المصادر==
== مزيد من المعلومات ==
+
*[https://ruby-doc.org/core-2.5.1/doc/syntax/refinements_rdoc.html صفحة Refinements في توثيق روبي الرسمي.]<noinclude>{{DISPLAYTITLE:التحسينات في  روبي}}</noinclude>
انظر [[about:blank|صفحة التوثيق]] الخاصة بالقواعد الحاليّة لاستخدام التحسينات، قد تجد فيها المزيد من التفاصيل.
+
[[تصنيف:Ruby]]
 +
[[تصنيف:Ruby Syntax]]

المراجعة الحالية بتاريخ 06:12، 19 نوفمبر 2018

إن ميّزة الأصناف المفتوحة في لغة روبي تسمح لك بإعادة تعريف أو إضافة وظائف إلى أصناف معرّفة مسبقًا. وهذا ما يسمى بمصطلح "ترقيع القرد" (monkey patch). المشكلة هنا أنَّ تعديلات من هذا النوع تكون مرئيّة على المستوى العام (global)، وبالتالي جميع مستخدمي الصنف المرقّع قادرون على رؤية هذه التغييرات، ممّا قد يسبّب تأثيرات جانبيّة غير محسوبة أو حتى عطب في البرامج.

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

class C
 def foo
   puts "C#foo"
 end
end
module M
 refine C do
   def foo
     puts "C#foo in M"
   end
 end
end 

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

ينشئ Module.refine بعدئذٍ وحدة مجهولة (anonymous) تحوي التعديلات أو التحسينات المطبّقة على هذا الصنف (في مثالنا الصنف C). واستخدام self أو Module.module_eval سيشير إلى هذه الوحدة الجديدة.

أمّا تفعيل هذا التحسين فيكون باستخدام using:

using M
c = C.new
c.foo # "C#foo in M" تطبع

النطاق

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

كما بإمكانك تفعيل التحسينات في سلسلة أوامر ممرّرة إلى التابع Kernel.eval، وحينها تبقى فعالة حتى نهاية هذه السلسلة.

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

وحين ينتقل التحكّم إلى خارج النّطاق، يُلغى تفعيل التحسينات تلقائيًّا؛ وهذا يعني أنّك إن قمت بطلب أو تحميل ملفّ، أو أجريت استدعاءً لتابع معرّف خارج النّطاق الحاليّ، فسيلغى تفعيل التحسينات تلقائيًّا:

class C
end
module M
 refine C do
   def foo
     puts "C#foo in M"
   end
 end
end
def call_foo(x)
 x.foo
end
using M
x = C.new
x.foo       # "C#foo in M" يطبع 
call_foo(x) #=> NoMethodError يسبب

إذا عُرّف تابع في نطاق كانت فيه التحسينات مفعّلة، فستبقى هذه التحسينات على حالة تفعيلها عند استدعاء هذا التابع. إليك المثال التالي الذي يمتدّ على ملفّات عدّة: الملف c.rb:

class C
end

الملف m.rb:

require "c"

module M
  refine C do
    def foo
      puts "C#foo in M"
    end
  end
end

الملف m_user.rb:

require "m"
using M
class MUser
 def call_foo(x)
   x.foo
 end
end

الملف main.rb:

require "m_user"
x = C.new
m_user = MUser.new
m_user.call_foo(x) # "C#foo in M" يطبع
x.foo              #=> NoMethodError يسبب

بما أنّ التحسين M فعال في الملف m_user.rb حيث تعريف التابع MUser.call_foo، فسيبقى هذا التحسين فعالًا عندما يُستدعى التابع call_foo في الملف main.rb.

وبما أنّ using هو تابع، فستفعّل التحسينات فقط عند استدعائه، وإليك أمثلة حيث يكون التحسين M مفعلًا أو غير مفعّل.

في ملف:

# غير فعال هنا
using M
# فعال هنا
class Foo
 # فعال هنا
 def foo
   # فعال هنا
 end
 # فعال هنا
end
# فعال هنا

في صنف:

# غير فعال هنا
class Foo
 # غير فعال هنا
 def foo
   # غير فعال هنا
 end
 using M
 # فعال هنا
 def bar
   # فعال هنا
 end
 # فعال هنا
end
# غير فعال هنا

لاحظ أنّ التحسينات M لا تفعّل تلقائيًّا في حال أُعيد فتح الصنف لاحقًا. في eval:

# غير فعال هنا
eval <<EOF
 # غير فعال هنا
 using M
 # فعال هنا
EOF
# غير فعال هنا

عند عدم تحقّق التنفيذ:

# غير فعال هنا
if false
 using M
end
# غير فعال هنا

عند تعريف عدة تحسينات في نفس الوحدة داخل عدّة كتل برمجيّة محسِّنة، فكلّ التحسينات في هذه الوحدة تصبح فعالة عند استدعاء تابع محسَّن (أيّ من توابع to_json في المثال أدناه) :

module ToJSON
 refine Integer do
   def to_json
     to_s
   end
 end
 refine Array do
   def to_json
     "[" + map { |i| i.to_json }.join(",") + "]"
   end
 end
 refine Hash do
   def to_json
     "{" + map { |k, v| k.to_s.dump + ":" + v.to_json }.join(",") + "}"
   end
 end
end
using ToJSON
p [{1=>2}, {3=>4}].to_json # "[{\"1\":2},{\"3\":4}]" تطبع

البحث عن التوابع

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

  • إذا وُجدت تحسينات فعالة للصنف C فيتمّ البحث بعكس ترتيب تفعيلها:
    • يبدأ بالوحدات المضمّنة باستخدام prepend في تحسينات الصنف C
    • ثمّ تحسينات الصنف
    • ثم الوحدات  المضمّنة باستخدام include في تحسينات الصنف C
  • ثم الوحدات المضمّنة في الصنف باستخدام prepend
  • ثم الصنف نفسه
  • ثم الوحدات المضمّنة في الصنف باستخدام include

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

لاحظ أنّ أولوية التوابع في الصنف الابن أعلى من التحسينات في الصنف الأب؛ فعلى سبيل المثال، لو عرّفنا التابع / في تحسين للصنف Numeric، فإنّ العملية 1/2 تستدعي التابع المقابل في الصنف Integer بسبب كون الأخير صنفًا فرعيًا عن Numeric فيسبقه في عمليّة البحث، وبما أنّ التابع موجود في الصنف الابن فسيتوقف البحث بمجرّد العثور على هذا التابع فيه.

وبالمقابل لو عرّفنا التابع foo في الصنف Numeric ضمن تحسين ما، فإنّ كتابة 1.foo ستستدعي هذا التابع من الصنف Numeric لكونه غير معرّف في الصنف Integer.

استعمال super

إليك الترتيب الذي يسير وفقه المفسر عند استدعاء super:

  • الوحدات المضمّنة في الصنف الحاليّ، مع ملاحظة أنّ الصنف الحاليّ يمكن أن يكون تحسينًا.
  • في حال كان الصنف الحاليّ تحسينًا، فإنّ عملية البحث تسير وفق الترتيب المذكور في الفقرة السابقة "البحث عن التوابع".
  • وفي حال كان للصنف الحاليّ أب مباشر، فستسير عمليّة البحث كما في الفقرة السابقة وإنّما على الصنف الأب.

لاحظ أنّ استدعاء super في تابع ضمن تحسين صنف ما سيستدعي هذا التابع من الصنف الذي يُحسَّن، حتى ولو سبق ذلك تفعيل تحسين آخر ضمن نفس السياق.

الاستدعاء غير المباشر للتوابع

إذا استدعي تابع بشكل غير مباشر كما هو الحال عند استخدام Kernel.send أو Kernel.method أو ?Kernel.respond_to، فحينها لا تُعطى التحسينات أولويّة أثناء عمليّة البحث. إلّا أنّ هذا السّلوك قد يتغيّر في المستقبل.

وراثة التحسينات باستخدام Module.include

إذا ضُمّنت الوحدة X في الوحدة Y، فإنّ الثانية ترث التحسينات المطبّقة في الأولى. ففي المثال التالي ترث C تحسينات من A و B:

module A
 refine X do ... end
 refine Y do ... end
end
module B
 refine Z do ... end
end
module C
 include A
 include B
end
using C
# فعالة هنا B و A التحسينات من

وتحسينات الأبناء لها أولوية أعلى من تحسينات الآباء.

المصادر