---
title: "فصل منطق العرض في Ruby on Rails باستخدام Presenters"
description: "حشو نماذج ActiveRecord بدوال مخصصة للعرض ممارسة سيئة وعلامة على مشكلة في الكود. تضيف الـ Presenters طبقة تجريد بين النماذج والواجهات لتساعدنا على بناء تطبيق أسهل في الصيانة."
canonical: https://oozou.com/ar/blog/abstracting-ruby-on-rails-view-logic-with-presenters-51
locale: ar
date: 2020-07-19
author: "OOZOU Team"
---

# فصل منطق العرض في Ruby on Rails باستخدام Presenters

> حشو نماذج ActiveRecord بدوال مخصصة للعرض ممارسة سيئة وعلامة على مشكلة في الكود. تضيف الـ Presenters طبقة تجريد بين النماذج والواجهات لتساعدنا على بناء تطبيق أسهل في الصيانة.

Published: 2020-07-19 · Author: OOZOU Team (Engineering) · 5 min read

بصفتكم مطوّري Rails، ستجدون كثيرًا نماذج (models) تحتوي على دوال يبدو أن كل ما تفعله هو بعض التنسيق. تُستخدم هذه الدوال عادة في الواجهات (views) لعرض البيانات بصيغة مألوفة أكثر للمستخدمين النهائيين.
من المغري جدًا تعريف دوال التنسيق والدوال المنطقية (boolean) هذه داخل النموذج، لأننا اعتدنا على فكرة أن النماذج هي ما يُعرض في الواجهات.
لكن إذا توقفنا وتأملنا دور كل كائن في تطبيقنا، فسنصل بسهولة إلى أن تعريف هذه الدوال يخلط بين المسؤوليات. ومع أنني لا أرى أن خلط المسؤوليات يجب أن يُمنع دائمًا، فإنني أعتقد أن حل هذه المشكلة سهل، ولذلك يستحق العناء.
الـ Presenters تجريد بسيط يحل مشكلتنا بجهد قليل وفائدة كبيرة. فهي تجعل تطبيقاتنا أسهل كثيرًا في الصيانة عبر تقليل المسؤوليات، وهذا بدوره يقلل الفوضى ويعزل المنطق الذي يخص جزءًا صغيرًا واحدًا فقط من التطبيق ككل.

# ما هو الـ Presenter؟

الـ Presenter كائن يقع بين النموذج والواجهة ليغلّف منطق التنسيق وغيره من منطق العرض المعقد.
على سبيل المثال، إذا كان لدينا BankCard ونريد إخفاء جزء من الرقم، يمكننا إنشاء BankCardPresenter وتعريف دالة masked_card_number

```ruby
class BankCardPresenter
  def initialize(bank_card)
    @bank_card = bank_card
  end

  def masked_card_number
    "**-**-**-#{@bank_card.card_number[-4..-1]}"
  end

  def valid?
    Time.current.before?(@bank_card.expiration)
  end

  def bank_name
    return 'KBank' if @bank_card.bank_name == 'Kasikorn'

    @bank_card.bank_name
  end
end
BankCardPresenter.new(BankCard.first).masked_card_number
# => **-**-**-4242
```

أو ربما نريد عرض رمز QR لنظام الدفع لدينا. يمكننا إنشاء TransactionPresenter وتعريف دالة 'qr_code'

```ruby
class TransactionPresenter
  def initialize(transaction)
    @transaction = transaction
  end

  def qr_code
    QRCode.new(transaction.transaction_identifier)
  end
end
```

مع الوقت، تبدأ هذه الدوال في إثقال نماذجنا. لا يمثل ذلك مشكلة عادة إلى أن تصبح لديكم مئات الدوال في نماذجكم العملاقة (god models). على الأرجح نحتاج إلى qr_code في واجهة واحدة فقط، وحتى لو احتجنا إليها في كل الواجهات، فلن نحتاج إليها في النماذج أو في المهام الخلفية (background jobs) وغيرها. يجب أن يُصمَّم منطق التطبيق ليعمل مع البيانات كما تأتينا مباشرة من قاعدة البيانات. ولا نحتاج إلى التنسيق إلا عند عرض البيانات للمستخدمين النهائيين، لتظهر بصيغة مألوفة يسهل استيعابها.

