---
title: "التعامل مع gems قديمة ما زلتم بحاجة إليها"
description: "عندما تعملون على نظام تطوّر على مدى سنوات، ستواجهون في النهاية تحديًا: كيف تحلّون التبعيات بين الأجزاء التي جمعتموها معًا؟ قد تحتاجون أحيانًا إلى استخدام gem اثنين يعتمدان على إصدارين مختلفين من gem ثالث، فترون في الـ console مثلًا foo -> require"
canonical: https://oozou.com/ar/blog/dealing-with-legacy-gems-you-still-need-to-use-94
locale: ar
date: 2020-08-28
author: "OOZOU Team"
---

# التعامل مع gems قديمة ما زلتم بحاجة إليها

> عندما تعملون على نظام تطوّر على مدى سنوات، ستواجهون في النهاية تحديًا: كيف تحلّون التبعيات بين الأجزاء التي جمعتموها معًا؟ قد تحتاجون أحيانًا إلى استخدام gem اثنين يعتمدان على إصدارين مختلفين من gem ثالث، فترون في الـ console مثلًا foo -> require

Published: 2020-08-28 · Author: OOZOU Team (Engineering) · 5 min read

# هذا المشروع يحتاج إلى من يتولى صيانته
بين حين وآخر تستقبلكم هذه الرسالة في صفحة مكتبة على GitHub. تزورونها لتعرفوا لماذا لا يسمح لكم _bundle_ بتحديث أحد الـ gems الذي تعتمد عليه ميزة جديدة. وتتفاجؤون بأن المستودع لم يتغير فيه شيء منذ فترة طويلة. وهنا تقعون في حيرة: هل تستبدلون المكتبة المهجورة وتتخلصون منها لحل المشكلة؟ (هذا إن كان ذلك ممكنًا أصلًا). أم تبحثون عن حل لا يتطلب حزمة مقفلة في الـ Gemfile؟

```
Bundler could not find compatible versions for gem "foobar"
<em>~ No one wants to see this in their console</em>
```

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

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

وكما سترون لاحقًا، سنميّز نسختي المكتبة باسم معدّل. لذلك سنحتاج، ضمن خطوات التنفيذ، إلى إعادة كتابة الاستدعاءات الموجهة إلى الـ gem المستورد، أو عمل monkey patch لها. ويمكننا أن نختار أي إصدار نستورده وأي إصدار نتركه لـ bundle، وهناك بعض الأمور التي يجب مراعاتها. أولًا، من المنطقي على الأرجح تفضيل استيراد الإصدار الأقدم ليتمكن bundle من تحديث الـ gem الأحدث. لكن إذا كان الإصدار الجديد اعتمادية لـ gem واحد فقط، بينما يعتمد 10 gems على الإصدار القديم، فقد يكون من الأفضل استيراد الإصدار الجديد لتوفير الوقت والجهد الذي سيذهب في الـ monkey patching.

# حسنًا، اتخذت القرار، فأين أضع الكود؟
كحال أمور كثيرة في عالم Rails، هنا أيضًا عُرف متّبع. وبحسب مدى تحفّظكم، ضعوا الكود الخارجي إما في _/app/lib/external_ أو في _/lib/external_. المحتوى الموجود داخل مجلد app يُحمَّل تلقائيًا (autoload)، فتذكروا فقط حذف جميع عبارات _require_ لأنكم لم تعودوا بحاجة إليها.

أما في الخيار الثاني فتصبح الأمور أعقد قليلًا. أولًا، عليكم التأكد من تحويل جميع عبارات _require_ إلى _require_relative._ ثم، لبدء التحميل، أنشئوا الملف _extensions.rb_ في مجلد initializers. يضمن ذلك تحميل جميع المكتبات الخارجية المطلوبة قبل أن نستدعي أي شيء منها.

```ruby
// extensions.rb

Dir[<strong>"</strong>#{Rails.root}<strong>/lib/external/foobar221/*.rb"</strong>]
  .each { |<em>file</em>| require <em>file </em>}
```

ولتجنب تعارض الـ namespace مع إصدار آخر من الـ gem موجود في الـ bundle، علينا الآن تغيير أسماء الـ modules والأصناف. فإذا كان الـ gem المعني يسمى _Foobar_ مثلًا، أضيفوا رقم إصداره في نهاية الاسم. ومن هذه اللحظة، كلما أردتم استخدام نسخة من الكود الذي أضفتموه، عليكم الإشارة إليه باسم _Foobar221._

# عمل monkey patch للـ gems المعتمدة عليه
إعادة كتابة كود الـ gems ليست أشرف ما يمكن فعله، وأفترض أنكم إن اخترتم هذا الطريق فلم يكن أمامكم خيار آخر. بعد أن تحددوا جميع المواضع التي تحتاج إلى تعديل، ضعوا كل الكود المتعلق بها في _/app/initializers/**baz**_patch.rb._ وقد تبدو بنية الملف على هذا النحو:

```
//**baz**_patch.rb 

<em># describe why you are doing it, 
# so your colleagues will 
# hate you a bit less</em> 

<em>***::Baz::***.class_eval do 
  def fetch_data   
    @data ||= Foobar221.call(...)   
    ... 
  end
  ...
end
language-ruby</em>
```

# اجعلوه ملفتًا للانتباه
ما اقترحناه أعلاه قد يتحول إلى حل دائم. وإذا عززناه باختبارات مناسبة وتعليقات واضحة، يمكننا تجنب موقف مقلق: أن يعترض أحدهم طريقة تنفيذنا دون قصد، إما باستخدام إصدار خاطئ من الـ gem، أو بحذف الـ _patch_ لأنه يبدو غير مستخدم_._ ولمنع الأمرين، ينبغي أن نجعله واضحًا للعيان.

لا تترددوا في توثيق قراراتكم داخل الـ Gemfile. أخبروا المطوّرين الآخرين في تعليق أنهم سيجدون إصدارًا آخر من هذا الـ gem في التطبيق، واشرحوا السبب. وينطبق الأمر نفسه على الملف _*_patch.rb_ الذي يجمع كل الدوال المعاد كتابتها.

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

إليكم مثالًا على كيفية القيام بذلك:

```
//**baz**_patch_spec.rb

require 'rails_helper' 

RSpec.describe **::Baz::** <strong>do  

  </strong>before <strong>do    
    </strong>...method stubbing..  
  <strong>end</strong> <strong>  

  </strong>describe '.fetch_data' <strong>do    
    </strong>subject { described_class.new.fetch_data }     

    it 'uses Foobar221 library when fetching data' <strong>do  
      </strong>expect(::Foobar221).to receive(:call)      
      subject    
    <strong>end  
  end
end
language-ruby</strong>
```

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

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