# هل يمكننا تحسين ذلك؟

تقدم Ruby صنفًا جميلًا يساعدنا على إنشاء الـ Presenters، وهو SimpleDelegator! وربما تكون الـ Presenters من أنسب حالات استخدام SimpleDelegator.
SimpleDelegator صنف أساسي في Ruby يتيح لنا تفويض جميع الدوال غير المعرّفة في الكائن الحالي إلى الكائن المغلَّف. ولتغليف كائن، يكفي أن نمرره عند الإنشاء.
يتيح لنا ذلك إنشاء Presenters لا تحتاج إلى منطق إنشاء مخصص، ويمكننا التعامل مع الـ Presenter كأنه الكائن المعروض نفسه، فنستدعي transaction_identifier على الـ Presenter مباشرة بدلًا من الكائن @transaction. وهذا يجعل الـ Presenters أنظف وأبسط.

```ruby
class BankCardPresenter < SimpleDelegator
  def masked_card_number
    "**-**-**-#{card_number[-4..-1]}"
  end
end
BankCardPresenter.new(BankCard.first).masked_card_number
# => **-**-**-4242
```

قد ترغبون أحيانًا في تجاوز (override) دالة موجودة في الكائن المغلَّف، ويتيح لنا SimpleDelegator استدعاء super للوصول إلى الكائن المعروض، مثلًا:

```ruby
class BankCardPresenter < SimpleDelegator
  def card_number
    "**-**-**-#{super[-4..-1]}"
  end
end
BankCardPresenter.new(BankCard.first).card_number
# => **-**-**-4242
```

# متى يجب استخدام الـ Presenter؟

شخصيًا أستخدمها في الواجهات والـ serializers حين يتطلب الموقف بعض التنسيق للخصائص. وكلما احتجت إلى خاصية غير موجودة أصلًا في النموذج، أنشئ Presenter.
تنظيم الـ Presenters أصعب قليلًا، وهو أمر يجب الانتباه إليه، وستحتاجون إلى التجربة. على سبيل المثال، إذا أنشأتم Presenter باسم UserRegistrationPresenter واستخدمتموه في 4 واجهات مختلفة، ثم احتاجت واجهة واحدة منها إلى خاصية إضافية، فيمكنكم إما إضافة تلك الخاصية رغم أنها تخص واجهة واحدة من أربع، أو إنشاء Presenter جديد بالكامل.
يمكنكم بالطبع إنشاء أصناف فرعية منها لمشاركة الدوال، أو حتى استخدام concerns أو وحدات PORO أو أساليب أخرى للمشاركة بين الـ Presenters المختلفة. ومن تجربتي، لن تحتاجوا في أغلب الأحيان إلى عدد كبير من الـ Presenters لإنجاز المهمة. أميل إلى إضافة تلك الدالة الإضافية فحسب، حتى لو كانت تخص بضع واجهات فقط. فهذا يظل تحسنًا كبيرًا مقارنة بحشر هذه الدوال في النماذج.
من الأمور اللطيفة في الـ Presenters أنها تعرض الكائن الممرَّر إليها، لذا يمكنكم نظريًا تمرير Presenter إلى Presenter آخر. لا أنصح بذلك بالضرورة، لأنني متأكد من أنه سيصبح من الصعب تتبع وجهة الاستدعاءات بدقة بعد مستوى معين من التداخل.
وكالعادة، القرار لكم. تمنحنا Ruby قدرًا هائلًا من المرونة والقوة، فاستخدموها بمسؤولية! الـ Presenters مجرد أداة إضافية نستخدمها لبناء تطبيقات سهلة الصيانة، وأرجو أن تجدوها مفيدة كما أجدها أنا.

---

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"
