المحتوى عن 'rails'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • مقالات عامة
  • التجارة الإلكترونية

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML5
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات عامّة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • مقالات عامّة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 17 نتائج

  1. يحتار المطورون في اختيار أفضل إطار لمشاريعهم وسيكون هذا تحديًا حقيقيًا للمبتدئين في الأطر الحديثة. بعد العمل على الأطر الثلاثة (Django، Laravel و Rails – والذي يُعرف باسم Ruby On -rails)، سأقارن بين هذه الأطر الرائعة على أساس شعارها، سهولة تعلمها، أدائها، قوة وضعف مكتباتها وقوالبها، دعمها، آفاقها المستقبلية، فرص العمل، التكلفة والصيانة. ملاحظة: ينتقد بعض المعجبين عند التحدث عن نقاط ضعف أطرهم، ولا أستطيع فعل أي شيء لأنه لا يمكن إخفاء الحقيقة، كل إطار لديه بعض المزايا مع بعض العيوب. المقدمة لغة البرمجة أهم فرق بين هذه الأطر هي أن Django بلغة بايثون، Laravel بلغة PHP وRails بلغة الروبي، لذا إذا كنت تنوي استخدام أي من هذه الأطر فيجب عليك تعلم لغتها أولاً، وبسبب هذا، العديد من المطورين يختارون الإطار الذي يتطابق مع اللغة التي يعرفونها. إن التحول من لغة إلى أخرى ليس صعبًا بل يحتاج إلى بعض الوقت، وإذا احترت في اختيار لغة البرمجة، فهذه مقارنة بين لغات بايثون و PHP وروبي. الشعار جميع هذه الأطر من نوع MVC وشعارها ‘لا تكرر نفسك’ أي تدعم إعادة الاستخدام وقابلية النقل، وجميعها مشاريع مجانية ومفتوحة المصدر. المواقع بعض المواقع المعروفة تستخدم Django مثل Pinterest، Instagram، Mozilla، The Washington Times، Disqus، the Public Broadcasting Service و Bitbucket. في حين أن Laravel هو إطار جديد، حيث صدر في يونيو عام 2011، لكنه أصبح مشهورا جدا، ومن بين المواقع التي تستخدمه هي Deltanet Travel، Sublimity، Neighborhood Lender، Sendity و MyRank. يعتبر Rails من الأطر الرائعة فمن المواقع التي تستخدمه Twitter، Shopify، SoundCloud، Heroku، Github، Bloomberg و Hulu. سهولة التعلم على الرغم من أن الأطر الثلاثة لديها مجتمعات كبيرة وتوثيق رسمي، إلا أن تعلم Django وLaravel أسهل بكثير من تعلم Rails، فالتوثيق الحالي ل Django يجعلها الأسهل، وإذا كنت تملك خلفية PHP فيمكنك تعلم Laravel في غضون أسبوعين أو ثلاثة أسابيع، وهذه هي الوثائق الرسمية: وثائق Django ووثائق Laravel وثائق Rails. الأداء الأمن جميع هذه الأطر آمنة جدا إذا لم يرتكب المبرمج أخطاء، فيمتلك Django برمجيات وسيطة ويمتلك Rails Active Records وأما Laravel فيمتلك برمجيات HTTP وسيطة، وتوفر كل هذه الأطر رموز csrf للنماذج. لا يوجد فرق أمني كبير بين هذه الأطر، وكل هذا يعتمد على خبرة المبرمج. تحديث:أشار بعض القراء أن المبرمجين هم بشر وسيخطئون، لذا سأقول في هذه الحالة أن Django هو الأكثر أمانا وLaravel هو الأقل أمانًا، اطلع على هذا التوثيق عن أمن Django وهذا دليل أمن Rails و هذا دليل أمن Laravel، وسأقول أيضا أنه لا يوجد إطار آمن بشكل كامل لأن المطورين هم أيضا بشر، ويمكنك زيادة الأمن لكنك لا تستطيع جعله آمن بنسبة 100%، لكن إذا كتبت التعليمات البرمجية بعناية وحذر فإن جميع الأطر متساوية من ناحية الأمن. السرعة جميع الأطر مكتوبة بشكل صحيح، لذلك سرعتها تعتمد على اللغة البرمجة المستخدمة، فDjango هو الأسرع بسبب البايثون و Laravel هي الأبطأ بسبب PHP. الوقت المطلوب لإنشاء تطبيق إذا كنت تفهم الإطار بشكل كامل فإن إنشاء تطبيق Rails هو الأسرع لأنه يوفر لك الكثير من الاختصارات وبهذا ستكتب أقل عدد من الأسطر البرمجية. ومن جهة أخرى، Laravel هو الأبطأ ولا يوفر مكتبة قوية. إذا كان المشروع معقد فإن الفرق الزمني بين تطبيقات Django وRails سيكون صغيرًا بسبب صياغة بايثون المريحة للمتابعة وأقل أرباك، أما بالنسبة لـ Laravel فيجب عليك كتابة الكثير من الأسطر البرمجية وهذا قد يسبب لك بعض الإرباك وسترتفع نسبة الأخطاء. قوة وضعف المكتبة الأشياء المشتركة في جميع الأطر: جميعها MVC (يسمى Django MTV أيضا لكن على الرغم من أن الاسم مختلف إلا أن المفهوم هو نفسه). تركز جميع الأطر على قابلية القراءة وبساطة الشيفرة البرمجية وتوزيع الملفات. جميعها تستعلم تلقائيًا من قاعدة البيانات، فلا يجب عليك كتابة استعلامات قاعدة البيانات بشكل مباشر. تبنى الجداول تلقائيا في قاعدة البيانات من النماذج (models). جميع الأطر تملك نظام توجيه سهل وآمن، وتعرض صفحات الويب بشكل حيوي. تملك جميعها أنظمة قوالب خاصة بها وكل نظام قوالب غني بالمرشحات والدوال المعرّفة مسبقًا، الفرق الوحيد في الصياغة. جميعها مرنة ومحمولة مع تقنيات حديثة أخرى. Django يمتلك Django مكتبة قوية مع المميزات التالية: يعتبر قسم الإدارة المدمجة، المزخرف (decorator)، وأصناف المناظر نقاط قوة ل Django. الاستمارات المولدة تلقائيا للنماذج مع عملية التحقيق تجعلها سهلة للغاية. يدعم الإطار خاصية التخزين المؤقت وستتمكن من استخدام أي من أساليب التخزين المؤقت المتاحة. يدعم الأصناف البرمجيات الوسطيّة والتي يمكن أن تتدخّل في مراحل مختلفة من معالجة الطلب وتُنفّذ دوال مخصصة. يسمح لك نظام مرسل (dispatcher) داخلي لمكونات التطبيق اتصال الأحداث مع بعضها البعض عبر إشارات محددة مسبقا. يملك نظام تدويل يتضمن ترجمات لمكونات Django إلى لغات مختلفة. يملك نظام تسلسل الذي يمكنك من إنتاج وقراءة تمثيل XML و/أو JSON لمثيلات نموذج Django. واجهة بايثون مدمجة في إطار اختبار الوحدة. نظام مصادقة (authentication) موسّع. واجهة إدارة حيوية. أدوات لتوليد RSS وتغذيات (feed) خلاصات Atom. إطار مواقع تسمح ل Django واحد بتشغيل مواقع متعددة، ولكل منها المحتوى والتطبيقات الخاصة به. يملك أدوات لتوليد Google Sitemap. يملك تقنيات مدمجة للتخفيف من التزوير عبر الموقع، ثغرات XSS، ثغرات حقن SQL، تكسير كلمات المرور وهجمات الويب النموذجية، ومعظمها يعمل افتراضيا. إطار لإنشاء تطبيقات GIS. Laravel على الرغم من أن مكتبات Laravel ليست قوية مثل Django وRails إلا أنها كافية لإنشاء أي نوع من المواقع. يوفر Bundles و composer عدد من حزم نظام وحدات التحزيم والاعتماديات. التوجيه (Routing) – يوّفر طريقة سهلة وبسيطة لإدارة وتوجيه الروابط إلى متحكم أو دالة تُنفَّذ عند زيارة رابط محدَّد. دعم Eloquent ORM – خدمة أخرى مقدمة لتجريد وأتمتة جزء النموذج، حيث سنطبق التقنيات المتعارف عليها على الإعدادات. التهجيرات – طريقة لإصدار سكربتات قواعد البيانات بطريقة أنيقة للغاية، فلا حاجة للحفاظ على جميع التحققات على التهجيرات، يمكن لفريق عمل المشروع سحب الهجرة المقدمة وستعيّن جميعها وستكون جاهزة للعمل. إدارة قائمة الانتظار (Queue management) – لتجريد المهام غير الضرورية ووضعهم في قائمة الانتظار وجعل وقت استجابة المستخدم أسرع بكثير. دعم Redis، ويمكن توسيعها إلى memcached. حقن الإعتماديّة – اختبار سهل وأتمتة تحميل الإعتماديّة. Artisan – لإنشاء تطبيقات سطر الأوامر في لحظة. تعلم استخدام Laravel عن طريق هذه الدروس. Rails يتضمن Rails أدوات لجعل مهام التطوير الشائعة أسهل (خارج الصندوق)، مثل scaffolding الذي يستطيع إنشاء بعض النماذج تلقائيًا والمناظر اللازمة لموقع ويب الأساسي، بالإضافة إلى WEBrick وهو خادم ويب روبي بسيط الموزع مع روبي و Rake والذي هو نظام بناء موزع كـ gem. وتوفر هذه الأدوات جنبا إلى جنب مع Rails بيئة تطوير أساسية. Active record: يلعب دورا رئيسيا في تطبيقات Rails، وهو أفضل من Eloquent ORM في Laravel ومن النماذج في Django. اختصارات: يعبر الكثير من الناس الذين يأتون من لغات برمجة أو إطارات أخرى أن هذا الإطار سحري بسبب الاختصارات الكثيرة، فأغلب الأشياء معرّفة مسبقًا ويجب عليك كتابة بعض الأسطر البرمجية لإنشاء تطبيقات معقدة. التوجيه التلقائي: بعض الدوال الشائع في جدول قاعدة البيانات مثل الإنشاء ، التعديل والعرض مُعرّفة تلقائيًا، وهذا يعني أننا لا نحتاج إلى تضييع الوقت في المهام البسيطة ويمكننا قضاء وقت أطول على الأجزاء المعقدة من المشروع. سطر الأوامر: الكثير من الأشياء يمكن إنجازها عن طريق سطر الأوامر مثل استخدام rake وهي Ruby Make، أداة روبي مستقلة تستبدل أداة يونكس 'make' وتستخدم 'Rakefile' وملفات .rake لبناء قائمة مهام. في Rails، يُستخدم Rake لمهام الإدارة الشائعة، خاصة المعقدة منها التي تبني من بعضها البعض. تحتوي وحدة ActiveModelHelper على أساليب المساعدة لإنشاء النماذج من الكائنات بسرعة التي تتبع اتفاقيات Active Model، بداية من Active Record. خدمات الاستضافة يمكنك تشغيل أي تطبيق على VPS أو على خدمة استضافة مخصصة، وهذه مجموعة من الروابط لمواقع تسمح لك باستضافة مشروعك مجانا أو على خطط الاستضافة المشتركة. Django: بعض من المواقع التي تستضيف مشاريع Django هي: WebFaction، PythonAnywhere ، Heroku ، Digital Ocean ، Bulehost ، Dreamhost ، Arvixe و Google App Engine. Laravel: يمكنك الاستضافة على Heroku ، Bulehost ، Inmotion Hosting ، Site5 ، Dreamhost ، Digital Ocean و Arvixe. Rails: مواقع لتطبيقات Rails هي: Heroku ، Bulehost ، Dreamhost ، Arvixe ، Hosting24 و Digital Ocean. معايير أخرى كل هذه الأطر جيّدة في المستقبل، ففرص العمل، التكلفة والصيانة هي تقريبا نفسها ويمتاز Rails على Django وLaravel في شروط العمل، على الرغم من سرعة نمو Laravel. خاتمة يمكنك أن تختار أي واحدة من هذه الأطر حسب لغة البرمجة والخبرة، وإذا كنت هنا لتقرر أي واحدة يجب عليك تعلمها فأنا أفضل Rails، فعلى الرغم من صعوبة تعلمها إلا أنها مريحة أثناء إنشاء التطبيقات، إذا أردت أشياء سهلة مع الكثير من المميزات فاختر Django، فصياغة بايثون ونماذجه تجعله خيار جيدا، وعلى الرغم من أن تعلم Django قد يستغرق بعض الوقت إلا أنه ليس أصعب من Rails.إذا كانت لدي خبرة في PHP أو إذا أردت التعلم بسرعة فاختر Laravel. ترجمة -وبتصرّف- للمقال Django vs Laravel vs Rails لصاحبه Harish Kumar
  2. سنتعرّف في سلسلة الدروس هذه والتي تبدأ بهذا الدرس على ارتباطات Active Record في روبي أند ريلز وسنبدأ أولًا بمعرفة لماذا الارتباطات: 1 لماذا الارتباطات Associations؟ في Rails، الارتباط association هو اتصال بين نموذجي Active Record. لماذا نحتاج إلى هذه الارتباطات بين النماذج؟ وذلك لأن العمليات المشتركة ستصبح أبسط وأسهل في الشيفرة البرمجية، على سبيل المثال، اعتبر أن تطبيق Rails بسيط يتضمن نموذج للمؤلفين ونموذج للكتب، كل مؤلف قد يملك العديد من الكتب، وبدون الارتباطات، سيشبه إعلان النموذج هذا: class Author < ApplicationRecord end class Book < ApplicationRecord end الآن، لنفترض أننا نريد إضافة كتاب جديد للمؤلف الحالي: @book = Book.create(published_at: Time.now, author_id: @author.id) أو حذف المؤلف مع جميع كتبه: @books = Book.where(author_id: @author.id) @books.each do |book| book.destroy end @author.destroy باستخدام ارتباطات Active Record، يمكننا تبسيط هذه العملية (وغيرها) عن طريق إعلان لـ Rails نخبره بوجود اتصال بين النموذجين، وهذه الشيفرة البرمجية معدلة لإعداد المؤلفين والكتب: class Author < ApplicationRecord has_many :books, dependent: :destroy end class Book < ApplicationRecord belongs_to :author end وبهذا، ستصبح عملية إنشاء كتاب لمؤلف معين أسهل: @book = @author.books.create(published_at: Time.now) بالإضافة إلى أن عملية حذف المؤلف مع جميع كتبه أسهل بكثير: @author.destroy لمزيد من المعلومات حول الأنواع المختلفة للارتباطات، اقرأ القسم التالي من هذا الدليل، وستجد بعدها بعض النصائح والحيل للعمل مع الارتباطات، ومن ثم مرجع كامل للنماذج وخيارات الارتباطات في Rails. 2 أنواع الارتباطات Associations يدعم Rails ستة أنواع من الارتباطات Associations: belongs_to has_one has_many has_many :through has_one :through has_and_belongs_to_many تنفّذ الارتباطات Association باستخدام نداءات نمط ماكرو (macro-style) بحيث يمكنك إضافة ميزات إلى نماذج، فعلى سبيل المثال، من خلال إعلان أن نموذج ينتمي belongs_to إلى آخر، ستجعل Rails يحافظ على بيانات المفتاح الرئيسي والمفتاح الخارجي (Primary Key-Foreign Key) بين مثيلات النموذجين، ويمكنك الحصول على عدد الأساليب المضافة إلى نموذجك. في ما تبقى من هذا الدليل، ستتعلم كيف تُعلن وتستخدم أشكال مختلفة من الارتباطات، لكن أولا، مقدمة قصيرة إلى الحالات التي تتناسب معها كل نوع من الارتباطات. 2.1 ارتباط belongs_to يعيّن ارتباط belongs_to اتصال واحد لواحد (one-to-one) مع نموذج آخر، بحيث أن كل مثيل للنموذج المعلن “يرتبط” بمثيل واحد للنموذج الآخر، فعلى سبيل المثال، إذا كان التطبيق يتضمن مؤلفين وكتب وكل كتاب يرتبط فقط إلى مؤلف واحد، يمكنك إعلان نموذج الكتاب كالتالي: class Book < ApplicationRecord belongs_to :author end عملية التهجير ستشبه هذه: class CreateBooks < ActiveRecord::Migration[5.0] def change create_table :authors do |t| t.string :name t.timestamps end create_table :books do |t| t.belongs_to :author, index: true t.datetime :published_at t.timestamps end end end 2.2 ارتباط has_one يعيّن ارتباط has_one أيضا اتصال واحد لواحد (one-to-one) مع نموذج آخر، لكن مع دلالات (semantics) -وعواقب- مختلفة، فيشير هذا الارتباط أن كل مثيل للنموذج يحتوي أو يمتلك مثيل لنموذج آخر فعلى سبيل المثال، إذا يملك كل مُورّد في تطبيقك حساب واحد فقط، سيشبه إعلان نموذج المورد هذا: class Supplier < ApplicationRecord has_one :account end عملية التهجير ستشبه هذه: class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name t.timestamps end create_table :accounts do |t| t.belongs_to :supplier, index: true t.string :account_number t.timestamps end end end بناءََ على حالة الاستخدام، ستحتاج إلى إنشاء فهرس فريد و/أو إلى قيد مفتاح خارجي (foreign key) على عمود المُورّد لجدول الحسابات، في هذه الحالة، سيشبه تعريف العمود لهذا: create_table :accounts do |t| t.belongs_to :supplier, index: { unique: true }, foreign_key: true # ... end 2.3 ارتباط has_many يشير ارتباط has_many إلى اتصال واحد إلى الكثير (one-to-many) مع نموذج آخر، ستجد في الكثير من الأحيان هذا الارتباط في الجانب الآخر لارتباط belongs_to، وهذا الارتباط يشير إلى أن كل مثيل للنموذج يملك صفر مثيل أو أكثر للنموذج الآخر، فعلى سبيل المثال، في التطبيق الذي يحتوي على المؤلفين وكتب، يمكنك الإعلان عن نموذج المؤلف كالتالي: class Author < ApplicationRecord has_many :books end عملية التهجير ستشبه هذه: class CreateAuthors < ActiveRecord::Migration[5.0] def change create_table :authors do |t| t.string :name t.timestamps end create_table :books do |t| t.belongs_to :author, index: true t.datetime :published_at t.timestamps end end end 2.4 ارتباط has_many :through يُستخدم ارتباط has_many :through لإنشاء اتصال كثير إلى كثير(many-to-many) مع نموذج آخر، فهذا النموذج يشير إلى أن النموذج المُعلن يمكن أن يقابل صفر مثيل أو أكثر من نموذج آخر من خلال نموذج ثالث، فعلى سبيل المثال، فكر في العمل الطبي حيث يحدد المرضى مواعيد لرؤية الأطباء، فسيُعلن الارتباط كالتالي: class Physician < ApplicationRecord has_many :appointments has_many :patients, through: :appointments end class Appointment < ApplicationRecord belongs_to :physician belongs_to :patient end class Patient < ApplicationRecord has_many :appointments has_many :physicians, through: :appointments end عملية التهجير ستشبه هذه: class CreateAppointments < ActiveRecord::Migration[5.0] def change create_table :physicians do |t| t.string :name t.timestamps end create_table :patients do |t| t.string :name t.timestamps end create_table :appointments do |t| t.belongs_to :physician, index: true t.belongs_to :patient, index: true t.datetime :appointment_date t.timestamps end end end يمكنك إدارة مجموعة نماذج المنضمّة عن طريق أساليب ارتباط has_many، فعلى سبيل المثال، إذا عيّنت هذا: physician.patients = patients فإنه سينشئ النماذج المنضمّة تلقائيا للكائنات المرتبطة حديثا، إذا كان بعضها موجود سابقا وفُقِد حاليا، فستُحذف صفوف الضم الخاصة به تلقائيا. يمكنك الاستفادة من ارتباط has_many :through لإنشاء "اختصارات” من خلال ارتباطات has_many متداخلة، فعلى سبيل المثال، إذا كان مستند يملك أقسام عديدة، ويحتوي القسم على فقرات، فقد تحتاج في بعض الأحيان إلى الحصول على مجموعة بسيطة من جميع الفقرات في المستند، ويمكنك إنشاء ذلك عن طريق: class Document < ApplicationRecord has_many :sections has_many :paragraphs, through: :sections end class Section < ApplicationRecord belongs_to :document has_many :paragraphs end class Paragraph < ApplicationRecord belongs_to :section end سيفهم Rails هذا الآن عن طريق محدد through: :sections: @document.paragraphs 2.5 ارتباط has_one :through تنشئ ارتباطات has_one :through اتصال واحد لواحد (one-to-one) مع نموذج آخر، هذا الارتباط يشير إلى أن النموذج المُعلن عنه يمكن أن يقابل مع مثيل من نموذج آخر من خلال نموذج آخر، على سبيل المثال، إذا كان يملك كل مُورّد حساب واحد، وكل حساب مرتبط بسجل حساب واحد، فسيكون نموذج المُورّد كالتالي: class Supplier < ApplicationRecord has_one :account has_one :account_history, through: :account end class Account < ApplicationRecord belongs_to :supplier has_one :account_history end class AccountHistory < ApplicationRecord belongs_to :account end عملية التهجير ستشبه هذه: class CreateAccountHistories < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name t.timestamps end create_table :accounts do |t| t.belongs_to :supplier, index: true t.string :account_number t.timestamps end create_table :account_histories do |t| t.belongs_to :account, index: true t.integer :credit_rating t.timestamps end end end 2.6 الارتباط has_and_belongs_to_many ينشئ ارتباط has_and_belongs_to_many اتصال الكثير إلى الكثير many-to-many مباشر مع نموذج آخر بدون تدخل نموذج ثالث، فعلى سبيل المثال، إذا كان تطبيقك يحتوي على تجميعات وأجزاء، ستجد أجزاء عديدة مع كل تجميع وكل جزء يظهر في الكثير من التجميعات، فيمكنك إعلان هذا النموذج كالتالي: class Assembly < ApplicationRecord has_and_belongs_to_many :parts end class Part < ApplicationRecord has_and_belongs_to_many :assemblies end عملية التهجير ستشبه هذه: class CreateAssembliesAndParts < ActiveRecord::Migration[5.0] def change create_table :assemblies do |t| t.string :name t.timestamps end create_table :parts do |t| t.string :part_number t.timestamps end create_table :assemblies_parts, id: false do |t| t.belongs_to :assembly, index: true t.belongs_to :part, index: true end end end 2.7 الاختيار بين belongs_to و has_one إذا أردت إنشاء علاقة واحد إلى واحد (one-to-one) بين نموذجين، فستحتاج إلى إضافة belongs_to to إلى أحدهم و has_one إلى الآخر، فكيف ستعرف لمن تضيف هذا؟ يمكنك تمييز ذلك عن طريق المكان الذي وضعت فيه المفتاح الخارجي (ستكون في الجدول الصنف الذي أعلنت فيه ارتباط belongs_to)، لكن يجب عليك أن تفكر في معنى الفعلي للبيانات، فعلاقة has_one تعني أنه يوجد شيء خاص بك، أي أن جزء منه يعود لك، فعلى سبيل المثال، من المنطقي القول بأن المُورّد يملك حساب على أن الحساب يملك مُورّد، وهذا يعني أن العلاقة الصحيحة مشابهة لهذه: class Supplier < ApplicationRecord has_one :account end class Account < ApplicationRecord belongs_to :supplier end عملية التهجير ستشبه هذه: class CreateSuppliers < ActiveRecord::Migration[5.0] def change create_table :suppliers do |t| t.string :name t.timestamps end create_table :accounts do |t| t.integer :supplier_id t.string :account_number t.timestamps end add_index :accounts, :supplier_id end end 2.8 الاختيار بين has_many :through و has_and_belongs_to_many يوفر Rails طريقتين لإعلان علاقة الكثير إلى الكثير (many-to-many) بين النماذج، أبسط طريقة هي عن طريق استخدام has_and_belongs_to_many، والتي تمكنك من إنشاء هذا الارتباط مباشرة: class Assembly < ApplicationRecord has_and_belongs_to_many :parts end class Part < ApplicationRecord has_and_belongs_to_many :assemblies end الطريقة الثانية هي عن طريق استخدام has_many :through لجعل الارتباط غير مباشرة، عن طريق ضم نموذج: class Assembly < ApplicationRecord has_many :manifests has_many :parts, through: :manifests end class Manifest < ApplicationRecord belongs_to :assembly belongs_to :part end class Part < ApplicationRecord has_many :manifests has_many :assemblies, through: :manifests end ببساطة، إذا أردت العمل مع نموذج العلاقة ككيان مستقل، يجب عليك إنشاء علاقة has_many :through، وإذا لم تحتاج إلى القيام بأي شيء مع نموذج العلاقة، سيكون من الأسهل إنشاء علاقة has_and_belongs_to_many (لا تنسى إنشاء جدول الضم joining table في قاعدة البيانات). يجب عليك استخدام has_many :through إذا احتجت إلى عمليات تحقق validations، دوال استدعاء callbacks أو سمات attributes إضافيَة في نموذج الضم. 2.9 ارتباطات متعددة الأشكال Polymorphic Associations من المواضيع المتقدمة قليلا في الارتباطات، هي الارتباطات متعددة الأشكال، فعن طريقها، يمكن للنموذج أن يرتبط بأكثر من نموذج بارتباط واحد، فعلى سبيل المثال، قد تملك نموذج صورة التي تنتمي إلى نموذج الموظف أو إلى نموذج المنتج، ويمكنك الإعلان عن ذلك عن طريق التالي: class Picture < ApplicationRecord belongs_to :imageable, polymorphic: true end class Employee < ApplicationRecord has_many :pictures, as: :imageable end class Product < ApplicationRecord has_many :pictures, as: :imageable end فكر في إعلان belongs_to متعدد الأشكال على أنه إنشاء لواجهة يمكن لأي نموذج آخر أن يستخدمها، من مثيل نموذج Employee، يمكنك استرداد مجموع من الصور عن طريق: @employee.pictures. وبنفس الطريقة يمكنك استرداد product.pictures@. إذا كنت تملك مثيل من نموذج الصورة، فيمكنك الحصول على الأب (parent) عن طريق picture.imageable@، ولتحقيق ذلك، تحتاج إلى إعلان كل من عمود مفتاح خارجي وعمود النوع في النموذج الذي أعلنت فيه واجهة متعددة الأشكال: class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name t.integer :imageable_id t.string :imageable_type t.timestamps end add_index :pictures, [:imageable_type, :imageable_id] end end يمكن تبسيط عملية التهجير عن طريق استخدام t.references: class CreatePictures < ActiveRecord::Migration[5.0] def change create_table :pictures do |t| t.string :name t.references :imageable, polymorphic: true, index: true t.timestamps end end end 2.10 ضم ذاتي Self Joins عند تصميم نموذج بيانات، ستجد في بعض الأحيان أن العلاقة يجب أن تكون مرتبطة بنفسها، فعلى سبيل المثال، قد تريد تخزين جميع الموظفين في نموذج قاعدة بيانات واحدة، لكن يجب تقدر أيضا على تتبع العلاقات مثل علاقة المدير بالمرؤوسين، وهذه الحالة يمكن إنشاءها عن طريق ارتباطات الضم الذاتي: class Employee < ApplicationRecord has_many :subordinates, class_name: "Employee", foreign_key: "manager_id" belongs_to :manager, class_name: "Employee" end مع هذا الإعداد، يمكنك استرداد employee.subordinates@ و employee.manager@. في عمليات التهجير/المخطط، ستحتاج إلى إضافة عمود المرجع إلى نفس النموذج. class CreateEmployees < ActiveRecord::Migration[5.0] def change create_table :employees do |t| t.references :manager, index: true t.timestamps end end end وسنتابع التعرّف على الإرتباطات في Active Record في الدروس اللاحقة. المصدر: توثيقات Ruby on Rails
  3. في الدرس السابق ألقينا نظرة عامة حول التحقيقات في Active Record وسنتابع في هذا الدرس تعلم مساعدات التحقيقات 2 مساعدات التحقيقات validation helpers يعرض Active Record العديد من مساعدات تحقيقات المعرفة مسبقًا التي يمكنك استخدامها مباشرة داخل تعريفات فئتك. تدعم تلك المساعدات قواعد تحقيقات شائعة. فى كل مرة يفشل تحقيق ، تضاف رسالة خطأ الى مجموعة أخطاء الكائن ، و ترتبط تلك الرسالة بالخاصية التي يتم التحقق منها. يقبل كل مساعد رقم اعتباطي من أسماء الخواص ، لذلك يمكنك إضافة نفس نوع التحقيق للعديد من الخواص بسطر كود واحد. جميعم يقبل خيارات on: و message: الاثنين يحددان متى يجب أن يتم اجراء التحقيق ، و أي رسالة يجب إضافتها إلى مجموعة الأخطاء فى حال فشلها ، على الترتيب. خيار ال on: يأخذ القيم create: (أنشىء) أو :update: (حدّث). هناك رسالة خطأ افتراضية لكل واحد من مساعدات التحقيق. تُستَخدم تلك الرسائل عندما لا يكون خيار message: محددًا. فلنأخذ نظرة على كل واحد من مساعدات التحقيق. 2.1 القبول acceptance تتأكد هذه الطريقة اذا ما كان الـ checkbox فى واجهة تشغيل المستخدم معلّمًا عندما يتم إرسال أي نموذج. يتم استخدام هذه الطريقة غالبًا عندما يكون المستخدم بحاجة لقبول شروطك لتقديم الخدمة ، يؤكد على قراءة نص معيّن ، أو أي مفهوم مشابه. class Person < ApplicationRecord validates :terms_of_service, acceptance: true end يجري هذا الفحص فقط إذا كانت شروط العمل ليست nil. رسالة الخطأ الافتراضية لهذا المساعد هي “must be acceptd” أي يجب أن تقبل. يمكن أيضًا تجاوز الرسالة المخصصة من خلال خيار الرسالة message. class Person < ApplicationRecord validates :terms_of_service, acceptance: { message: 'must be abided' } end يمكن أيضًا أن تستقبل خيار accept: التي تحدد القيم المسموح بها لأن تعتبر مقبولة. إنها تعود كما هو افتراضي إلى [`1` , true] و يمكن أن تتغير بسهولة. class Person < ApplicationRecord validates :terms_of_service, acceptance: { accept: 'yes' } validates :eula, acceptance: { accept: ['TRUE', 'accepted'] } end هذا التحقيق مخصص جدًا لتطبيقات المواقع و هذا “القبول” acceptance لا يحتاج إلى أن يسجّل في أي مكان بقاعدة البيانات. إذا لم يكن لديك مجال له ، سوف ينشىء المساعد خاصية افتراضية. إذا كان لا يوجد مجال في قاعدة بياناتك يجب أن يكون خيار القبول مفعّلًا أو يتضمن true وإلا لن يجري التحقيق. 2.2 validates.associated مرتبط بالتحقيق يجب أن تستخدم هذا المساعد عندما يكون لنموذجك ارتباطات بنماذج أخرى و هم أيضًا بحاجة إلى أن يتم التحقق منهم. عندما تحاول حفظ كائن ، ?valid سيتم استدعائها لكل واحد من الكائنات المرتبطة. class Library < ApplicationRecord has_many :books validates_associated :books end هذا التحقيق سوف يعمل مع كل أنواع الارتباطات. رسالة الخطأ الافتراضية لـ validates_associated هي “is invalid” أي غير صالح. لاحظ أن كل associated object لن يحتوي على مجموعة أخطائه ، الأخطاء لا تتصاعد إلى نموذج الاستدعاء. 2.3 التأكيد confirmation يجب أن تستخدم هذا المساعد عندما يكون لديك حقول نصوص التي يجب أن تستقبل نفس المحتوى بالضبط. على سبيل المثال من الممكن أن تكون تريد التأكيد على عنوان بريد إلكتروني أو كلمة مرور. هذا التحقيق ينشىء خاصية افتراضية بنفس اسم الحقل الذي يجب أن يتم التأكيد عليه بـ”_confirmation” (تأكيد) مُلحق. class Person < ApplicationRecord validates :email, confirmation: true end فى قالب العرض خاصتك يمكن أن تستخدم شيئًا مثل: <%= text_field :person, :email %> <%= text_field :person, :email_confirmation %> هذا الفحص يجري فقط إذا كان email_confirmation ليس nil. لكي تطلب معلومات تأكد من إضافة presence check لخاصية التأكيد: class Person < ApplicationRecord validates :email, confirmation: true validates :email_confirmation, presence: true end يوجد خيار case_sensitive الذي يمكنك استخدامه لتحدد إذا ما كان من قيود التأكيد أن يكون مستشعرًا لحالة الحروف أم لا (أي يفرق بين الحروف الـ capital و الـ small). والخيار الافتراضي له هو true. class Person < ApplicationRecord validates :email, confirmation: { case_sensitive: false } end رسالة الخطأ الافتراضية لهذا المساعد هي “doesn’t match confirmation” أي لا يتوافق مع التأكيد. 2.4 استثناء exclusion هذا المساعد يصدّق أن قيم الخواص ليست متضمّنة فى مجموعة معطاة. في الحقيقة ، تلك المجموعة يمكن أن تكون أي كائن غير قابل للعد. class Account < ApplicationRecord validates :subdomain, exclusion: { in: %w(www us ca jp), message: "%{value} is reserved." } end يمتلك مساعد الاستثناء خيار in: الذي يستقبل مجموعة من القيم التي لن تقبل الخصائص المتحقق منها. خاصية in: لها اسم آخر متعارف عليه هو within: و الذي يمكن استخدامه لنفس الوظيفة. هذا المثال يستخدم خيار message: ليعرض كيف يمكن تضمين قيمة الخصّيصة. من أجل خيارات مفصّلة/كاملة لمتغير الرسالة. رسالة الخطأ الافتراضيّة هى “is reversed” 2.5 الشكل/التصميم format هذا المساعد يتحقق من قيم الخواص باختبار اذا ما كانوا تطابق تعبير اعتيادي معطى ، الذي يتم تحديده باستخدام خيار with: . class Product < ApplicationRecord validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, message: "only allows letters" } end بدلًا من ذلك يمكنك اختيار ألا تطابق الخواص المحددة expression معين باستخدام خيار without: . رسالة الخطأ الافتراضية هى “is invalid” . 2.6 التضمين inclusion هذا المساعد يتحقق أن قيم الخواص متضمنة فى مجموعة معينة. هذه المجموعة يمكن أن تكون أي كائن غير معدود. class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" } end مساعد التضمين inclusion helper لديه خيار in: الذي يستقبل مجموعة من القيم التي ستكون مقبولة. خيار in: لديه اسم آخر هو within: الذي يمكن استخدامه بدلًا منه لنفس الوظيفة. يستخدم المثال السابق خيار الـ message: ليبين كيف يمكن تضمين قيمة الخصّيصة. رسالة الخطأ الافتراضية لهذا المساعد هي “is not included in the list”. 2.7 الطول هذا المساعد يتحقق من طول قيم الخصائص. إنه يدعم خيارات مختلفة حتى يتسنّى للمستخدم تحديد قيود الطول بطرق مختلفة. class Person < ApplicationRecord validates :name, length: { minimum: 2 } validates :bio, length: { maximum: 500 } validates :password, length: { in: 6..20 } validates :registration_number, length: { is: 6 } end خيارات قيود الطول المتاحة هي: minimum: (أقل حد) الخاصية لا يمكن أن تكون أقل من الطول المحدد maximum: (أقصى حد) الخاصية لا يمكن أن تكون أكثر من الطول المحدد. in: (أو within:) (في حدود) طول الخاصية يجب أن يكون متضمن في أبعاد معيّنة. قيمة هذا الخيار يجب أن يكون نطاق/مدى. is: (يساوي) طول الخاصية يجب أن يكون مساويًا للقيمة المعطاه. رسالة الخطأ الافتراضيّة تعتمد على نوع تحقيق الطول المُجرى. يمكنك تخصيص هذه الرسائل باستخدام خيارات wrong_length: (طول خطأ) و too_long: (طويل جدًا) و too_short: (قصير جدًا) ، و {count} كماسك للمكان للرقم المناظر لقيد الطول المُستخدم. يمكنك أيضًا استخدام خيار message: لتحدد رسالة الخطأ. class Person < ApplicationRecord validates :bio, length: { maximum: 1000, too_long: "%{count} characters is the maximum allowed" } end لاحظ أن رسائل الأخطاء هي جمع (على سبيل المثال “قصير جدًا( الحد الأدنى هو {count} أحرف)”). لهذا السبب عندما يكون الحد الأدنى هو 1 يجب أن توفر رسالة مخصصة أو استخدم presence : true بدلًا من ذلك. عندما يمتلك in: أو within: حد أدنى 1 ينبغى عليك إما توفير رسالة خطأ مخصصة أو استدعاء presence طبقًا لأولوية الطول. 2.8 العددية numericality هذا المساعد يتحقق أن خواصّك بها قيم عددية فقط. افتراضيّا ، سوف يقوم بالتحقق من وجود رقم صحيح integer أو عدد عشري floating point number. لتحدد السماح بالأرقام الصحيحة فقط اضبط only_integer: على true. إذا قمت بضبط only_integer: على true ، ستقوم باستخدام: /\A[+-]?\d+\z/ عبارة اعتيادية للتحقق من قيمة الخصيصة. فيما عدا ذلك ستقوم بتحويل القيمة الى رقم باستخدام float. class Player < ApplicationRecord validates :points, numericality: true validates :games_played, numericality: { only_integer: true } end بجانب only_integer: هذا المساعد يقبل أيضًا هذه الخيارات لإضافة قيود على القيم المقبولة: greater_than: تحديد القيمة الصُغرى للقيمة المُضافة greater_than_or_equal_to: تحديد القيمة الصُغرى مع إمكانية المساواة equal_to: القيمة المُضافة يجب أن تساوي less_than: تحديد القيمة الكُبرى للقيمة المضافة less_than_or_equal_to: تحديد القيمة الكُبرى مع إمكانية المساواة other_than: القيمة المُضافة يجب ألّا تساوي تلك القيمة Odd: القيمة المضافة يجب أن تكون فردية Even: القيمة المضافة يجب أن تكون زوجية رسالة الخطأ الافتراضية هى “is not a number” (ليس رقمًا). 2.9 وجود presence هذا المساعد يتحقق أن الخصائص المحددة ليست فارغة. إنها تستخدم طريقة ?blank لتكتشف إذا كانت القيمة إما nil أو سلسلة فارغة blank string – و التي تكون سلسلة فارغة أو تتكون من مساحة. class Person < ApplicationRecord validates :name, :login, :email, presence: true end إذا أردت أن تتأكد أن الارتباطات موجودة سوف تحتاج أن تختبر إذا ما كان الكائن نفسه موجودًا ، و ليس المفتاح الخارجي المستخدم لتنظيم الارتباط. class LineItem < ApplicationRecord belongs_to :order validates :order, presence: true end يجب أن تحدد خيار inverse: للرابطة حتى تتحقق من السجلّات المرتبطة التي يجب أن تكون موجودة. class Order < ApplicationRecord has_many :line_items, inverse_of: :order end إذا تحققت من وجود كائن من خلال علاقة has_one أو has_many سوف يفحص إذا ما كان الكائن ليس ?blank (فارغ) ولا marked_for_destruction (محدد للمحو) بما أن ?false.blank صحيح ، ينبغى أن تستخدم أحد التحقيقات التالية إذا أردت التحقق من وجود حقل Boolean: validates :boolean_field_name, inclusion: { in: [true, false] } validates :boolean_field_name, exclusion: { in: [nil] } باستخدام أحد هذه التحقيقات سوف تتأكد أن القيمة لن تكون nil ، مما سينتج عنه قيمة NULL فى أغلب الحالات. 2.10 الغياب absence هذا المساعد يتحقق من أن الخصائص المحددة غائبة. إنها تستخدم وسيلة ?present لتكتشف إذا ما كانت القيمة ليست nil أو balnk string class Person < ApplicationRecord validates :name, :login, :email, absence: true end إذا أردت التأكد أن التصادق غائب ، سوف تحتاج أن تختبر ما إذا كان الكائن نفسه غائبا أم المفتاح الخارجي المستخدم لتنظيم الارتباط. class LineItem < ApplicationRecord belongs_to :order validates :order, absence: true end حتى تتحقق من السجلات المتصادقة التي يلزم غيابها يجب أن تحدد خيار inverse_of: للمصادقة. class Order < ApplicationRecord has_many :line_items, inverse_of: :order end إذا تحققت من غياب كائن من خلال علاقة has_one أو has_many ، سوف تفحص إذا ما كان الكائن ليس ?present أو ?marked_for_destruction مردودا كـ false. لو أردت التحقق من غياب حقل Boolean ينبغي أن تستخدم {[validates :field_name, exclusion: {in: [true, false . رسالة الخطأ الافتراضية هى “must be blank”. أى يجب أن يكون فارغًا. 2.11 التفرّد/التميّز uniqueness هذا المساعد يتحقق أن قيمة الخصيصة فريدة قبل حفظ الكائن مباشرة. إنها لا تنشىء قيد تفرّد uniqueness constraint في قاعدة البيانات ، لذلك من الممكن أن ينشىء قاعدتي بيانات مختلفتين سجلين مختلفين بنفس القيمة لعمود كنت تقصد جعله فريدًا. حتى تتجنّب ذلك يجب أن تنشىء مؤشّر متفرّد على هذا العمود في قاعدة بياناتك. class Account < ApplicationRecord validates :email, uniqueness: true end التحقق يحدث بإجراء SQL query في جدول الوسائل ، بحثًا عن سجل موجود بنفس القيمة التي في الخاصية. يوجد خيار scope: الذي يمكن استخدامه لتحديد خاصية أو أكثر تستخدم لتحديد فحص التفرّد. class Holiday < ApplicationRecord validates :name, uniqueness: { scope: :year, message: "should happen once per year" } end إذا كنت تأمل فى إنشاء قيود في قاعدة البيانات لمنع أي انتهاك لتحقيق التفرّد باستخدام خيار scope: ، فعليك إنشاء فهرس فريد لكلا العمودين في قاعدة بياناتك. انظر the SQL manual لتفاصيل أكثر عن فهارس العواميد المتعددة أو the PostgresSQL manual لأمثلة على قيود فريدة تنطبق على مجموعة من الأعمدة. يوجد أيضًا خيار case_sensitive: الذي يمكنك استخدامه لتحديد ما إذا كان قيد التفرّد سيكون حساسًا لحالة الأحرف أم لا. الضبط الافتراضي للخيار يكون ساري/صحيح true. class Person < ApplicationRecord validates :name, uniqueness: { case_sensitive: false } end رسالة الخطأ الافتراضية هي “has already been taken”. أي تم أخده بالفعل. 2.12 يتحقق بـ validates_with هذا المساعد ينقل السجل إلى فئة مختلفة من التصديق/التحقيق. class GoodnessValidator < ActiveModel::Validator def validate(record) if record.first_name == "Evil" record.errors[:base] << "This person is evil" end end end class Person < ApplicationRecord validates_with GoodnessValidator end يأخذ مساعد validates_with فئة أو قائمة من الفئات لاستخدامها للتحقيق. لا يوجد رسالة افتراضية لرسائل الخطأ لـ validates_with. يجب عليك أن تضيف الأخطاء إلى مجموعة أخطاء السجل في فئة المحقق يدويًّا. لكي تنفّذ وسيلة التحقيق يجب أن تعرّف معامل السجل ، و الذي هو السجل الذي سيتم التحقق منه. على غرار باقي التحقيقات validates_with تأخذ خيارات if: و unless: و on: . إذا جرّبت أي خيار آخر سيقوم بإرسال تلك الخيارات إلى فئة المتحقق كخيارات متاحة. class GoodnessValidator < ActiveModel::Validator def validate(record) if options[:fields].any?{|field| record.send(field) == "Evil" } record.errors[:base] << "This person is evil" end end end class Person < ApplicationRecord validates_with GoodnessValidator, fields: [:first_name, :last_name] end لاحظ أن المتحقق سوف يبدأ مرة واحدة فقط في دائرة عمر التطبيق ، و ليس لكل إجراء تحققي ؛ لذلك كن حذرًا عند استخدام متغيرات مقترحة/تجريبية بداخله. لو كان متحققك معقّد كفاية لدرجة أنك تريد متغيرات instance يمكنك استخدام plain old Ruby object بسهولة بدلاً من ذلك: class Person < ApplicationRecord validate do |person| GoodnessValidator.new(person).validate end end class GoodnessValidator def initialize(person) @person = person end def validate if some_complex_condition_involving_ivars_and_private_methods? @person.errors[:base] << "This person is evil" end end # ... End 2.13 تحقق من الكل validates_each هذا المساعد يتحقق من الخصائص مقابل block. إنها لا تمتلك وظيفة تحقيق معينة ، يجب أن تنشىء واحدة باستخدام block و كل خاصية تمر إلى validates_each سوف تُختبر في مقابلها. في المثال التالي لا نريد الأسماء و أسماء العائلات أن تبدأ بحروف صغيرة. class Person < ApplicationRecord validates_each :name, :surname do |record, attr, value| record.errors.add(attr, 'must start with upper case') if value =~ /\A[[:lower:]]/ end end الـ block يستقبل السجل و اسم الخاصية و قيمة الخاصية. يمكنك فعل أي شىء ، مثل الكشف عن البيانات الصالحة داخل الـ block. إذا فشلت تحقيقاتك ينبغي عليك إضافة رسالة خطأ إلى النموذج، و من ثم جعله غير ساري. سنتابع في الدروس القادمة بقية هذا الدليل التعليمي حول Active Record Validations. المصدر: توثيقات Ruby on Rails.
  4. في الدرسين السابقين ألقينا نظرة عامة حول التحقيقات في Active Record وتعرّفنا على مساعدات التحقيقات وتعرّفنا أيضًا على الخيارات الشائعة وأنواع التحقيقات وسنتابع في هذا الدرس ما تبقى من هذا الدليل التعليمي . 6 أداء/تنفيذ تحقيقات مخصّصة Performing Custom Validations عندما لا تكون التحقيقات المدمجة كافية لاحتياجاتك يمكنك كتابة محققاتك الخاصّة أو وسائل تحقيق كما تفضّل. 6.1 المحققات المخصّصة Custom Validators المحققات المخصصة عبارة عن فئات تقتبس من ActiveModel::Validator. تلك الأصناف يجب أن تنفّذ وسيلة validate التي تأخذ سجل كـتعبير و تُؤدّي التحقيق عليه. يُستدعى المحقق الخاص باستخدام وسيلة validates_with. class MyValidator < ActiveModel::Validator def validate(record) unless record.name.starts_with? 'X' record.errors[:name] << 'Need a name starting with X please!' end end end class Person include ActiveModel::Validations validates_with MyValidator end أسهل طريقة لإضافة محققات مخصّصة للتحقق من خصائص شخصيّة تكون ب ActiveModel::EachValidator مناسب. في هذه الحالة يجب أن ينفّذ المتحقق الخاص وسيلة validate_each ، التي تأخذ ثلاثة تعبيرات: سجل و خاصّية و قيمة. يتم إدارتهم مع ما يناسبهم: الخاصّية يتم التحقق منها ، و قيمة الخاصّية يتم تجاوزها. class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i record.errors[attribute] << (options[:message] || "is not an email") end end end class Person < ApplicationRecord validates :email, presence: true, email: true end كما هو موضّح بالمثال يمكن دمج(ربط) محقّقات أساسيّة بمحقّقاتك الخاصّة. 6.2 الوسائل المخصّصة يمكنك أيضًا إنشاء وسائل تؤكّد على حالة النموذج و تضيف رسائل إلى الـ errorscollection عندما يكونوا غير صالحين. يجب أن تسجّل بعد ذلك تلك الوسائل باستخدام فئة (validate (API ، مرورًا بالرموز الخاصة بأسماء وسائل التحقيق. يمكنك تمرير(إرسال) أكثر من رمز واحد لكل وسيلة لفئة و سيجري التحقيقات المخصصة في نفس الترتيب الذي سُجّلوا به. وسيلة ?valid سوف تؤكّد أن مجموعة الأخطاء فارغة، لذلك يجب أن تضيف تحقيقاتك المخصّصة أخطاء إليها عندما ترغب أن يفشل التحقيق. class Invoice < ApplicationRecord validate :expiration_date_cannot_be_in_the_past, :discount_cannot_be_greater_than_total_value def expiration_date_cannot_be_in_the_past if expiration_date.present? && expiration_date < Date.today errors.add(:expiration_date, "can't be in the past") end end def discount_cannot_be_greater_than_total_value if discount > total_value errors.add(:discount, "can't be greater than total value") end end end كما هي مضبُوطة افتراضيّا ، سوف تعمل هذه التحقيقات كل مرّة تستدعي ?valid أو تحفظ الكائن. لكن من الممكن أيضًا التحكم في وقت عمل هذه التحقيقات بإعطاء خيار on: إلى وسيلة validate إما بـ create: أو update: class Invoice < ApplicationRecord validate :active_customer, on: :create def active_customer errors.add(:customer_id, "is not active") unless customer.active? end end 7 التعامل مع أخطاء التحقيق Working with Validation Errors بالإضافة إلى وسائل ?valid و ?invalid التي تم شرحها مُسبقًا ، يدعم Rails عددًا من الوسائل للتعامل مع مجموعة الأخطاء و يحقق في صحّة الكائن. 7.1 الأخطاء Errors ترجع فئة ActiveModel::Errors يحتوي على كل الأخطاء. كل مفتاح هو اسم الخاصّية و القيمة في مجموعة مصفوفة من المتسلسلات النصّية بالأخطاء. class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end person = Person.new person.valid? # => false person.errors.messages # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} person = Person.new(name: "John Doe") person.valid? # => true person.errors.messages # => {} 7.2 الأخطاء [ ] Errors تستخدم errors عندما تريد أن تفحص رسالة الخطأ لخاصّية معيّنة. تقوم بالرد بمجموعة مصفوفة من المتسلسلات النصّية بكل رسائل الأخطاء لخاصّية مُعيّنة ، كل متسلسلة برسالة خطأ واحدة. لو لم يكن هناك أخطاء مرتبطة بالخاصّية ، تقوم بالرد بمصفوفة فارغة. class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end person = Person.new(name: "John Doe") person.valid? # => true person.errors[:name] # => [] person = Person.new(name: "JD") person.valid? # => false person.errors[:name] # => ["is too short (minimum is 3 characters)"] person = Person.new person.valid? # => false person.errors[:name] # => ["can't be blank", "is too short (minimum is 3 characters)"] 7.3 إضافة الأخطاء errors.add تجعلك وسيلة add أن تضيف رسالة خطأ مرتبطة بخاصّية مُعيّنة. تقوم بأخذ الخاصّية و رسالة الخطأ كتعابير. ترد وسيلة errors.full_messages (أو مكافئاتها errors.to_a ) برسالة الخطأ بشكل مفضّل للمستخدم ، و اسم الخاصّية مكبّرًا ومضافًا على بداية كل رسالة ، كما يظهر في الأمثلة بالأسفل. class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, "cannot contain the characters !@#%*()_-+=") end end person = Person.create(name: "!@#") person.errors[:name] # => ["cannot contain the characters !@#%*()_-+="] person.errors.full_messages # => ["Name cannot contain the characters !@#%*()_-+="] أن ترفق رسالة في بداية مصفوفة errors.messages لخاصّية ما ، مكافىء لأن تستخدم errors#add . class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.messages[:name] << "cannot contain the characters !@#%*()_-+=" end end person = Person.create(name: "!@#") person.errors[:name] # => ["cannot contain the characters !@#%*()_-+="] person.errors.to_a # => ["Name cannot contain the characters !@#%*()_-+="] 7.4 تفاصيل الأخطاء errors.details يمكنك تحديد نوع محقّق جدول تقطيع تفاصيل الخطأ المُعاد التقطيع باستخدام وسيلة errors.add. class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, :invalid_characters) end end person = Person.create(name: "!@#") person.errors.details[:name] # => [{error: :invalid_characters}] لكي تحسّن تفاصيل الأخطاء ، لتحتوي مجموعة الحروف غير المسموح بها على سبيل المثال ، يمكنك تمرير مفاتيح إضافيّة إلى errors.add. class Person < ApplicationRecord def a_method_used_for_validation_purposes errors.add(:name, :invalid_characters, not_allowed: "!@#%*()_-+=") end end person = Person.create(name: "!@#") person.errors.details[:name] # => [{error: :invalid_characters, not_allowed: "!@#%*()_-+="}] كل محقّقات Rails المدمجة تزيد جدول تقطيع التفاصيل بنوع محقّق متناظر. 7.5 قاعدة الأخطاء[ errors [ :base يمكنك إضافة رسائلك أخطاء مرتبطة بحالة الكائن ككل بدلًا من كونها مرتبطة بخاصّية مُعيّنة. يمكنك أيضًا استخدام تلك الوسيلة عندما تريد أن تقول أن الكائن غير صالح للاستخدام ، بغض النظر عن قيم خصائصه. بما أن [errors[ :base مصفوفة ، يمكنك ببساطة إضافة متسلسلة نصّية إليها و سوف تُستَخدَم كَرسالة خطأ. class Person < ApplicationRecord def a_method_used_for_validation_purposes errors[:base] << "This person is invalid because ..." end end 7.6 محو الأخطاء errors.clear تُستخدم وسيلة clear عندما تريد أن تمسح كل رسائل الخطأ في مجموعة الأخطاء errorscollection . بالطبع مناداة errors.clear لكائن غير صالح لن يجعله ساريًا أو صالحًا للاستخدام. مجموعة الأخطاء سوف تُفرّغ ، لكن المرة القادمة التي تستدعي فيها ?valid أو أي وسيلة تحاول أن تحفظ الكائن في قاعدة البيّانات ، سوف تعمل التحقيقات مجّددًا. لو فشل أيّ من التحقيقات سوف تُملأ مجموعة الأخطاء مجددًا. class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end person = Person.new person.valid? # => false person.errors[:name] # => ["can't be blank", "is too short (minimum is 3 characters)"] person.errors.clear person.errors.empty? # => true person.save # => false person.errors[:name] # => ["can't be blank", "is too short (minimum is 3 characters)"] 7.7 حجم الأخطاء errors.size ترد وسيلة size بالعدد الكلي لرسائل الأخطاء للكائن. class Person < ApplicationRecord validates :name, presence: true, length: { minimum: 3 } end person = Person.new person.valid? # => false person.errors.size # => 2 person = Person.new(name: "Andrea", email: "andrea@example.com") person.valid? # => true person.errors.size # => 0 8 تقديم أخطاء التحقيق للعرض Displaying Validation Errors in Views بمجرّد إنشائك نموذجًا و إضافة تحقيقات ، لو كان النموذج مصنوع بواسطة شكل المواقع ، من المحتمل أن تريد عرض رسالة عند فشَل أحد التحقيقات. لأن كل تطبيق يعالج هذا النوع من الأشياء بطريقة مختلفة ، لا يتضمّن Rails أي view helpers لتساعدك على توليد تلك الرسائل بصورة مباشرة. مع ذلك ، بسبب فرط الوسائل التي يعطيك إيّاها Rails لكي تتعامل مع التحقيقات بشكل عام، تكون العملية سهلة جدّا لإنشاء ما يخصّك منها. بالاضافة إلى ذلك ، عند إنتاج هيكل رئيسي مساعد ، سوف يضع Rails بعض ERB إلى _form.html.erb الذي تنتجه والذي يعرض قائمة بكل الأخطاء في النموذج. بافتراض أن لدينا نموذج تم حفظه في متغيّر باسم article@ ، سيبدو كالتالي: <% if @article.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved:</h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> علاوةً على ذلك، إذا استخدمت تصميم Rails من المساعدين لتولّد تصميماتك(نماذجك)، عندما يحدث خطأ تحقيق في الحقل سوف تنتج <div> إضافي بالقرب من المدخل. <div class="field_with_errors"> <input id="article_title" name="article[title]" size="30" type="text" value=""> </div>` يمكنك حين ذاك تشكيل ال div كما تريد. الهيكل الافتراضى الذي ينتجه Rails على سبيل المثال يضيف قاعدة ال CSS هذِهِ. .field_with_errors { padding: 2px; background-color: red; display: table; } هذا يعني أنّ أي حقل به خطأ ينتهي به الحال بإطار أحمر بعرض 2 بكسل. المصدر: توثيقات Ruby on Rails.
  5. في الدرسين السابقين ألقينا نظرة عامة حول التحقيقات في Active Record وتعرّفنا على مساعدات التحقيقات وسنتابع في هذا الدرس تعلم الخيارات الشائعة للتحقيقات وأنواع التحقيقات. 3 خيارات تحقيق شائعة common Validation Options هذه خيارات تحقيق شائعة 3.1 اسمح بمجموعة خالية allow_nil خيار allow_nil يتجاوز التحقيقات عندما تكون القيمة التي يجري عليها التحقيق nil. class Coffee < ApplicationRecord validates :size, inclusion: { in: %w(small medium large), message: "%{value} is not a valid size" }, allow_nil: true end لكل الخيارات لمضمون الرسالة من فضلك اقرأ الفقرة (3.3 أمر الإرسال :message). 3.2 اسمح بالفراغ :allow_blank خيار :allow_blank مماثل لخيار :allow_nil . الخيار سوف يتجاوز التحقيقات إذا كانت القيمة blank? (فراغ). مثل مجموعة أو سلسلة فارغة على سبيل المثال. class Topic < ApplicationRecord validates :title, length: { is: 5 }, allow_blank: true end Topic.create(title: "").valid? # => true Topic.create(title: nil).valid? # => true 3.3 أمر الإرسال :message كما رأيت مسبقًا ، خيار :message يسمح لك بتحديد الرسالة التي ستضاف إلى مجموعة الأخطاء عند فشل التحقيق. عندما لا يُستخدم هذا الخيار ، سيقوم Active Record باستخدام الوضع الافتراضي لرسائل الخطأ لكل مساعد تحقيق. يقبل خيار :message أي string أو proc) و proc هو قالب من الكود الذي يتم إضافته أو قيده لمجموعة من المتغيّرات المحلّية ، و عندما يضاف يمكن أن يتم استدعاء هذا القالب في أكثر من سياق و يزال ويمكنه الوصول لتلك المتغيرات). قيمة رسالة سلسلة نصّية String :message يمكن –اختياريًا- أن تحتوي على أي أو كلّا من %{value} و %{attribute} و %{model} ، و التي سيتم تبديلها تلقائيّا عند فشل التحقيق. يتم هذا التغيير باستخدام I18n gem ، وماسك المكان يجب أن يتطابق تمامًا ، لا يُسمح بالمساحات. قيمة proc :message تُعطى تعبيرين ، الكائن الذي يتم التحقق منه ، و جدول تقطيع بأزواج key-value و هم :model و :attribute و :value . class Person < ApplicationRecord # Hard-coded message validates :name, presence: { message: "must be given please" } # Message with dynamic attribute value. %{value} will be replaced with # the actual value of the attribute. %{attribute} and %{model} also # available. validates :age, numericality: { message: "%{value} seems wrong" } # Proc validates :username, uniqueness: { # object = person object being validated # data = { model: "Person", attribute: "Username", value: <username> } message: ->(object, data) do "Hey #{object.name}!, #{data[:value]} is taken already! Try again #{Time.zone.tomorrow}" end } 3.4 على :on خيار :on يدعك تحدد متى ينبغي أن يجرى التحقيق. السلوك الافتراضي لكل مساعدين التحقيق المدمجين أن تجري عند الحفظ (عندما تكون تنشىء سجل جديد و عندما تقوم بتحديثه). إذا أردت تغير ذلك يمكنك استخدام on: :create لإجراء التحقيق فقط عند إنشاء سجل جديد ، أو on: :update لإجرائه عندما يتم تحديث السجل فقط. class Person < ApplicationRecord # it will be possible to update email with a duplicated value validates :email, uniqueness: true, on: :create # it will be possible to create the record with a non-numerical age validates :age, numericality: true, on: :update # the default (validates on both create and update) validates :name, presence: true end يمكنك أيضًا استخدام on: لتحديد سياق مخصّص. يحتاج السياق المخصص إلى تنشيط بشكل صريح من خلال إرسال اسمه إلى valid? أو invalid? أو save. class Person < ApplicationRecord validates :email, uniqueness: true, on: :account_setup validates :age, numericality: true, on: :account_setup end person = Person.new .(person.valid? (:account_setup يستثنى كلا التحقيقين بدون حفظ النموذج. و (person.save(context: :account_setup) يتحقق من الشخص في سياق account_setup قبل الحفظ. على منشّطات صريحة ، يتم التحقق من النموذج بمحققات تابعة لهذا السياق فقط و محققات بدون سياق. 4 تحقيقات صارمة Strict Validations يمكنك أيضًا اختيار محققات لتكون مشدّدة و تنشّط ActiveModel : :StrictValidationFailed عندما يكون الكائن غير ساري. class Person < ApplicationRecord validates :name, presence: { strict: true } end Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank يوجد أيضًا امكانية لعمل استثناء مخصّص على خيار :strict . class Person < ApplicationRecord validates :token, presence: true, uniqueness: true, strict: TokenGenerationException end Person.new.valid? # => TokenGenerationException: Token can't be blank 5 التحقيق المشروط conditional validation في بعض الأوقات سيبدو من المنطقي أن يتم التحقق من كائن فقط عندما تُوفى معايير فلترة معيّنة. يمكنك عمل ذلك باستخدام خيارات :if و :unless ، التي يمكن أن تأخذ رمز ، سلسلة نصّية ، أو proc. يمكنك استخدام خيار :if عندما تريد أن تحدد متى ينبغى أن يجرى التحقيق. إذا كنت تريد أن تحدد متى لا ينبغي أن يجري التحقيق إذن استخدم خيار :unless 5.1 استخدام رمز مع إذا و إذا لم Using a Sympol with :if and :unless يمكنك مصادقة خيارات :if و :unless مع رمز تناظر مع اسم الوسيلة التي سوف تُستدعى قبل حدوث التحقيق مباشرةً. هذا هو أكثر الخيارات الشائعة استخدامًا. class Order < ApplicationRecord validates :card_number, presence: true, if: :paid_with_card? def paid_with_card? payment_type == "card" end end 5.2 استخدام بروك مع إذا و إذا لم Using a Proc with :if and :unless أخيرًا يمكن ربط :if و :unless مع كائن proc ، الذي سيتم استدعاؤه. استخدام Proc object يعطيك القدرة على كتابة شرط داخلي بدلًا من وسيلة منفصلة. هذا الخيار الأفضل لها فالاستخدام مع برامج السطر الواحد (ادخال نصّي إلى ال command-line الخاص بنظم تشغيل تقوم بوظائف ما بسطر واحد من الكود) class Account < ApplicationRecord validates :password, confirmation: true, unless: Proc.new { |a| a.password.blank? } end 5.3 تجميع التحقيقات الشرطيّة Grouping Conditional Validations في بعض الأوقات يكون من المفيد أن يستخدم تحقيقات متعددة نفس الشرط. يمكن الوصول لذلك بسهولة من خلال with_options. class User < ApplicationRecord with_options if: :is_admin? do |admin| admin.validates :password, length: { minimum: 10 } admin.validates :email, presence: true end end كل التحقيقات بداخل قالب with_options سيكون قد تجاوز تلقائيّا بالفعل شرط ?if: :is_admin . 5.4 ضم/دمج شروط التصديق Combining Validation Conditions من ناحية أخرى ، عندما يحدد شروط متعددة إذا كان التحقيق ينبغي أن يجري أم لا ، يُمكن استخدام مصفوفة. إضافة إلى ذلك يمكنك تطبيق كلا من :if و :unless على نفس التحقيق. class Computer < ApplicationRecord validates :mouse, presence: true, if: ["market.retail?", :desktop?], unless: Proc.new { |c| c.trackpad.present? } end يجري التحقيق فقط عندما يكون شروط :if محققة (قيمتها ture) ، و كل شروط :unless غير محقّقة. سنتابع في الدرس القادم والأخير ما بقي من هذا الدليل التعليمي حول تحقيقات Active Record. المصدر: توثيقات Ruby on Rails.
  6. في الدروس السابقة ألقينا نظرة عامة على تهجير Active Record وكيفية إنشائه كما وتعلمنا أوامر التهجير وسنتابع في هذا الدرس ما بقي من هذه السلسلة التعليمية حول تهجير Active Record. 4 تشغيل التهجير Running Migration Rails توفر مجموعة من مهام bin/rails لتشغيل مجموعة التهجيرات التي تريدها. إن أول مهام التهجير bin/rails التي ستستعملها سيكون غالبًا rails db:migrate. و هذا هو أبسط صور التهجير حيث يقوم بتشغيل دوال change و up لكل التهجيرات التي لم تقم بتشغيلها بعد. و سيقوم بالإغلاق إن لم يُكن لديك تهجيرات من هذا النوع. عدا ذلك سيقوم بتشغيل التهجيرات بالترتيب طبقًا لتوقيته. لاحظ أن تشغيل أمر db:migrate سيقوم بتشغيل الأمر db:schema:dump و هذا سوف يقوم بتحديث ملف db/schema.rb ليوافق تركيب قاعدة بياناتك. إذا قُمت بتحديد نُسخة version معينة، فإن Active Record سيقوم بعمل التهجيرات اللازمة (باستخدام دوال change و up و down) حتى يصل للنسخة المرغوبة. يُعبر عن النسخة باستخدام أرقام توضع في إسم ملف التهجير، فإذا أردت أن تقوم بالتهجير لنُسخة 20080906120000 قُم بتشغيل: $ bin/rails db:migrate VERSION=20080906120000 إذا كانت النُسخة المرغوبة تحمل رقم أعلى من الرقم السابق فإن التهجير سيقوم بتشغيل دوال change أو up. و يقوم بعمل التهجيرات حتى يصل إلى رقم النُسخة المرغوبة. أما إذا كانت النسخة المرغوبة تحمل رقمًا أعلى فإن التهجير سيقوم بتشغيل دالة down حتى تصل إلى رقم النُسخة، لكنه لن يقوم بعمل تهجير لها(لأن ذلك سيؤدي إلى نُسخة أقل من المرغوبة). 4.1 العودة للوراء باستخدام rollback سيتحتم عليك أحيانًا العودة بالتهجير للوراء (لاحظ الفرق بينه و بين العكس). مثال: إذا أردت أن تقوم بتصحيح خطأ قُمت به، فبدلًا من تتبع رقم النُسخة المرتبطة بذلك التهجير، يُمكنك تشغيل هذا الكود: $ bin/rails db:rollback إن أمر rollback سيقوم بعكس دالة change أو تشغيل دالة down. أما إذا أردت أن تعود لأكثر من تهجير يُمكنك تحديد عدد المرات التي ستعمل فيها الدالة rollback عن طريق STEP. مثال: $ bin/rails db:rollback STEP=3 هذا سوف يعكس 3 تهجيرات. إن أمر db:migrate:redo يُعتبر إختصار لأمر rollback و لكنه سيعود بك لنقطة البداية مُجددًا. و يُمكنك تعيين عدد الخطوات به أيضًا باستخدام STEP، مثال: $ bin/rails db:migrate:redo STEP=3 إن دالة db:migrate يُمكنها أي شيئ تقوم به أوامر bin/rails ولكنهم أكثر مُلائمة عندما لا تحتاج لتحديد النُسخة التي تعمل عليها بدقة. 4.2 تهيئة قاعدة البيانات Setup Database أمر rails db:setup سيقوم بصنع قاعدة البيانات و تحميل الإسكيما و تزويدها ببياناتها الأولية. 4.3 إعادة تهيئة قاعدة البيانات Resetting Database لإعادة التهيئة استخدم أمر rails db:reset ثم قُم باستعمال أمر التهيئة من جديد. و هذان الأمران يُمكن استبدالهما بدالة rails db:drop db:setup 4.4 تشغيل تهجيرات محددة Running Specific Migrations إذا كُنت تريد إجراء عملية تهجير محددة سواء للأمام up أو للخلف down. فيُمكنك إستخدام أمر db:migrate:up أو db:migrate:down و لكن سيتوجب عليك تحديد تعيين رقم نُسخة التهجير الذي ترغب بعمله، مثال: $ bin/rails db:migrate:up VERSION=20080906120000 في المثال السابق سيقوم Active Record بالتأكد من وجود هذا التهجير مُسبقًا أم لا. فإن لم يكن موجودًا من قبل فسيقوم بتشغيل دالة change أو دالة up. أما إذا كان موجودًا بالفعل فلن يقوم بشيء. 4.5 تشغيل التهجير في بيئات عمل مختلفة different environments تلقائيًا يتم تشغيل bin/rails db:migrate في بيئة development. ويُمكنك استعمال هذا الأمر RAILS_ENV لتحديد بيئة العمل التي ترغب بعمل التهجير عليها، في الكود التالي سنقوم بعمل التهجيرات على بيئة test: $ bin/rails db:migrate RAILS_ENV=test 4.6 تغيير ناتج (خارج – Output) التهجير إن التهجير تلقائيًا يقوم بإخبارك بالمهام التي يفعلها و الوقت الذي قد يأخذه، فإذا قُمت باستخدام تهجير لصنع جدول أو إضافة قاموس، فإن الناتج سيكون هكذا: == CreateProducts: migrating ================================================= -- create_table(:products) -> 0.0028s == CreateProducts: migrated (0.0028s) ======================================== و هُناك العديد من الدوال المتوفرة داخل ملفات التهجير تساعد بمعرفة هذا الأمر: الدالة غرضها suppress_message يقوم بتحديد جزء من الكود block و يقوم بإخراج رسالة تحتوي على ما بهذا الكود. say يقوم هذا الأمر بحفظ نص رسالة و تقديمها كما هي. و سيقوم مُتغير من نوع Boolean بتحديد ظهور الرسالة من عدمه say_with_time يقوم بتحديد وقت مُعين لظهور الرسالة، و إذا كان الكود المكتوب يُخرج رقم integer فإنه يُعامله على أنه عدد الصفوف المُتأثرة. على سبيل المثال، فإن هذا التهجير: class CreateProducts < ActiveRecord::Migration[5.0] def change suppress_messages do create_table :products do |t| t.string :name t.text :description t.timestamps end end say "Created a table" suppress_messages {add_index :products, :name} say "and an index!", true say_with_time 'Waiting for a while' do sleep 10 250 end end end سوف يُنتج هذه المُخرجات == CreateProducts: migrating ================================================= -- Created a table -> and an index! -- Waiting for a while -> 10.0013s -> 250 rows == CreateProducts: migrated (10.0054s) ======================================= أما إذا أردت من Active Record ألا يُخرج أي شيئ فيُمكنك استخدام أمر rails db:migrate VERBOSE=false 5 تغيير التهجيرات الموجودة مُسبقًا إذا قُمت بارتكاب خطأ ما أثناء كتابتك للتهجير، و قُمت بتشغيل التهجير. فلا يُمكنك تعديل التهجير ثًم تشغيله من جديد. لأن Rails لن تفعل شيئًا عند كتابة الأمر rails eb:migrate لأنك بالفعل قُمت بتشغيله من قبل. لذلك عليك استخدام أمر العودة rollback بكتابة bin/rails db:rollback ثُم تعديل التهجير ثُم تشغيله من جديد. بوجه عام، تعديل التهجيرات السابقة ليس أمرًا جيدًا، لأنه سيتسبب لك بالكثير من العمل، أنت و من يعملون معك على قاعدة البيانات. خصيصًا إن تم تشغيل هذا التهجير على أجهزة السيرفرات بالفعل. فبدلًا من عمل ذلك يُمكنك كتابة تهجير جديد وظيفته هي تغيير الأخطاء أو عمل التعديلات اللازمة. فإن تعديل تهجير مكتوب حديثًا أسهل من تعديل تهجير تم تشغيله على الأجهزة فعلًا. يُمكنك إستخدام أمر العاكس revert الذي تحدثنا عنه هُنا مُسبقًا. 6 إهمال الإسكيما Schema Dumping 6.1 ماهي ملفات الإسكيما؟ إن التهجيرات، ليست هي المُتحكمة بالإسكيما الخاصة بقاعدة بياناتك. إن هذا الأمر (التحكم) يعود إلى ملفات db/schema.rb أو ملف SQL. و تلك الملفات تولد من Active Record عن طريق مُعاينة قاعدة البيانات. وإن تلك الملفات ليست مُصممة ليتم التعديل عليها، بل هي تُظهر و تُمثل حالة قاعدة البيانات. ليس هُناك حاجة لعمل جزئية جديدة بتطبيقٍ ما عن طريق إعادة تشغيل جميع التهجيرات بالتطبيق. فإنه من الأسهل و الأسرع إضافة وصف تلك الجزئية للإسكيما الحالية لقاعدة البيانات. هكذا تكون قاعدة البيانات الإختبارية على سبيل المثال، بحيث يتم إهمال الملفات الحالية لقاعدة البيانات (ملف db/schema.rb و db/structre.sql) و يتم تحميلها إلى قاعدة البيانات الإختبارية test database. إن ملفات الإسكيما مُفيدة أيضًا إذا أردت أن تلقي نظرة سريعة إلى الخصائص التي تحملها كائنات Active Record. حيث أن هذه المعلومات لا توجد في كود نموذج قاعدة البيانات و أحيانًا تكون مُنتشرة بين العديد من التهجيرات، لكن المعلومات تكون جميعها موجود في ملفات الإسكيما بشكل مُنظم. 6.2 أنواع إهمال الإسكيما schema dumps هُناك طريقتين أساسيتين لهجر/إهمال إسكيما ما. هذا يكون موجودًا داخل ملف config/application.rb عن طريق إعداد config.active_record.schema_format، و من المُمكن أن يكون ملف روبي ruby: أو sql: إذا كان من نوع روبي ruby: فإنه سيبدو مثل تهجير كبير، و ستجده داخل db/schema.rb، مثال: ActiveRecord::Schema.define(version: 20080906171750) do create_table "authors", force: true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end create_table "products", force: true do |t| t.string "name" t.text "description" t.datetime "created_at" t.datetime "updated_at" t.string "part_number" end end في كثير من الحالات، يتم صُنع هذه الملفات عن طريق مُعاينة قاعدة البيانات و تركيبها باستعمال الدوال المُستخدمة بصُنعها مثل create_table و add-Index و غيرهم. و لأن هذا أمر يكون مُستقل بقاعدة البيانات، فيُمكن استخدامها على أي قاعدة بيانات يدعمها Active Record. ومن فوائد هذا الأمر، أنه يُمكنك توزيع بيانات تطبيق ما على العديد من قواعد البيانات. من سلبيات ملف الإسكيما db/schema.rb أنه لا يُمكنك التعبير عن جميع مُكونات قاعدة البيانات مثل: المُشغلات/مُنشطات triggers و نقاط التحقق check constraints. بينما في التهجير يُمكنك الإستعانة بسطور SQL. و الإسكيما لا يُمكنها أن تحتوي على تلك السطور. لذلك إن إحتوت قاعدتك بيانات على مثل هذه الخصائص، فيجب عليك تحويل صيغة ملف الإسكيما إلى sql:. بدلًا من إهمال استعمال إسكيما Active Record، فإنه يُمكنك إهمال تركيب/تخطيط قاعدة البيانات بإستخدام أدوات مُعينة مثل (أمر rails db:structre:dump ) في ملف db/structre.sql. على سبيل المثال: في قواعد بيانات من نوع PostgreSQL يُمكنك إستخدام pg_dump. أما في قواعد بيانات MySQL و MariaDB فإن الملف سيظهر لك SHOW CREATE TABLE لمُختلف الجداول التي يحويها هذا الملف. 6.3 إهمال الإسكيما و التحكم بالكود المصدري source control لأن أوامر إهمال الإسكيما هي المُنحكم الرئيسي بالإسكيما الخاصة بقاعدة البيانات. لذلك يُنصح بشدة بأن تتأكد من وجودهم بلوحة تحكم المصدر الخاصة بك. إن ملف db/schema.rb يحتوي على النُسخة الحالية من قاعدة البيانات الخاصة بك. هذا يؤكد حدوث الأخطاء عند دمج العديد منها. و حل هذا الأخطاء يكون يدويًا بإبقاء النُسخة الأعلى من المدمجين. 7 Active Record و التكامل المرجعي referential integrity إن Active Record يدعي أن هُناك آلية ذكية في النماذج الخاصة بقواعد البيانات و ليس بقواعد البيانات نفسها. مثل المميزات triggers و constraints التي تضيف هذه الآلية إلى قاعدة البيانات نفسها, و لكنها ليست مُستخدمة بكثرة. التحقيقات مثل validates:foreign_key و uniqueness: true هي التي تُستخدم عن تكامل البيانات بين قواعد البيانات. فأمر dependent: في القواعد المُتصلة يسمح بتدمير الكائن المورَث عند تدمير الكائن المورِث. هذا لا يُمكن ضمان حدوثه في التكامل المرجعي بوجود تحقيقات المفاتيح الأجنبية في قاعدة البيانات. و مع ذلك فإن Active Record لا يوفر جميع الأدوات التي تعمل مُباشرةً مع هذه المميزات، لذلك فإنه يُمكنك إستخدام أمر excute لتنفيذ أوامر SQL. 8 التهجير و البيانات المرجعية seed data حتى لا ننسى أن الهدف الأساسي من خاصية التهجير و هو عمل أوامر تُعدل على الإسكيما بإستخدام أوامر ثابتة و مُتسقة. إن التهجير أيضًا يُمكن إستعماله لإضافة أو تعديل البيانات، و هذا مُفيد في قواعد البيانات الموجودة مُسبقًا التي لا يُمكن تدميرها و إعادة صُنعها. مثل تلك قاعدة البيانات الخاصة بالإنتاج: class AddInitialProducts < ActiveRecord::Migration[5.0] def up 5.times do |i| Product.create(name: "Product ##{i}", description: "A product.") end end def down Product.delete_all end end لإضاافة بيانات أولية seed data بعد صُنع قاعدة البيانات. إن Rails لديها دالة داخلية built-in تُساعد على تسهيل تلك العملية. خصيصًا عند إعادة تحميل قواعد البيانات بشكل مُتكرر أثناء التطوير و الإختبار. يُمكنك إستخدام تلك الخاصية بكتابة أكواد ruby التي تريدها في ملف db/seeds.rb ثُم تشغيل أمر rails db:seed 5.times do |i| Product.create(name: "Product ##{i}", description: "A product.") End و يُمكنك إستخدام تلك الخاصية لإعداد قاعدة بيانات فارغة من البداية. وبذلك نكون قد أنهينا هذه السلسلة المخصصة لتعلّم تهجير Active Record في إطار العمل روبي أون ريلز. المصدر: توثيقات Ruby on Rails.
  7. إن التهجير من أهم مزايا Active Record، حيث يسمح لك بتطوير قاعدة البيانات بسهولة مع مرور الزمن. فبدلًا من إعادة كتابة مُخططات قاعدة البيانات بسطور SQL من جديد، سيسمح لك التهجير باستعمال كود Ruby موحد المجال Domain – specific language لوصف التعديلات اللازمة التي ستقوم بها على جداولك. 1 نظرة عامة على التهجير التهجير هو الطريقة الملائمة لتبديل أجزاء و مُخططات قاعدة البيانات بمرور الزمن بطريقة سهلة و متناسقة. ونقوم بإستعمال لُغة Ruby موحدة المجال DSL بدلًا من كتابة العديد من سطور SQL مما يسمح للتغيرات بأن تكون مُستقلة تمامًا عن قاعدة البيانات و لا تؤثر عليها سلبًا. يُمكنك التفكير في التهجير على أنه عمل تحديث جديد لقاعدة البيانات. ففي بداية الإسكيما تكون فارغة، ثُم بعد ذلك تقوم عمليات التهجير بإضافة الجداول، العواميد، أو المدخلات. و Active Record يقوم بعمل هذه التحديثات على الإسكيما من الحالة التي كانت عليها قبل التحديث (أو في النُسخة السابقة). كما سيقوم Active Record بتحديث ملف db/schema.rb ليُناسب بناء قاعدة البيانات بعد التحديث. مثال على التهجير class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end في المثال السابق، قُمنا بعملية تهجير حيث أضفنا جدول جديد يُسمى products يحتوي على عامود نصي يُسمى name و عامود آخر يُسمى description. وسيتم إضافة عامود مفتاح أساسي يُسمى id ضمنيًا، لأنه –كما علمنا من قبل- المفتاح الرئيسي الإفتراضي لجميع نماذج Active Record. آما أمر timestamps يُضيف عامودين، صُنع في created_at وتم تحديثه في updated_at. و تلك العواميد الخاصة يتم إدارتها و تحديثها تلقائيًا بواسطة Active Record. لاحظ أن عملية التغير التي نقوم بها تتحرك أمامًا مع مرور الوقت، فقبل تشغيل التهجير run migration لن يوجد لدينا ذلك الجدول، و بمجرد تشغيل كود التهجير سنحصل على الجدول، و العكس صحيح! فإن Active Record يُمكنه عكس عملية التهجير بإستخدام أمر العودة Roll back و سيُحذف الجدول الجديد. بعض قواعد البيانات تدعم الصفقات Transactions* مع التغيرات التي تحدث للإسكيما. خاصية التهجير مُحاطة بتلك الصفقات Transactions. و لكن إذا كانت قاعدة البيانات لا تسمح بالصفقات، فإن التهجير سيقوم بإلغاء الأجزاء التي لم تطرأ عليها تلك التغيرات. class ChangeProductsPrice < ActiveRecord::Migration[5.0] def change reversible do |dir| change_table :products do |t| dir.up { t.change :price, :string } dir.down { t.change :price, :integer } end end end end و بدلًا من إستخدام أمر change ستستخدم أوامر up و down. class ChangeProductsPrice < ActiveRecord::Migration[5.0] def up change_table :products do |t| t.change :price, :string end end def down change_table :products do |t| t.change :price, :integer end end end 2 إنشاء التهجير 2.1 إنشاء تهجير مُستقل و مُنفرد بنفسه إن عمليات التهجير تكون مُخزنة داخل ملف يوجد في db/migrate، و يوجد ملف لكل عملية تهجير حدثت على فئة مُعينة class. إسم الملف يتخذ الصيغة الآتية YYYYMMDDHHMMSS_creat_products.rb و هي تحتوي على صيغة الوقت التي حدث فيه التهجير، ثم علامة “_” ثم إسم عملية التهجير. إن هذا الإسم يجب أن يُطابق إسم الفئة class التي لحق بها, فعلى سبيل المثال، إن كان إسم التهجير 20080906120000_creat_products.rb فيجب أن يُعرف فئة class إسمها CreatProducts. آما بالنسبة للوقت المُلحق بالإسم، فإن Rails تستعمله لترتيب التهجيرات و ترتيب كيفية عملها. لذلك إذا قُمت بنسخ تهجير ما من تطبيق آخر. فيجب أن تلتفت لهذا الأمر. و حساب الوقت بالضبط أي الدقيقة و الثانية لأمرٌ صعب. لذلك يوفر Active Record مولد لصُنع هذا الأمر. $ bin/rails generate migration AddPartNumberToProducts سيصنع الكود أعلاه تهجير فارغ، لكنه مُسمى بتسمية صحيحة. class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change end end إذا كان التهجير على هيئة AddXXXToYYY أو RemoveXXXfromYYY أي يحتوي على أوامر إضافة أو حذف و يحتوي أيضًا على عواميد تحتوي على الأسماء names و الأنواع types، فسيتم صُنع عواميد مُناسبة مثل add_column و remove_column. و الكود أدناه مثال على ذلك. $ bin/rails generate migration AddPartNumberToProducts part_number:string سينتج عنه: class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string end end و يُمكنك أيضًا عمل فهرسة للعواميد الجديدة. $ bin/rails generate migration AddPartNumberToProducts part_number:string:index سينتج عنه: class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_index :products, :part_number end end و بنفس الطريقة يُمكنك عمل تهجير ليزيل عامود بإستخدام هذا الكود. $ bin/rails generate migration RemovePartNumberFromProducts part_number:string سينتج عنه: class RemovePartNumberFromProducts < ActiveRecord::Migration[5.0] def change remove_column :products, :part_number, :string end end كما أنك لست مُقيد بنوع واحد من العواميد لإنتاجه، مثال على ذلك $ bin/rails generate migration AddDetailsToProducts part_number:string price:decimal سينتج عنه class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_column :products, :price, :decimal end end لاحظ هُنا تحديد نوع البيانات الذي قُمنا به أما إذا كان التهجير يتخذ صيغة CreateXXX و مُتبع بقائمة بأسماء و أنواع العواميد، فإن التهجير سيقوم بعمل جدول XXX يحتوي على تلك العواميد، مثال: $ bin/rails generate migration CreateProducts name:string part_number:string و هذا سينتج عنه: class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name t.string :part_number end end end وكالعادة، فإنه يُمكنك تعديل ما يتم توليده من قبل التهجير عن طريق الإضافة و الحذف. عند طريق التعديل على هذا الملف db/migrate/YYYMMDDHHMMSS_add_details_to_products.rb، على سبيل المثال: $ bin/rails generate migration AddUserRefToProducts user:references و هذا سينتج عنه: class AddUserRefToProducts < ActiveRecord::Migration[5.0] def change add_reference :products, :user, foreign_key: true end end هذا التهجير سوف يصنع عامود user_id مع فهرس مُناسب. و هُناك أيضًا العديد من الخيارات الأخرى لدالة add_reference سنتطرق إليها فيما بعد. هذا الكود سيولد جداول مُدمجة Join tables إذا إستعملت JoinTable كجزء من إسم التهجير، مثال: الجداول المُدمجة Join Tables هي تجميع لعدد معين من العواميد من جدول واحد أو أكثر من جدول. $ bin/rails g migration CreateJoinTableCustomerProduct customer product و هذا سوف ينتج التهجير الآتي: class CreateJoinTableCustomerProduct < ActiveRecord::Migration[5.0] def change create_join_table :customers, :products do |t| # t.index [:customer_id, :product_id] # t.index [:product_id, :customer_id] end end end 2.2 موالدات النماذج إن مولدات النماذج و الإسكافولد Model and Scaffold Generators يُمكنها صُنع التهجير المُناسب لإضافة نموذج جديد. حيث سيحتوي التهجير على التعليمات اللازمة لصناعة الجداول المُناسبة. فإذا حددت العواميد التي تريدها، سيتم إضافة سطور الكود اللازمة لإضافة تلك العواميد، مثال: $ bin/rails generate model Product name:string description:text و ذلك سوف يصنع هذا التهجير: class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end و بالطبع يُمكنك إضافة أي عدد تشاءه 2.3 تعدية المُعدلات يُمكنك كتابة كود لتعدي المُعدلات Modifiers لأنهم قد تُقيدك. فإذا قُمت بتشغيل هذا الكود: $ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:references{polymorphic} سوف يُنتج هذا التهجير: class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :price, :decimal, precision: 5, scale: 2 add_reference :products, :supplier, polymorphic: true end end سنتابع في الدروس القادمة بقية أجزاء دليل تعليم Active Record Migrations.. المصدر: توثيقات Ruby on Rails.
  8. في الدرس السابق ألقينا نظرة عامة على تهجير Active Record وكيفية إنشائه وسنتابع في هذا الدرس تعلّم كيفية كتابة أوامره عبر صنع الجداول وتعديلها وغيرها. 3 كتابة أوامر التهجير بمجرد استخدامك للمولدات لتوليد التهجير، فسيتحتم عليك البدء بالعمل الحقيقي و كتابة أوامر التهجير. 3.1 صُنع الجداول إن دالة create_tables هي الأساسية لصنع الجداول، و لكن في مُعظم الأوقات ستقوم المولدات بإنتاج تلك الجداول كما رأينا في الأمثلة السابقة. مثال على صُنع جدول create_table :products do |t| t.string :name end وهذا سوف يصنع جدول يُسمى products يحتوي على عامود نصي يُسمى name (وعامود مُفتاح رئيسي يُسمى id يتولد تلقائيًا كما ناقشنا من قبل) و تلقائيًا، ستنتج دالة create_table ستصنع عامود مُفتاح رئيسي id. يُمكنك تغيير إسم هذا المُفتاح باستخدام أمر primary_key: (ولا تنسَ أن تقوم بهذا التغيير في بقية النماذج models)، كما يُمكنك إلغاء وجود ذلك المُفتاح إن لم تحتاجه بإستخدام أمر id: false. أما إذا أردت تعديل تخطي بعض الخيارات الأخرى لقاعدة البيانات، فيُمكنك استخدام سطور SQL في خيار options: وعلى سبيل المثال: create_table :products, options: "ENGINE=BLACKHOLE" do |t| t.string :name, null: false end وهذا الكود سوف يُضيف ENGINE = BLACKHOLE إلى سطور SQL المسؤولة عن صُنع الجداول ( وفي حالة استعمال MySQL أو MariaDB، فإن الكود المُستخدم هو ENGINE = InnoDB). يُمكنك تعدي خيار comment: باستخدام أي وصف للجدول مُخزن في قاعدة البيانات نفسها و سيتم ظهور هذا الوصف أدوات إدارة قاعدة البيانات، مثل MySQL Workbench أو PgAdmin III. كما أنه من المُفضل لتوضيح التعليقات comments في التهجيرات المُستخدمة بتطبيقات قواعد البيانات الكبيرة. حيث سُتساعد من يقرأ قاعدة البيانات لفهم نماذج البيانات و عمل التوثيقات اللازمة لقاعدة البيانات. 3.2 صُنع الجداول المُدمجة إن دالة التهجير creat_join_table تقوم بصُنع جداول مُدمجة (HABTM-has and belongs to many) و التي كما عرفناها سابقًا عبارة عن عدة عواميد من جدول واحد أو جداول مُختلفة. مثال: create_join_table :products, :categories و هذا سوف يصنع جدول categories_products يحتوي على عامودين يُسميان category_id و product_id. وهذه العواميد تلقائيًا تُعطي قيمة false للأمر null: و يُمكن تخطي هذا الأمر بالتعديل على الخيار column_options:، مثال: create_join_table :products, :categories, column_options: { null: true } إسم الجدول المُدمج سيتكون من إسم العواميد المكونة له طبقًا لترتيبها الأبجدي، و لتغير ذلك يُمكنك إستخدام قواعد التسمية المُستخدمة في المقال السابق و بدالة table_name:، مثال: create_join_table :products, :categories, table_name: :categorization مما سينتج عن الكود السابق جدول يُسمى categorization. كما أن دالة create_join_table يُمكنها إضافة الفهارس و العواميد الإضافية (و لا يتم عمل ذلك بشكل تلقائي إذ يجب عليك تحديد الفهارس أو العواميد الإضافية) مثال: create_join_table :products, :categories do |t| t.index :product_id t.index :category_id end 3.3 تغيير الجداول إن دالة تغيير الجداول change_table مُشابهة لدالة صُنعها create_table عدا أن الثانية تُستخدم لتغير جداول موجودة مُسبقًا. و تعمل بنفس الطريقة و لكنك لن تحتاج إعادة تعريف كُل شيئ لأنه سيأخُذها من الجدول المُتغير منه. مثال على ذلك: change_table :products do |t| t.remove :description, :name t.string :part_number t.index :part_number t.rename :upccode, :upc_code end الكود السابق سوف يحذف العواميد description و columns. و سيصنع عامود نصي part_number وسيضيف فهرس إليه، و سيقوم أيضًا بإعادة تسمية عامود upccode. 3.4 تغيير الأعمدة توفر Rails أمر التهجير change_column و الذي يؤدي دور الأوامر remove_column و add_column. change_column :products, :part_number, :text الكود أعلاه سيُغير نوع العامود part_number في الجدول products ليُصبح حقل نصوص texts. لاحظ أن الدالة change_column لا يُمكن عكسها. وبجانب دالة change_column يوجد دوال change_column_null و change_column_default التي تُستخدم تحديدًا لتغيير القيم الإفتراضية default values للعامود. change_column_null :products, :name, false change_column_default :products, :approved, from: true, to: false الكود أعلاه سيقوم بتغيير الحقل name: في جدول products ليُصبح من نوع NOT NULL و القيمة الإفتراضية لحقل approved: من true إلى false. 3.5 مُعدلات الأعمدة إن مُعدلات الأعمدة Column Modifiers يُمكن أن تُطبق عند صُنع أو تغيير العواميد: limit : يقوم بتحديد أقصى مساحة للحقول من الأنواع string/text/binary/integer Precision : يُحدد دقة الأرقام العشرية بتحديد عدد الوحدات في الرقم الواحد قبل العلامة العشرية Scale : يُحدد دقة الأرقام العشرية بتحديد عدد الوحدات في الرقم الواحد بعد العلامة العشرية Polymorphic : يُضيف عامود type إلى رابطة belong_to Null : تسمح بإستخدام قيمة NULL أو عدم إستخدامها بالعامود. Default : يُتيح لك بتعيين قيمة إفتراضية بالعامود. مع مُلاحظة أنه عند إستخدامك قيمة ديناميكية (مثل التاريخ، فإنه سيتم تعيين القيمة الإفتراضية للتاريخ الذي تم صُنع التهجير عنده. Index : يُضيف فهرس للعامود Comment : يُضيف تعليق للعامود بعض المحولات adapters تدعم بعض الخيارات الإضافية: يُمكنك رؤية التوثيقات الخاصة بتلك المحولات لمعرفة المزيد عن هذا الأمر. 3.6 المفاتيح الأجنبية على الرغم من عدم إضطرارك لإضافة المفاتيح الأجنبية Foreign Keys إلا أنه يُمكنك فعل ذلك لضمان السلامة المرجعية لقاعدة البيانات. add_foreign_key :articles, :authors هذا سوف يُضيف مفتاح أجنبي جديد إلى عامود author_id في جدول articles. والمفتاح سيرمز إلى عامود id في جدول authors. أما إذا كانت أسماء الأعمدة لا يُمكن إشتقاقها من أسماء الجداول، فيُمكنك إستخدام الأوامر column: و primary_key:. إن Rails أيضًا ستقوم بتوليد إسم إفتراضي لكُل مُفتاح أجنبي، سيبدأ بـ fk_rails_ ثُم سيُتبع بعشرة حروف التي ستتولد تلقائيًا من أمر from_table و column. و يُمكنك أيضًا إستخدام name: لتعيين إسم مُختلف إذا أردت. حذف المفاتيح الأجنبية لهو أمر سهل أيضًا: # let Active Record figure out the column name remove_foreign_key :accounts, :branches # remove foreign key for a specific column remove_foreign_key :accounts, column: :owner_id # remove foreign key by name remove_foreign_key :accounts, name: :special_fk_name 3.7 إستخدام دالة التغيير change إن دالة التغيير change هي الطريقة الأساسية لكتابة أوامر التهجير (حيث كما علمنا فإن التهجير هو عبارة عن تغيير في الأساس). و في أغلب الحالات، فإن Active Record يُمكنه عكس تلك التهجيرات. حاليًا، يُمكنك إستخدام دالة التغيير على الدوال الآتية: add_column add_foreign_key add_index add_reference add_timestamps change_column_default (must supply a :from and :to option) change_column_null create_join_table create_table disable_extension drop_join_table drop_table (must supply a block) enable_extension remove_column (must supply a type) remove_foreign_key (must supply a second table) remove_index remove_reference remove_timestamps rename_column rename_index rename_table دالة change_table يُمكن عكسها في حالة أن الجدول لا يقوم بإستدعاء دالة change أو change_default أو remove. ودالة remove_column أيضًا يُمكن عكسها إذا قُمت بتوفير نوع العامود كعامل ثالث موفرًا أيضًا باقي خيارات العامود. وبدون ذلك فإن Rails لن تتمكن من عمل العامود عند إستخدام الأوامر العكسية: remove_column :posts, :slug, :string, null: false, default: '', index: true أما إذا أُضطررت لإستعمال دوال أخرى فعليك إستعمال أمر reversible أو كتابة أوامر up و down بدلًا من إستعمال دالة change. 3.9 إستعمال أمر العكس reversible إن التهجيرات المُعقدة تتطلب مُعالجة لا يستطيع Active Record عكسها. لذلك يُمكنك إستخدام أمر reversible لتحديد ماذا يحدث عند تشغيل التهجير و ماذا يحدث عند عكسه، مثال: class ExampleMigration < ActiveRecord::Migration[5.0] def change create_table :distributors do |t| t.string :zipcode end reversible do |dir| dir.up do # add a CHECK constraint execute <<-SQL ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5) NO INHERIT; SQL end dir.down do execute <<-SQL ALTER TABLE distributors DROP CONSTRAINT zipchk SQL end end add_column :users, :home_page_url, :string rename_column :users, :email, :email_address end end إن إستعمال دالة reversible سوف تتأكد من تنفيذ excute سطور الكود في الترتيب الصحيح. ففي المثال السابق، سيتم عكس التهجير بالترتيب، فسيتم تشغيل البلوك down بعد حذف عامود home_page_url و قبل إضافة جدول distributors. في بعض الأحيان، لن يُمكن عكس عملية التهجير. لأنه –وعلى سبيل المثال- قد تؤدي إلى تدمير بعض البيانات. في هذه الحالات يُمكنك إستخدام أمر ActiveRecord::IrreversibleMigration في البلوك down. و إذا حاول أحد عكس التهجير فإن رسالة خطأ error message ستُخبره بأن هذا الأمر غير مُمكن. 3.10 إستعمال دوال up/down يُمكنك إستعمال الأسلوب القديم للتهجير بدوال up و down بدلًا من إستعمال دالة change. فإن دالة up يجب أن تصف فيها التغيرات التي تريد عملها في الإسكيما. و دالة down ستقوم بعكس التغيير الذي قامت به دالة up. بإختصار، فإنك إذا قُمت بتطبيق دالة up ثُم بعد ذلك دالة down فيجب ألا يحدث تغيير على قاعدة البيانات. و من المُرجح أن تكون التغييرات التي ستقوم بها دالة up يُمكن عكسها بسهولة. المثال السابق ذكره في قسم (3.9 أمر العكس) سيؤدي وظيفته هذا المثال: class ExampleMigration < ActiveRecord::Migration[5.0] def up create_table :distributors do |t| t.string :zipcode end # add a CHECK constraint execute <<-SQL ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5); SQL add_column :users, :home_page_url, :string rename_column :users, :email, :email_address end def down rename_column :users, :email_address, :email remove_column :users, :home_page_url execute <<-SQL ALTER TABLE distributors DROP CONSTRAINT zipchk SQL drop_table :distributors end end في بعض الأحيان، لن يُمكن عكس عملية التهجير. لأنه –وعلى سبيل المثال- قد تؤدي إلى تدمير بعض البيانات. في هذه الحالات يُمكنك إستخدام أمر ActiveRecord::IrreversibleMigration في البلوك down. و إذا حاول أحد عكس التهجير فإن رسالة خطأ error message ستُخبره بأن هذا الأمر غير مُمكن. 3.11 عكس تهجيرات سابقة يُمكنك إستعمال دالة revert في Active Record لعكس التهجيرات السابقة: require_relative '20121212123456_example_migration' class FixupExampleMigration < ActiveRecord::Migration[5.0] def change revert ExampleMigration create_table(:apples) do |t| t.string :variety end end end دالة reverse أيضًا تقبل عكس مجموعة block من الأوامر. حيث إن هذا سيكون مُفيد في عكس أجزاء مُعينة فقط من تهجير قُمت به مُسبقًا. على سبيل المثال، إفترض أنك إحتحجت إستخدام تحقيقات السحل النشط على التهجير ExampleMigration في مكان إستخدام CHECK لتأكيد الرقم البريدي zipcode. class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration[5.0] def change revert do # copy-pasted code from ExampleMigration reversible do |dir| dir.up do # add a CHECK constraint execute <<-SQL ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5); SQL end dir.down do execute <<-SQL ALTER TABLE distributors DROP CONSTRAINT zipchk SQL end end # The rest of the migration was ok end end end إن نفس التهجير يُمكن كتابته بدون إستخدام revert و لكن ستقوم بعمل المزيد من الخطوات: عكس ترتيب الأمر create_table و الأمر reversible، و إستبدال الأمر create_table بالأمر drop_table، و أخيرًا ستقوم بعكس أمرين up و down و العكس صحيح. هذا كُله يتم عمله بواسطة دالة revert. سنتابع في الدرس التالي ما تبقى من هذا الدليل التعليمي حول تهجير Active Record المصدر: توثيقات Ruby on Rails.
  9. تقدّمت منذ فترة بطلب إلى مطوّري إطار عمل Laravel لإضافة مكتبة JavaScript مشابهة لتلك الموجودة في إطار العمل Ruby on Rails. إذ تقدّم المكتبة المتوفرة في إطار Rails مجموعة من الخصائص المفيدة التي يمكن إضافتها إلى شيفرة HTML للحصول على المزيد من الوظائف التي تسهّل على المطوّر أداء الكثير من المهامّ الشائعة. أجري نقاش حول هذا الموضوع وتمخّض النقاش عن الاقتراح التالي: إما أن يصار إلى إنشاء حزمة Composer من طرف ثالث، أو أن المكتبة التابعة لإطار العمل Rails صالحة للاستخدام في Laravel. وفقًا للمقترح السابق قرّرت استخدام مكتبة UJS التابعة لـ Rails في أحد مشاريعي ورحت أتفحّص الطرق التي يمكن من خلالها استخدام نفس الخصائص في الإصدار الرابع من Laravel. ولسوء الحظ لا يمتلك Laravel نفس التوابع المساعدة الموجودة في Rails والمسؤولة عن أداء وظائف معيّنة، ولكن لا زال بالإمكان تفعيل هذه الوظائف في المكان الذي ترغب فيه باستخدام الخواص الملائمة، ومن المؤكّد أن هذه الوظائف ستعمل حسب المطلوب في أي إطار عمل آخر. البداية بعد أن تربط مكتبتي jQuery و Rails UJS مع ملف HTML ستحتاج إلى إجراء بعض التعديلات السريعة. سنحتاج أوّلًا إلى إعداد مكتبة UJS للعمل مع رمز CSRF على فرض أنّك تستخدم هذه الرموز في تطبيقاتك (أنصحك باستخدامها)، لذا سنضع وسمي <meta> في بداية المستند: <meta name="csrf-param" content="_token"> <meta name="csrf-token" content="{{ csrf_token() }}"> يخبر الوسم الأول مكتبة UJS بالاسم الذي يجب استخدامه لرمز CSRF عند إنشاء الطلب، أما الوسم الثاني فيخبر المكتبة بوسم CSRF الذي يجب استخدامه. يستخدم Laravel افتراضيًا token_ كاسم للمعامل (ألق نظرة على ملف filters.php للمزيد من المعلومات)، أما التابع ()csrf_token فيضيف رمز الحماية إلى الطلب. خاصية data-method هذه الخاصية مفيدة جدًّا عند استخدام للروابط، إذ تخطف مكتبة UJS النقرة على الرابط وتنشئ طلبًا باستخدام فعل Http الذي نقدّمه إلى الخاصية. مثال: <a href="posts/1" data-method="delete" rel="nofollow">Delete this post</a> عند النقر على الرابط، يتم إنشاء طلب من نوع DELETE بدلًا من الطلب الاعتيادي GET والذي ينشأ من مثل هذه الروابط، وبهذا يمكنك إجراء أحداث بنمط RESTful في تطبيقك دون الحاجة إلى الاستمارات. إن كنت ترغب في إنشاء روابط مشابهة لهذا الرابط في Laravel فإليك المثال التالي والذي نستخدم فيه دالة link المساعدة: {{ link_to_route('posts.destroy', 'Delete this post', $post->id, ['data-method' => 'delete', 'rel' => 'nofollow']) }} جدير بالذكر أنّه يجب إضافة الخاصية rel="nofollow" عند استخدام هذه الدالة المساعدة لضمان عدم فهرسة محركات البحث لهذا الرابط. خاصية data-confirm هذه الخاصية مفيدة للتحقّق من الموافقة على إجراء الحدث قبل الاستمرار، وتتم عملية التحقّق هذه بواسطة مربع التنبيه الذي يظهر بواسطة الدالة confirm() في JavaScript، ويمكن استبدال مربّع التنبيه هذا بآخر ذي شكل أجمل حسب الرغبة. تعمل هذه الخاصية بصورة جيّدة مع المثال السابق، فإن كان الرابط أو الزرّ يؤدّي إلى حذف شيء ما فيستحسن حينئذٍ التحقّق من الرغبة في القيام بذلك قبل إجراء عملية الحذف. <a href="posts/1" data-method="delete" data-confirm="Are you sure you want to delete this post?" rel="nofollow">Delete this post</a> إن ألغى المستخدم صندوق التأكيد هذا لن يتم تنفيذ الحدث، والعكس بالعكس. جدير بالذكر أنّك لست ملزمًا باستخدام هذه الطريقة مع خاصية data-method بل يمكن استخدامها مع أي حدث ترغب في التحقق منه قبل إجراءه. خاصية data-disable قد ترغب أحيانًا في تعطيل زرّ معين بعد النقر عليه مباشرة لإتاحة الفرصة لاكتمال الطلب وفي حال فشل الطلب تعيد تفعيل الزرّ مرة أخرى. تتيح لك مكتبة UJS القيام بهذا الأمر بواسطة خاصّية data-disable. <input type="submit" value="Save post" data-disable> هكذا سيتم تعطيل الزر بعد النقر عليه وستمنع المستخدم من النقر المستمر على الزر وإنشاء 10 سجلات جديدة في قاعدة البيانات. بالنسبة لمستخدمي Laravel يمكن الاستفادة من هذه الخاصّية باستخدام منشئ الاستمارات form builder. {{ Form::submit('Save post', ['data-disable']) }} خاصّية data-disable-with يمكن التوسع في المثال السابق وذلك بتقديم بعض المعلومات المفيدة للمستخدم، فعند تعطيل الزرّ يظهر نصّ يخبر المستخدم بأنّ الطلب في طور الإنشاء في الخلفية. <input type="submit" value="Save post" data-disable-with="Saving this post..."> والآن عندما يكون الزرّ معطلًا، يتم استبدال النص بعبارة تدلّ على أن النقر على الزرّ قد أدى إلى حدوث شيء ما في الخلفية. خاصّية data-remote عند إضافة الخاصّية data-remote إلى الاستمارة، تقوم مكتبة UJS بإرسال الاستمارة كطلب Ajax. وستأخذ المكتبة كل شيء بالحسبان (طريقة الإرسال، التحقق من الموافقة على الإجراء… الخ) قبل إرسال الاستمارة. <form action="posts" method="post" data-remote> <!-- Form goes here. --> </form> ويمكن التعامل مع الاستجابة الواردة بعد نجاح الطلب بكل سهولة، فإن استجاب المتصفّح بواسطة JavaScript فستنفّذ الاستجابة فور اكتمال الطلب. على سبيل المثال، قد تختار إعادة توجيه المستخدم بعد تنفيذ طلب للحذف: window.location.href = '{{ route('posts.index') }}'; أو لنقل مثلًا أنّك تستخدم رابطًا لحذف مقالة من صفحة تتضمن قائمة بالمقالات المتوفّرة في المدونة، وترغب في إخفاء المقالة بعد حذفها مباشرة. <a href="{{ route('posts.destroy', $post->id) }}" data-method="destroy" data-confirm="Delete this post?" rel="nofollow" data-remote>Delete this post</a> يمكن استخدام الشيفرة التالية لإخفاء المقالة: $('#post-{{ $post->id }}').fadeOut(); وهكذا، تُحذف المقالة من قاعدة البيانات والعرض في آن معًا. خاصّية data-type: إن استخدمت الخاصّية السابقة data-remote يمكنك كذلك استخدام خاصية data-type لتحديد هيئة البيانات التي يتم إرسالها إلى الخادوم. فعلى سبيل المثال يمكن إرسال الاستمارة على هيئة Json: <form data-type="json"> الأحداث البعيدة عند استخدام خاصّية data-remote يمكن الوصول إلى الأحداث المختلفة في طلب Ajax لتأدية بعض المهامّ الإضافية. إذ تطلق مكتبة UJS عددًا من الأحداث المساعدة خلال دورة حياة الطلب والتي من شأنها أن تساعد في بناء تطبيقات أكثر تعقيدًا. ajax:before ينطلق هذا الحدث قبل حدوث أي شيء، وإيقاف هذا الحدث يؤدي إلى إلغاء الطلب. ajax:beforeSend: ينطلق هذا الحدث قبل إرسال طلب Ajax مباشرة، وإيقاف الحدث يؤدي إلى إلغاء الطلب. ajax:send: ينطلق عند إرسال طلب Ajax. ajax:success: ينطلق بعد اكتمال الطلب بنجاح. ajax:error: ينطلق بعد فشل إرسال الطلب. ajax:aborted:required: ينطلق في حال وجود حقول إلزامية فارغة في الاستمارة، وفي حال إيقاف الحدث يتم إرسال الاستمارة. ajax:aborted:file: ينطلق في حال وجود حقول ملفات غير فارغة في الاستمارة، ويتم تجاهل الطلب في حال توقّف الحدث. آمل أن يكون هذا الموضوع مفيدًا لكل من يرغب في استخدام مكتبة UJS التابعة لإطار العمل Ruby on Rails مع إطارات العمل الأخرى مثل Laravel، إذ أرى أن هذه المكتبة مفيدة ولا يجوز أن تكون حكرًا على مستخدمي إطار Rails. ترجمة - وبتصرّف - للمقال Using Rails UJS in Laravel 4 (or any other framework) لصاحبه Dwight Conrad Watson.
  10. سنتابع في هذا الدرس دليل Active Record، حيث تعلمنا في الدرس السابق ماهية Active Record، استيراد الأكواد من اللغات الأخرى والنماذج. وسنتابع في هذا الدرس بقية دليل Active Record. 4 تخطي طُرق التسمية الخاصة بـ Rails ماذا تفعل إن إحتجت أن تستعمل طريقة أخرى للتسمية غير المُتبعة بـ Rails؟ لا يوجد مُشكلة حيث يُمكنك تخطي قواعد التسمية بسهولة. إن سجل التطبيق ApplicationRecord يستلهم من Active Record ActiveRecord::Base و هذا الأمر يُعطينا العديد من الطرق التي يُمكن إستخدامها لتخطي قواعد التسمية. فيُمكنك –مثلًا- إستعمال هذا الأمر ActiveRecord::Base.table_name= لتحديد الإسم المُناسب للجدول الذي تُريد إنشائه. class Product < ApplicationRecord self.table_name = "my_products" end إذا فعلت ذلك فيجب عليك أن تعرف إسم الفئة class التي تحمل الثوابت (my_products.yml) يدويًا بإستعمال هذا الأمر set_fixture_class في تعريف الاختبار الخاص بك test definition. class ProductTest < ActiveSupport::TestCase set_fixture_class my_products: Product fixtures :my_products ... end ومن المُمكن أيضًا تخطي تسمية الأعمدة التي تُستعمل كمفاتيح أساسية باستعمال الأمر: ActiveRecord::Base.primary_key=. class Product < ApplicationRecord self.primary_key = "product_id" end 5 قراءة و كتابة البيانات CRUD إن CRUD هي إختصار الأربع كلمات الآتية: Create, Read, Update و Delete. Active Record يقوم –تلقائيًا- بصنع الدوال التي تسمح لتطبيقٍ ما بقراءة و تعديل البيانات الموجودة داخل جداولها. 5.1 أمر Create يُمكن عمل كائنات objects في Active Record باستخدام رمز الشباك hash أو كما يُمكن تعيين خصائصها يدويًا بعد تعريفها. فإن دالة new سوف تُخرج كائن جديد. بينما دالة create سوف تُخرج كائن جديد و تقوم بحفظه في قاعدة البيانات. على سبيل المثال، بافتراض وجود نموذج model يُسمى User و خصائصه name و occupation، فإن دالة method سوف تقوم بصُنع النموذج و حفظه بقاعدة البيانات. user = User.create(name: "David", occupation: "Code Artist") بينما إستعمال دالة new سوف تقوم بصُنع الكائن object و لكن بدون حفظه user = User.new user.name = "David" user.occupation = "Code Artist" و عند كتابة user.save سوف يُحفظ الكائن في قاعدة البيانات. وبافتراض وجود block مُسبق، فإن كلاً من الأمرين creat و new سوف يقومان بإخضاع ذلك الكائن للبلوك block في البداية. user = User.new do |u| u.name = "David" u.occupation = "Code Artist" end 5.2 أمر Read إن Active Record يوفر واجهة برمجة تطبيقات قوية من أجل الولوج للبيانات الموجودة داخل قاعدة البيانات. في الأكواد التالية، سيكون هُناك عدة أمثلة للولوج إلى قواعد البيانات بإستخدام Active Record. # return a collection with all users users = User.all # return the first user user = User.first # return the first user named David david = User.find_by(name: 'David') # find all users named David who are Code Artists and sort by created_at in reverse chronological order users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc) 5.3 أمر Update يُمكنك إعادة التعديل على خصائص الكائن object و حفظ التغييرات في قاعدة البيانات. user = User.find_by(name: 'David') user.name = 'Dave' user.save و يوجد طريق مُختصر لعمل التحديثات أيضًا بإستخدام hash mapping. user = User.find_by(name: 'David') user.update(name: 'Dave') و هذه الطريقة مُفيدة إذا أردت أن تُحدث العديد من الخصائص في وقتٍ واحد. و لكن إذا أردت تحديث العديد من السجلات مرة واحدة، فيُمكنك إستخدام أمر update_all: User.update_all "max_login_attempts = 3, must_change_password = 'true'" 5.4 أمر Delete يُمكنك حذف أي كائن بعد تخزينه بقاعدة البيانات، باستخدام تلك الأكواد: user = User.find_by(name: 'David') user.destroy 6 التوثيق Validation إن Active Record يسمح لك بعمل توثيق لحالة نماذج البيانات (models) قبل إدخالها في قواعد البيانات. هناك العديد من الطرق لتوثيق النماذج قبل إدخالها قواعد البيانات مثل التأكد من أن قيمة المُدخل ليست فارغة و أنها غير موجودة بالفعل في قاعدة البيانات أو أنها تتبع تنسيقًا معينًا و الكثير من الأمور التي يمكن استخدامها للتوثيق. إن التوثيق أمر في غاية الأهمية عند تثبيت البيانات داخل قاعدة البيانات. لذلك فإن الدوال save وupdate قد يُخرجوا لنا false عندما يفشل التوثيق و لن يقوما بأية عمليات في قاعدة البيانات. و تلك الدوال يوجد دوال مُناقضة لها (!save و !update) فيُخرجا إستثناء ActiveRecord::RecordInvalid في حالة فشل التوثيق، مثال على ذلك: class User < ApplicationRecord validates :name, presence: true end user = User.new user.save # => false user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank 7 دوال الإستدعاء Callbacks إن دوال الإستدعاء داخل Active Record تسمح لك بإرفاق الكود لحدث مُعين يتكرر في دورة تكرار النموذج model الخاص بك. وهذا الأمر يسمح لك بإضافة أفعال إلى نماذجك مشروطة بحدوث أحداث مُعينة. فعلى سبيل المثال: عند صُنع سجل جديد يقوم Active Record بتحديث آخر أو حذفه، إلخ. 8 التهجير Migrations إن Rails توفر لغة مُحددة المجال domain-specific لإدارة قواعد البيانات تُسمى بالتهجير. إن أوامر التهجير مُخزنة بملفات يتم إخراجها لأي قاعدة بيانات يتم دعمها من Active Record باستخدام الأمر rake، ها هو كود تهجير يقوم بإنشاء جدول: class CreatePublications < ActiveRecord::Migration[5.0] def change create_table :publications do |t| t.string :title t.text :description t.references :publication_type t.integer :publisher_id t.string :publisher_type t.boolean :single_issue t.timestamps end add_index :publications, :publication_type_id end end إن Rails تتقفى آثر الملفات التي سُلمت لقاعدة البيانات و توفر إمكانية إستعادة تلك القاعدة rollback. لذلك فلإنشاء جدول يجب عليك كتابة الأمر في rails db:migrate و إستعادته بإستخدام db::rollback. لاحظ أن الكود المُستخدم أعلاه يُمكن إستخدامه بأي قاعدة بيانات مثل MySQL و PostgreSQL و Oracle و غيرها. من توثيقيات Ruby on Rails
  11. سنتابع في هذا الدرس من سلسلة تعليم إطار العمل Ruby on Rails حيث سنتعرف على Active Record. 1 ما هو Active Record؟ Active Record هو جزء من النمط البرمجي MVC -شرحها بأسفل المقال- و هو المسئول عن جزء البيانات. حيث إن Active Record هو الجزء المسؤول عن عرض البيانات و خوارزميتها. فيسهل Active Record صنع و إستخدام عناصر البيانات التي تتطلب مساحة دائمة في قاعدة البيانات database. إن تطبيق نمط Active Record يُعتبر نفسه وصف و جزء من الـ ORM – يوجد تعريف بالأسفل- . ** MVC- Model View Controller - هو نمط تم اعتماده كطريقة للبرمجة حيث يعتمد هذا النمط في الاساس على عزل ما هو مرأي للمستعمل (user interface) عن ما يتعلق بالبيانات(data) و طرق استخدامها، حيث ينفصل الجزء المختص بالبيانات عن الواجهة و يمكن عمل أكتر من مطور على المشروع بسلاسة و دون تعارض. من مثال المنصات التي تعمل بتلك الطريقة هي Laravel ** ORM Object- Relational- Object هو تكنيك برمجي مستخدم لتحويل البيانات بين الأنظمة غير المتوافقة عن طريق تحويل الصفوف و الأعمدة إلى كائنات بإستخدام البرمجة الشيئية. 1.1 نمط Active Record لقد عرف مارتن فويلر Active Record في كتابه Patterns of Enterprise Application Architecture وفي هذا التعريف أوضح ماهيته. في Active Record، يحمل الكائن object كلاً من البيانات الدائمة و العمليات التي ستتم عليها. Active Recordيعمل على ربط منطق الولوج إلى البيانات بالكائن بحيث يُعلم المُستخدمين كيفية إدخال و إخراج البيانات من قاعدة البيانات. 1.2 رسم العلاقات بين الكائنات Object Relational Mapping يُرمز إلى هذا المصطلح بالإختصار ORM ، وهو تقنية تربط بين الكائنات المُختصة بتطبيقٍ ما مع جداول في نظام إدارة قواعد البيانات المتصلة. بإستعمال ORM يمكن تخزين و إستخدام خصائص و علاقات الكائنات في التطبيق بدون الحاجة إلى كتابة أوامر SQL بل بطريقة مُباشرة و بإستخدام أكواد أقل. 1.3 Active Record كمنصة ORM Active Record يوفر لنا العديد من الإمكانيات، أهمها: تمثيل النماذج models و بياناتها تمثيل الروابط بين تلك النماذج تمثيل البيانات الموروثة بين نماذج البيانات المُتصلة توثيق النماذج قبل تثبيتها بقواعد البيانات تطبيق عمليات قواعد البيانات بإستخدام إسلوب البرمجة الشيئية 2 سهولة إستيراد الأكواد من اللغات الأخرى عند كتابة أحد التطبيقات بلغةٍ ما أو منصةٍ أخرى لإستعمالها في منصة ORM فإن هذه الأكواد تحتاج إلى الكثير من التهيئة configuration . و لكن هذا الأمر قد تم حله في Rails حيث ستحتاج إلى كتابة أكواد قليلة جداً لعمل التهيئة (وفي بعض الأحيان لن تحتاج إلى التهئية على الإطلاق) عند صنع نماذج Active Record. طريقة عمل هذه التهيئة تعتمد على الإستناد إلى تهيئة التطبيقات بالطريقة نفسها في معظم الأوقات بطريقة رئيسية. ومع ذلك، في بعض الأحيان ستحتاج إلى كتابة أكواد التهيئة عندما لا تتناسب التطبيقات التي تستوردها مع الطريقة الرئيسية للتهيئة في Rails. 2.1 طريقة التسمية Naming إن Active Record يستعمل قواعد تسمية تُساعد على صُنع الروابط بين النماذج و جداول قواعد البيانات. Rails ستقوم بتحويل أسماء الأصناف classes إلى صيغة الجمع لتناسب جدول قاعدة البيانات المناسب لها. على سبيل المثال، إذا كان لديك فئة تُسمى book فستحصل على جدول قاعدة بيانات يُسمى books. و إن Rails تستعمل آلية قوية لذلك تُمكنها من جمع الأسماء المنتظمة و الشاذة. و عند إستعمال فئة إسمها مكون من كلمتين، فإن قواعد التسمية تميل إلى طريقة CamelCase حيث سيحتوي جدول قاعدة البيانات على الكلمتين ذاتهم مفصولتين بإستخدام “_”. على سبيل المثال، فئة تُسمي BookClub فإن جدول قاعدة البيانات الخاص بها سيسمى book_clubs، و يجب أن نلاحظ أن الإسم المفرد تبدأ كلماته بحروف استهلالية Capital. Model / Class Table / Schema Article articles LineItem line_items Deer deers Mouse Mice Person people 2.2 طريقة تنظيم قاعدة البيانات و تخطيطها إن Active Record يستعمل طريقة التسمية التي تحدثنا عنها لتسمية العواميد columns في جداول قواعد البيانات، إعتمادًا على غرض هذه العواميد. المفاتيح الأجنبية Foreign Keys هي مفاتيح أساسية في جداول أخرى و يتم إستخدامها للربط بين قواعد البيانات ببعضها البعض. طريقة تسميتها تتبع النمط الآتي singularized_table_name_id مثال على ذلك item_id, order_id المفاتيح الأساسية Primary Keys: إن Active Record يقوم بعمل عامود مكون من أرقام integers يُسمى id و مهمته هو تمييز الجدول و عناصره، فعند إستخدام خاصية التهجير Active Record Migration لعمل جداول جديدة، سيتم عمل عامود المفاتيح الأساسية تلقائيًا. هُناك أيضًا بعض العواميد الإختيارية التي تُضيف مميزات جديدة لـ Active Record: مفتاح created_at يقوم بتخزين وقت صُنع السجل مع مراعاة الوقت الحالي. مفتاح updated_at يقوم بتخزين وقت تحديث السجل مع مراعاة الوقت الحالى. مفتاح lock_version يضيف خاصية الإغلاق المتفائل لقاعدة البيانات مفتاح type يُحدد إذا ما كانت خاصية Single Table Inheritance أم لا مفتاح (association_name)_type يستخدم لتخزين النوع الخاص بـ polymorphic associations. مفتاح (table_name)_count يُستخدم لتخزين عدد الكائنات أو الأشياء objects الموجودة داخل علاقة ما، و على سبيل المثال: إذا كان لديك class تُسمى Article تحتوي على عامود يُسمى comments_count. سيقوم هذا المُفتاح بتخزين أعداد الـ comments الموجودة بكل Article. 3 صُنع نماذج Active Record إن صُنع نماذج Active Record لهو أمرٌ في غاية السهولة. كُل ما عليك فعله هو عمل صنف class فرعية من سجل التطبيق ApplicationRecord. class Product < ApplicationRecord end هذا سوف يصنع نموذج model يُسمى Product مُتصل بجدول يُسمى products بقاعدة البيانات فبواسطة ذلك سيُمكنك ربط الأعمدة الموجودة بكل صف في ذلك الجدول مع الخصائص الخاصة بنموذجك. فإعتبر أنك قُمت بإنشاء الجدول بإستخدام سطور SQL الآتية: CREATE TABLE products ( id int(11) NOT NULL auto_increment, name varchar(255), PRIMARY KEY (id) ); بإستعمال طريقة تخطيط البيانات السابق ذكرها، يمكنك كتابة كود مثل الآتي: p = Product.new p.name = "Some Book" puts p.name # "Some Book" وسنتابع في الدرس التالي التسمية،قراءة وكتابة البيانات، التوثيق، الاستدعاء والتهجير… من توثيقيات Ruby on Rails
  12. هذا هو الجزء الأخير من سلسلة “مدخل إلى إطار العمل Ruby on Rails” وفي هذا الجزء سنعيد هيكلة الشيفرة التي كتبناها في الأجزاء السابقة من السلسلة، وسنتعرّف إلى نظام الاستيثاق البسيط الذي يقدّمه إطار العمل Rails. إعادة هيكلة الشيفرة بعد أن أصبحت المقالات والتعليقات تعمل بصورة جيدة، لنلقِ نظرة على القالب app/views/articles/show.html.erb . يبدو الملف طويلًا جدًّا، لذا سنستخدم الملفات الجزئية لتنظيف وترتيب الشيفرة البرمجية. تصيير مجموعة الملفات الجزئية في البداية سننشئ ملفًّا جزئيًا خاصًّا بالتعليقات وظيفته عرض جميع التعليقات الخاصّة بالمقالة. أنشئ الملف app/views/comments/_comment.html.erb وأضف إليه الشيفرة التالية: <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> والآن يمكنك تعديل الملف `app/views/articles/show.html.erb` كما يلي: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <%= render @article.comments %> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> بهذه الطريقة سيتم تصيير الملف الجزئي في app/views/comments/_comment.html.erb لكلّ تعليق موجود في مجموعة @article.comments، وعندما يتنقّل التابع render بين عناصر مجموعة التعليقات فإنه يُسند كل تعليق إلى متغيّر محلي local variable يحمل اسم الملف الجزئي ذاته، وفي حالتنا هذه comment والذي يكون متوفّرًا في الملف الجزئي. تصيير الملف الجزئي الخاصّ بالاستمارة لنقم بإزالة قسم التعليقات الجديد إلى ملف جزئي خاصّ به، ومرة أخرى أنشئ ملفًّا باسم _form.html.erb في المجلد app/views/comments/ وأضف إليه ما يلي: <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> ثم عدّل الملف app/views/articles/show.html.erb ليصبح بالصورة التالية: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <%= render @article.comments %> <h2>Add a comment:</h2> <%= render 'comments/form' %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> يعرّف تابع render الثاني القالب الجزئي الذي نرغب في تصييره وهو comments/form، ونظرًا لوجود المحرّف / ضمن هذه السلسلة النصّية سيعرف Rails بأنّك ترغب في تصيير الملف _form.html.erb الموجود في المجلد app/views/comments. أما الكائن @article فسيكون متوفّرًا لأيّ ملفّ جزئي يتم تصييره في العرض لأنّنا عرّفناه كمتغيّر من نوع instance. حذف التعليقات إن القدرة على حذف التعليقات المزعجة هي من الميزات المطلوب توفرها في المدوّنة، ولتنفيذ ذلك سنحتاج إلى إضافة رابط لحذف التعليقات ضمن العرض وإلى حدث destroy في المتحكّم CommentsController. لذا سنضيف أوّلًا رابط الحذف ضمن الملفّ الجزئي app/views/comments/_comment.html.erb وكما يلي: <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> <p> <%= link_to 'Destroy Comment', [comment.article, comment], method: :delete, data: { confirm: 'Are you sure?' } %> </p> سيؤدّي النقر على هذا الرابط إلى إرسال الفعل DELETE متمثّلًا بالرابط /articles/:article_id/comments/:id إلى المتحكّم CommentsController، والذي سيبحث بدوره - مستعينًا بهذا الرابط - عن التعليق المراد حذفه من قاعدة البيانات. لنضِف حدث destroy إلى المتحكّم في الملف app/controllers/comments_controller.rb: class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) redirect_to article_path(@article) end def destroy @article = Article.find(params[:article_id]) @comment = @article.comments.find(params[:id]) @comment.destroy redirect_to article_path(@article) end private def comment_params params.require(:comment).permit(:commenter, :body) end end سيبحث الحدث destroy عن التعليق المراد حذفه، ثم يعيّن موقعه في مجموعة @article.comments ثم يحذفه من قاعدة البيانات ويعيد توجيهنا إلى حدث show الخاصّ بالمقالة. حذف الكائنات المترابطة من البديهي أنّه عند حذف مقالة معيّنة فإن من الواجب أن يتم حذف التعليقات المرتبطة بها، وإلا فستشغل هذه التعليقات مساحة ضمن قاعدة البيانات دون أيّ فائدة. يتيح لنا Rails استخدام الخيار dependent لتحقيق ذلك. توجّه إلى نموذج Article (app/models/article.rb) وعدّله بالصورة التالية: class Article < ApplicationRecord has_many :comments, dependent: :destroy validates :title, presence: true, length: { minimum: 5 } end الاستيثاق Authentication في Rails إن كنت ترغب في نشر المدوّنة على الإنترنت، سيكون بإمكان أي شخص إضافة وتعديل وحذف المقالات والتعليقات فيها. يقدّم Rails نظام استيثاق HTTP بسيط يمكن استخدامه في التطبيقات البسيطة كتطبيقنا هذا. سنحتاج في المتحكّم ArticlesController إلى وسيلة لمنع وصول الشخص غير المستوثق منه إلى الأحداث التي يتضمّنها هذا المتحكّم، ويمكن استخدام تابع http_basic_authenticate_with لتحقيق ذلك. ولاستخدام نظام الاستثياق سنقوم بالإفصاح عنه في بداية ملف المتحكّم ArticlesController in app/controllers/articles_controller.rb وسنستوثق من جميع الأحداث المتوفّرة في هذا المتحكّم عدا حدثي index وshow: class ArticlesController < ApplicationController http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show] def index @articles = Article.all end # بقيّة الشيفرة ... كذلك سنسمح للمستخدمين المستوثق منهم فقط بحذف التعليقات، لذا أضف الشيفرة التالية إلى المتحكّم CommentsController في الملف app/controllers/comments_controller.rb: class CommentsController < ApplicationController http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy def create @article = Article.find(params[:article_id]) # ... end # بقيّة الشيفرة ... والآن إن حاولت إنشاء مقالة جديدة، ستتلقّى طلب استيثاق كهذا: جدير بالذكر أنّ هناك العديد من وسائل الاستيثاق في تطبيقات Rails، أشهرها Devise rails engine و Authlogic. إطار العمل Rails ونظام الترميز UTF-8 إن أسهل طريقة للعمل مع Rails هي تخزين جميع البيانات الخارجية بنظام الترميز UTF-8، وإن لم تفعل ذلك فغالبًا ما تقوم مكتبات Ruby وإطار العمل Rails بتحويل البيانات الأصلية إلى هذا الترميز، ولكن لا يمكن الاعتماد على هذه المكتبات بصورة تامّة، ويفضّل التأكد من أنّ جميع البيانات الخارجية مرمّزة بهذا النظام. وفي حال حدوث أي خطأ في نظام الترميز فإن الحروف ستظهر في المتصفّح غالبًا على هيئة أشكال معينية سوداء بداخلها علامة استفهام، أو قد تظهر الحروف على هيئة رموز غريبة كأن يظهر الرمز “ü” بدلاً من الحرف “ü”. يتّخذ Rails بعض الإجراءات في نظامه الداخلي للتقليل من المسبّبات الشائعة لهذه المشاكل والتي يمكن الكشف عنها وتصحيحها بصورة تلقائية. ولكن، إن كنت تتعامل مع بيانات من مصادر خارجية غير مخزّنة بترميز UTF-8، لن يكون Rails قادرًا على الكشف بصورة تلقائية عن أسباب المشكلة أو تقديم حلّ لها. وهناك مصدران شائعان للبيانات غير المخزّنة بترميز UTF-8: محرر النصوص: تحفظ معظم محرّرات النصوص الملفات البرمجية بصيغة UTF-8، وإن لم يقم محرّر النصوص الذي تستخدمه في كتابة الشيفرات البرمجية بذلك، فقد ينتج عن ذلك تحوّل الحروف الخاصّة أو حروف اللغات الأخرى غير الإنكليزية إلى التحول في المتصفّح إلى أشكال معينية بداخلها علامة استفهام. ينطبق هذا الأمر كذلك على ملفات الترجمة i18n. تجدر الإشارة إلى أنّه تتيح معظم محررات النصوص التي لا تحفظ الملفات البرمجية بهذا الترميز افتراضيًّا (مثل Dreamweaver) إمكانية تغيير الترميز الافتراضي للملفات المحفوظة إلى نظام UTF-8، وننصح بالقيام بذلك. قاعدة البيانات: يحوّل Rails البيانات القادمة من قاعدة البيانات إلى ترميز UTF-8، ولكن إن لم يكن هذا نظام الترميز هذا مستخدمًا من طرف قاعدة البيانات فلن يكون بالإمكان تخزين جميع المحارف المدخلة من قبل المستخدم. فعلى سبيل المثال، إن كان نظام الترميز الداخلي لقاعدة البيانات هو Latin-1 وأدخل المستخدم كلمات باللغة الروسية أو العربية أو اليابانية، فستخسر البيانات إلى الأبد بمجرد دخولها إلى قاعدة البيانات. لذا ينصح دائمًا بتحويل نظام الترميز الداخلي في قاعدة البيانات إلى نظام UTF-8. المصدر: توثيقات Ruby on Rails.
  13. تحدّثنا في الجزء السابق من هذه السلسلة عن النماذج في إطار العمل Ruby on Rails وتعرّفنا على طريقة إنشائها والتعامل معها من خلال كتابة الشيفرة المسؤولة عن حفظ المقالة الجديدة في قاعدة البيانات. في الجزء الثاني من هذا الموضوع سنتعلّم كيفية ربط نموذجين مع بعضهما البعض من خلال إنشاء نموذج جديد خاصّ بالتعليقات. ولكن قبل ذلك سنكمل ما بدأناه في الدروس السابقة من السلسلة في بناء عمليات “CRUD” حيث غطّينا سابقًا عمليتي الإنشاء Create و القراءة Read، وسنغطي اليوم العمليتين المتبقّيتين وهما التحديث Update والحذف Destroy. تحديث المقالات الخطوة الأولى في عملية تحديث المقالات هي إضافة حدث edit إلى المتحكم ArticlesController بين حدثي new و create وكما يلي: def new @article = Article.new end def edit @article = Article.find(params[:id]) end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end سيتضمن العرض استمارة مشابهة لتلك التي استخدمناها في إنشاء المقالات الجديدة. أنشئ ملفًّا باسم app/views/articles/edit.html.erb وأضف إليه الشيفرة التالية: <h1>Edit article</h1> <%= form_for(@article) do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Back', articles_path %> سنوجّه الاستمارة هذه المرة إلى حدث update والذي لم نقم بتعريفه حتى الآن. يؤدي تمرير كائن المقالة للتابع إلى إنشاء عنوان url لإرسال استمارة المقالة التي تم تعديلها، ومن خلال هذا الخيار نخبر Rails بأنّنا نرغب في أن يتم إرسال هذا النموذج من خلال فعل HTTP PATCH وهو أحد أفعال HTTP التي تستخدم في تحديث الموارد حسب بروتوكول REST. يمكن أن يكون المعامل الأول لـ form_for كائنًا، مثلًا @articl، والذي سيؤدي بالدالة المساعدة إلى ملء الاستمارة بالحقول التابعة للكائن، ويؤدي تمرير الرمز (:article) بنفس اسم المتغيّر من نوع instance (@article) إلى نفس النتيجة تلقائيًا. والآن سنقوم بإنشاء الحدث update في المتحكّم app/controllers/articles_controller.rb وسنضيفه بين حدث create والتابع ذي المحدّد الخاصّ private: def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end def update @article = Article.find(params[:id]) if @article.update(article_params) redirect_to @article else render 'edit' end end private def article_params params.require(:article).permit(:title, :text) end يستخدم الحدث update عندما ترغب في تحديث سجل موجود في قاعدة البيانات، ويستقبل هذا الحدث جدول تقطيع hash يحتوي الخصائص التي ترغب في تحديثها. وكما سبق، في حال وجود خطأ في عملية التحديث سنعرض الاستمارة على المستخدم من جديد. سنستخدم التابع article_params الذي عرّفناه في وقت سابق للحدث create. لا حاجة لتمرير جميع الخصائص لغرض تحديثها، فعلى سبيل المثال، إن تم استدعاء @article.update(title: 'A new title') فسيقوم Rails بتحديث خاصية العنوان فقط، ويترك باقي الخصائص دون تعديل. أخيرًا، نرغب في عرض رابط إلى الحدث edit في الصفحة التي نعرض فيها قائمة المقالات، لذا توجّه إلى الملف app/views/articles/index.html.erb وأضف الرابط ليظهر إلى جانب رابط “Show”: <table> <tr> <th>Title</th> <th>Text</th> <th colspan="2"></th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> <td><%= link_to 'Edit', edit_article_path(article) %></td> </tr> <% end %> </table> سنضيف كذلك رابطًا إلى قالب app/views/articles/show.html.erb ليظهر رابط “Edit” في صفحة المقالة أيضًا: ... <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> هذا هو شكل تطبيقنا حتى هذه اللحظة: استخدام الملفات الجزئية partials لإزالة التكرار من العروض تبدو صفحة تحرير المقالة مشابهة تمامًا لصفحة إنشاء مقالة جديدة، وفي الواقع تستخدم الصفحتان الشيفرة ذاتها لعرض الاستمارة. سنقوم الآن بالتخلص من هذا التكرار باستخدام ملفات العرض الجزئية. تحمل هذه الملفات أسماء تبدأ بالمحرف (_). أنشئ ملفًّا جديدًا باسم _form.html.erb ضمن المسار app/views/articles/ وأضف إليه الشيفرة التالية: <%= form_for @article do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> لاحظ أننا لم نغيّر شيئًا باستثناء الإعلان عن التابع form_for وسبب استخدام هذه الأسلوب المختصر والبسيط في الإعلان عن التابع form_for للتعبير عن الاستمارتين هو أن @article عبارة عن مورد يرتبط بمجموعةٍ من مسارات RESTful، وبإمكان Rails أن يخمّن عنوان URI والتابع الذي يجب استخدامه. والآن لنقم بتحديث العرض app/views/articles/new.html.erb لاستخدام الملف الجزئي الذي أنشأناه وسنقوم بإعادة كتابة العرض من جديد وكما يلي: <h1>New article</h1> <%= render 'form' %> <%= link_to 'Back', articles_path %> ثم قم بالأمر عينه في ملف العرض app/views/articles/edit.html.erb: <h1>Edit article</h1> <%= render 'form' %> <%= link_to 'Back', articles_path %> حذف المقالات هذه هي العملية الأخيرة ضمن عمليات CRUD، وبحسب معايير REST فإن المسار الذي يؤدي إلى حذف المقالات وكما يظهر في مخرجات الأمر bin/rails routes هو: DELETE /articles/:id(.:format) articles#destroy يجب استخدام الفعل DELETE في المسار المسؤول عن حذف الموارد، أما في حال استخدام الفعل GET فسيكون بالإمكان إنشاء رابط خبيث كهذا الرابط مثلًا: <a href='http://example.com/articles/1/destroy'>look at this cat!</a> سنستخدم التابع delete لحذف المصادر، وهذا المسار مرتبط بالحدث destroy ضمن المتحكّم app/controllers/articles_controller.rb والذي لم نقم بإنشائه بعد. عادة ما يكون التابع destroy التابع الأخير ضمن المتحكّم، وكما هو الحال مع بقية التوابع العامّة public يجب الإعلان عنه قبل أي توابع خاصّة أو محميّة protected. def destroy @article = Article.find(params[:id]) @article.destroy redirect_to articles_path end الصورة النهائية للمتحكّم ArticleController في الملف app/controllers/articles_controller.rb هي: class ArticlesController < ApplicationController def index @articles = Article.all end def show @article = Article.find(params[:id]) end def new @article = Article.new end def edit @article = Article.find(params[:id]) end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end def update @article = Article.find(params[:id]) if @article.update(article_params) redirect_to @article else render 'edit' end end def destroy @article = Article.find(params[:id]) @article.destroy redirect_to articles_path end private def article_params params.require(:article).permit(:title, :text) end end يمكن استدعاء التابع destroy في كائنات التسجيلة النشطة Active Record عندما ترغب في حذفها من قاعدة البيانات. لاحظ أنّنا لسنا بحاجة لإضافة عرض خاص بهذا الحدث لأنّنا نعيد توجيه المستخدم إلى الحدث index. أخيرًا أضف رابط ‘Destroy’ إلى القالب app/views/articles/index.html.erb لنربط كل الصفحات مع بعضها البعض. <h1>Listing Articles</h1> <%= link_to 'New article', new_article_path %> <table> <tr> <th>Title</th> <th>Text</th> <th colspan="3"></th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> <td><%= link_to 'Edit', edit_article_path(article) %></td> <td><%= link_to 'Destroy', article_path(article), method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </table> استخدمنا هنا التابع link_to بطريقة مختلفة، حيث مررّنا اسم المسار كمعامل ثانٍ، ثمّ مرّرنا الخيارات الأخرى بعد ذلك. تستخدم الخيارات method: :delete وdata: { confirm: 'Are you sure?' } كخصائص HTML5 بحيث يؤدي الضغط على الرابط إلى عرض مربع حوار للتأكد من رغبة المستخدم في حذف المقالة، ثم إرسال الرابط باستخدام التابع delete. تتمّ تعملية التحقّق هذه بواسطة ملف JavaScript الذي يحمل الاسم rails-ujs والموجود بصورة افتراضية في مخطط التطبيق (app/views/layouts/application.html.erb)، وفي حال عدم وجود هذا الملف لن يظهر مربع الحوار التأكيدي للمستخدم. تهانينا أصبح بإمكانك الآن إنشاء وعرض وسرد وتحديث وحذف المقالات في مدوّنتك. إضافة النموذج الخاصّ بالتعليقات سنقوم الآن بإنشاء نموذج جديد في تطبيقنا هذا ستكون وظيفته التعامل مع التعليقات. إنشاء النموذج لإنشاء النموذج الخاص بالتعليقات سنتبع الأسلوب السابق نفسه وذلك باستخدام أداة المولّد لإنشاء نموذج يحمل الاسم Comment ويمثّل مرجعًا إلى المقالة. اكتب الأمر التالي في سطر الأوامر: $ bin/rails generate model Comment commenter:string body:text article:references سينشئ هذا الأمر أربعة ملفات: الملف Purpose الملف/المجلد الوظيفة db/migrate/20140120201010_create_comments.rb ملف التهجير المسؤول عن إنشاء جدول التعليقات في قاعدة البيانات (سيحمل اسم الملف لديك ختمًا زمنيًا مختلفًا) app/models/comment.rb النموذج الخاص بالتعليقات test/models/comment_test.rb ملف الاختبارات الخاص بنموذج التعليقات test/fixtures/comments.yml نماذج تعليقات تستخدم في إجراء الاختبارات لنلق نظرة في البداية على ملف app/models/comment.rb: class Comment < ApplicationRecord belongs_to :article end كما تلاحظ فمحتوى هذا الملف مشابه لنموذج Article الذي أنشأناه سابقًا، والفارق الوحيد هو السطر belongs_to :article والذي ينشئ رابطًا بين النموذجين، وسنتحدّث عن الروابط بعد قليل. أما الكلمة المفتاحية (:references) ضمن الأمر الذي قمنا بتنفيذه في سطر الأوامر، فهي نوع خاص من البيانات بالنسبة للنماذج. تنشئ هذه الكلمة المفتاحية عمودًا في الجدول الموجود في قاعدة البيانات يحمل اسم النموذج الذي تمّ تمريره إلى هذه الكلمة مع إضافة _id والذي يمثّل عددًا صحيحًا. ستتضح الأمور أكثر بالنسبة إليك إن تفحّصت ملف db/schema.rb أدناه. قام Rails - بالإضافة إلى إنشاء النموذج - بإنشاء تهجير وظيفته إنشاء الجدول المقابل للنموذج في قاعدة البيانات: class CreateComments < ActiveRecord::Migration[5.0] def change create_table :comments do |t| t.string :commenter t.text :body t.references :article, foreign_key: true t.timestamps end end end يُنشئ السطر t.references عمودًا من نوع integer باسم article_id إضافة إلى فهرس index خاص بهذا العمود وقيد مفتاح خارجي Foreign Key Constraint والذي يشير إلى عمود id في جدول المقالات. والآن نفذ التهجير باستخدام الأمر التالي: $ bin/rails db:migrate ينفّذ Rails التهجيرات غير المنفّذة فقط؛ لذا ستكون نتيجة الأمر التالي كما يلي: == CreateComments: migrating ================================================= -- create_table(:comments) -> 0.0115s == CreateComments: migrated (0.0119s) ======================================== ربط النماذج مع بعضها البعض تسهّل روابط التسجيلة النشطة تكوين العلاقات بين النماذج، وفي حالتنا هذه سنُنشئ علاقة بين جدولي التعليقات والمقالات، ولو فكّرنا في طبيعة العلاقة التي تربط بينهما فسنجد أنه: ينتمي كل تعليق إلى مقالة واحدة. تمتلك المقالة الواحدة العديد من التعليقات. يستخدم Rails صياغة مشابهة للربط بين النماذج، وقد شاهدنا في نموذج Comment في الملف app/models/comment.rb الشيفرة المسؤولة عن ربط كل تعليق بمقالة واحدة: class Comment < ApplicationRecord belongs_to :article end سنحتاج الآن إلى تكوين الجانب الثاني من الرابطة، أي ربط المقالات بالتعليقات، لذا توجّه إلى الملف app/models/article.rb وعدّله بالصورة التالية: class Article < ApplicationRecord has_many :comments validates :title, presence: true, length: { minimum: 5 } end والآن أصبح النموذجان مرتبطين مع بعضهما البعض تلقائيًا، فعلى سبيل المثال، في حال كان لدينا متغيّر @article والذي يمثّل مقالة معيّنة، يمكن استدعاء جميع التعليقات المرتبطة بتلك المقالة على هيئة مصفوفة وذلك من خلال @article.comments. إضافة مسار خاص بالتعليقات كما هو الحال مع متحكم welcome سنحتاج إلى إضافة مسار نحدّد من خلاله العنوان الذي نرغب في استخدامه لمشاهدة التعليقات؛ لذا افتح ملف config/routes.rb مرة أخرى، وعدّله كما يلي: resources :articles do resources :comments end بهذه الطريقة تصبح التعليقات بمثابة موارد مضمّنة في المقالات، وهذه الطريقة هي جزء من العلاقة الهرمية التي تنشأ بين المقالات والتعليقات. إنشاء المتحكّم الخاصّ بالتعليقات بعد أن انتهينا من إعداد النموذج، أصبح بإمكاننا الآن إنشاء المتحكّم الخاص بالتعليقات، وسنستخدم أداة المولّد كما فعلنا سابقًا: $ bin/rails generate controller Comments سينشئ هذا الأمر خمسة ملفات إضافة إلى مجلّد فارغ: الملف/المجلد الوظيفة app/controllers/comments_controller.rb المتحكّم الخاص بالتعليقات /app/views/comments يتم تخزين العروض الخاصّة بالتعليقات في هذا المجلد test/controllers/comments_controller_test.rb ملف الاختبار الخاصّ بالمتحكّم app/helpers/comments الملف الخاصّ بمساعد العرض app/assets/javascripts/comments.coffee ملف CoffeScript الخاصّ بالمتحكّم app/assets/stylesheets/comments.scss أوراق الأنماط المتتالية CSS الخاصّة بالمتحكّم كما هو الحال مع أي مدوّنة، فإن القرّاء سيكتبون تعليقاتهم بعد قراءة المقالة مباشرة، وبعد أن يرسلوا تعليقاتهم يتم توجيههم إلى صفحة عرض المقالة ليتمكّنوا من مشاهدة التعليقات. وستكون وظيفة المتحكّم CommentsController هي توفير التوابع اللازمة لإنشاء التعليقات وحذف التعليقات المزعجة حال وصولها. سنقوم أولًا بتعديل قالب عرض المقالات app/views/articles/show.html.erb لنتمكن من إضافة تعليق جديد: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> ستضيف الشيفرة السابقة استمارة إلى صفحة عرض المقالات يمكن من خلالها إضافة تعليق جديد من خلال استدعاء الحدث create ضمن المتحكّم CommentsController. ويستخدم الاستدعاء form_for مصفوفة ستعمل على إنشاء مسار متداخل nested route مثل: /articles/1/comments. لنجرِ الآن التعديلات اللازمة على الحدث create في الملفّ app/controllers/comments_controller.rb: class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) redirect_to article_path(@article) end private def comment_params params.require(:comment).permit(:commenter, :body) end end ستتعقّد الأمور هنا قليلًا وذلك بسبب التداخل nesting الحاصل بين المسارات، إذ في كل مرة يتمّ فيها طلب تعليق معيّن يجب أن يتابع ذلك الطلب المقالة التي يرتبط بها هذا التعليق، وبالتالي استدعاء التابع find في نموذج Article والمسؤول عن اختيار المقالة المطلوبة حسب المعرّف المحدّد في المسار. بالإضافة إلى ذلك، استفدنا من بعض التوابع التي تقدّمها عملية الربط بين النموذجين، فقد استخدمنا التابع create على @article.comments لإنشاء التعليق وحفظه، وسيؤدي هذا إلى ربط التعليق الجديد بالمقالة المحدّدة. وبعد إنشاء التعليق الجديد نعيد توجيه المستخدم إلى المقالة الأصلية باستخدام الدالة المساعد article_path(@article). وكما شاهدنا تستدعي هذه الدالة الحدث show ضمن المتحكّم ArticlesController والذي يعمل بدوره على تصيير القالب show.html.erb، وهو المكان الذي نرغب أن تظهر التعليقات فيه؛ لذا سنقوم بإجراء التعديلات اللازمة على الملف app/views/articles/show.html.erb. <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <% @article.comments.each do |comment| %> <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> <% end %> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> أصبح بإمكانك الآن إضافة المقالات والتعليقات إلى مدوّنتك وعرضها في الأماكن الصحيحة. في الدرس القادم سنكمل العمل على التعليقات، حيث سنستخدم الملفات الجزئية لترتيب القوالب أوّلًا، ثم نضيف إمكانية حذف التعليقات من قاعدة البيانات، وفي الختام سنتطرّق إلى عملية الاستيثاق Authentication بصورة سريعة ومبسّطة. المصدر: توثيقات Ruby on Rails.
  14. تعرّفنا في الدرس السابق بإيجاز على طريقة عمل إطار العمل Ruby on Rails حيث تعرّفنا على المتحكّمات والعروض وبدأنا بإنشاء تطبيق المدوّنة البسيطة وأنشأنا في الدرس السابق الاستمارة الخاصة بإضافة مقالة جديدة، ولكن وصلنا إلى النقطة التي نحتاج فيها إلى تخزين المقالة في قاعدة البيانات، وهنا يأتي دور النماذج Models. تحمل النماذج في Rails اسمًا بصيغة المفرد في حين يحمل الجدول المرتبط بها في قاعدة البيانات اسمًا بصيغة الجمع. تتيح أداة المولّد generator في Rails إنشاء النماذج ويلجأ أغلب المطوّرين إلى هذه الأداة لإنشاء النماذج. لإنشاء نموذج جديد استخدم الأمر التالي في سطر الأوامر: $ bin/rails generate model Article title:string text:text من خلال هذه الأمر نخبر Rails بأننا نرغب في إنشاء نموذج باسم Article إلى جانب خاصّية title من نوع string، وخاصّية text من نوع text. تضاف هذه الخواص بصورة تلقائية إلى جدول المقالات في قاعدة البيانات ويتم ربطها بنموذج Article. ويستجيب Rails لهذا الأمر بإنشاء عدد من الملفات، وما يهمّنا منها الآن هما app/models/article.rb و db/migrate/20140120191729_create_articles.rb (لاحظ أن اسم الملف الثاني يختلف قليلًا عن هذا الاسم). الملف الثاني مسؤول عن إنشاء بنية قاعدة البيانات، وهو ما سنتحدث عنه بعد قليل. إجراء عملية التهجير Migration كما لاحظنا فإن الأمر bin/rails generate model قد أنشأ ملف تهجير لقاعدة البيانات داخل المجلد db/migrate. والتهجيرات هي عبارة عن أصناف مصمّمة لتسهيل عملية إنشاء الجداول في قواعد البيانات والتعديل عليها. يستخدم Rails أوامر rake لإجراء التهجيرات، ويمكن التراجع عن عملية التجهير بعد إجرائها على قاعدة البيانات. تتضمّن أسماء ملفات التهجير ختمًا زمنيًا لضمان معالجة هذه الملفات حسب التسلسل الزمني لإنشائها. لو ألقينا نظرة في ملف db/migrate/YYYYMMDDHHMMSS_create_articles.rb (تذكّر أن الملف عندك يحمل ختمًا زمنيًّا مختلفًا) فسنجد التالي: class CreateArticles < ActiveRecord::Migration[5.0] def change create_table :articles do |t| t.string :title t.text :text t.timestamps end end end ستنشئ عملية التهجير أعلاه تابعًا يحمل اسم `change` والذي يتم استدعاؤه عند إجراء عملية التهجير. حتى الحدث المُعرّف ضمن التابع قابل للتراجع، ما يعني أن Rails قادر على التراجع عن التغييرات الحاصلة من إجراء عملية التهجير في حال أردت ذلك في وقت لاحق. عند إجراء عملية التهجير هذه سيتم إنشاء جدول باسم `articles` يتضمن عمودًا من نوع `string` وآخر من نوع `text`، إضافة إلى عمودين للختم الزمني يمكن لـ Rails من خلالهما متابعة تواريخ إنشاء وتعديل المقالات. لتنفيذ عمية التهجير توجّه إلى سطر الأوامر ونفذ الأمر التالي: $ bin/rails db:migrate سينفّذ Rails أمر التهجير التالي وسيخبرك بإنشاء جدول Articles. == CreateArticles: migrating ================================================== -- create_table(:articles) -> 0.0019s == CreateArticles: migrated (0.0020s) ========================================= حفظ البيانات بواسطة المتحكّم سنعود الآن إلى المتحكّم ArticlesController، حيث سنعمل على تعديل الحدث create ليستخدم النموذج الجديد Article لحفظ البيانات في قاعدة البيانات. افتح الملف app/controllers/articles_controller.rb وعدّله بالصورة التالية: def create @article = Article.new(params[:article]) @article.save redirect_to @article end يمكن استحداث initialize كل نموذج في Rails مع الخصائص Attributes المرتبطة به، والتي يتم ربطها تلقائيًا مع الأعمدة المقابلة في قاعدة البيانات. وقد قمنا بذلك في السطر الأول في الحدث create (هل تذكر params[:article] والذي يضمّ الخصائص التي نريدها). بعد ذلك يمكن حفظ النموذج في قاعدة البيانات من خلال الدالة @article.save. وفي النهاية نعيد توجيه المستخدم إلى الحدث show الذي سنعرّفه في وقت لاحق. توجّه الآن إلى العنوان http://localhost:3000/articles/new وستتلقّى الخطأ التالي: يدعم Rails العديد من مزايا الأمان التي تساعد في كتابة تطبيقات أمينة، ونحن الآن نتعامل مع إحدى هذه المزايا. تدعى هذه الميزة بالمعاملات القوية strong parameters والتي تجبرنا على تحديد المعاملات المسموح بها في الأحداث الموجودة ضمن المتحكم. ما الفائدة من ذلك؟ صحيح أن القدرة على إضافة جميع المعاملات إلى النموذج دفعة واحدة وبصورة تلقائية يختصر الكثير من الجهد بالنسبة للمبرمج، إلا أنّ البرنامج يكون في هذه الحالة عرضة للاستخدامات الخبيثة. فماذا لو تمّ إنشاء طلب إلى الخادوم يتضمن استمارة إنشاء مقالة جديدة إضافة إلى حقول أخرى تحتوي على معلومات تضرّ بالتطبيق؟ سيتم إسناد المعلومات الإضافية بصورة شاملة “Mass Assignment” إلى النموذج ثم إلى قاعدة البيانات جنبًا إلى جنب مع البيانات الأصلية، وهذا قد يتسبب في تعطيل عمل برنامجك أو قد يحدث ما هو أسوأ من ذلك بكثير. يجب علينا إذًا تحديد المعاملات المسموح بإدخالها إلى النموذج، وفي حالتنا هذه سنسمح بإدراج معاملي title و text ونطلب توفّر قيم لهما. وللقيام بذلك عدّل السطر الأول من حدث create بالصورة التالية: @article = Article.new(params.require(:article).permit(:title, :text)) غالبًا ما يتمّ تحديد المعاملات المسموح بإدخالها إلى النموذج في تابع خاص ليصبح بالإمكان إعادة استخدامها بواسطة عدة أحداث في المتحكّم نفسه مثل حدثي create و update، إضافة إلى ذلك يكون هذا التابع خاصًّا وذلك باستخدام المحدّد private لضمان عدم إمكانية استدعائه من خارج السياق المقرّر له، وبالشكل التالي: def create @article = Article.new(article_params) @article.save redirect_to @article end private def article_params params.require(:article).permit(:title, :text) end عرض المقالات إن قمت بتعبئة استمارة المقالة الجديدة وإرسالها فستتلقّى خطأ مفاده عدم عثور Rails على الحدث show، لذا سنقوم بإنشاء هذا الحدث الآن. كما رأينا سابقًا في مخرجات الأمر bin/rails routes فإن مسار الحدث show هو: article GET /articles/:id(.:format) articles#show تعني الصيغة الخاصة :id أن هذا المسار يطلب وجود معامل :id والذي يمثّل في حالتنا هذه معرّف المقالة. وكما فعلنا سابقًا، يجب علينا إضافة الحدث show إلى ملف المتحكّم app/controllers/articles_controller.rb وتحديد العرض المرتبط به. عادة ما تأخذ أحداث CRUD في المتحكّمات الترتيب التالي: index, show, new, edit, create, update, destroy. ويمكن اتّباع الترتيب الذي يعجبك، ولكن تذكّر أن هذه التوابع هي توابع عامّة public، ويجب الإعلان عنها قبل الإعلان عن التوابع الخاصّة. سنضيف الآن الحدث show آخذين ما سبق بعين الاعتبار: class ArticlesController < ApplicationController def show @article = Article.find(params[:id]) end def new end # بقيّة الشيفرة ..... استخدمنا الدالة Article.find للبحث عن المقالة المطلوبة، وذلك بتمرير المعامل params[:id] للحصول على قيمة المعرّف من الطلب الذي أرسلته صفحة إنشاء مقالة جديدة. كذلك استخدمنا متغيّرًا من نوع instance (مسبوقًا بعلامة @) ليكون مرجعًا لكائن المقالة، وذلك لأنّ Rails يمرّر هذا النوع من المتغيّرات إلى العرض. أنشئ الآن ملفًّا جديدًا باسم show.html.erb في المسار app/views/articles/ وأضف إليه الشيفرة التالية: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> ستكون الآن قادرًا على إنشاء مقالة جديدة؛ لذا توجّه إلى العنوان http://localhost:3000/articles/new وجرّب إضافة مقالة جديدة. عرض قائمة بمقالات المدوّنة نحتاج الآن إلى عرض قائمة بجميع المقالات الموجودة في المدونة، وسيكون المسار المرتبط بهذا الحدث وبحسب مخرجات الأمر bin/rails routes كالتالي: articles GET /articles(.:format) articles#index أضف الحدث index المرتبط بهذا المسار إلى المتحكّم ArticlesController في الملف app/controllers/articles_controller.rb. من الممارسات الشائعة بين المطوّرين هو كتابة الحدث index في بداية المتحكّم: class ArticlesController < ApplicationController def index @articles = Article.all end def show @article = Article.find(params[:id]) end def new end # بقية الشيفرة ... بعدها أضف العرض الخاصّ بهذا الحدث والموجود في المسار app/views/articles/index.html.erb والذي يتضمّن الشيفرة التالية: <h1>Listing articles</h1> <table> <tr> <th>Title</th> <th>Text</th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> </tr> <% end %> </table> توجّه الآن إلى العنوان http://localhost:3000/articles في المتصفّح وستشاهد قائمة بجميع المقالات التي أنشأتها مسبقًا. إضافة الروابط للتنقل بين صفحات المدوّنة أصبح بمقدورنا الآن إنشاء وعرض وسرد قائمة المقالات المتوفّرة في المدونة، ولكنّنا بحاجة إلى بعض الروابط التي تساعدنا في التنقل بين صفحات الموقع. افتح الملف app/views/welcome/index.html.erb وعدّله كما يلي: <h1>Hello, Rails!</h1> <%= link_to 'My Blog', controller: 'articles' %> التابع link_to هو أحد دوال العروض المساعدة والمضمّنة في Rails، ووظيفة هذا التابع إنشاء رابط تشعّبي بالاستناد إلى النصّ الذي نمرّره إليه، وهو في حالتنا هذه المسار الخاص بسرد قائمة المقالات. لنضف بعض الروابط إلى العروض الأخرى، ولنبدأ بإضافة رابط إنشاء مقالة جديدة إلى الملف app/views/articles/index.html.erb قبل وسم <table>: <%= link_to 'New article', new_article_path %> سيوجّه هذا الرابط المستخدم إلى الصفحة التي تتضمن استمارة إنشاء مقالة جديدة. سنضيف رابطًا آخر إلى الملفّ app/views/articles/new.html.erb بعد الاستمارة مباشرة، ليتمكن المستخدم من العودة إلى الصفحة الرئيسية: <%= form_for :article, url: articles_path do |f| %> ... <% end %> <%= link_to 'Back', articles_path %> وأخيرًا، سنضيف رابطًا إلى القالب app/views/articles/show.html.erb يوجّه المستخدم إلى الصفحة الرئيسية أيضًا، وبهذا يصبح بميسور من يستعرض مقالة معيّنة أن يرجع إلى الصفحة التي تعرض جميع المقالات: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <%= link_to 'Back', articles_path %> التحقّق من المدخلات لو نظرت إلى النموذج الذي أنشأناه سابقًا فسترى أنّ الملف بسيطٌ للغاية: class Article < ApplicationRecord end لاحظ أنّ الصنف Article موروث من الصنف ApplicationRecord وهو بدوره موروث من الصنف ActiveRecord::Base والذي يتضمّن الكثير من الوظائف والإجراءات الخاصة بالنماذج، مثل عمليات CRUD البسيطة (Create, Read, Update, Destroy) والتحقّق من البيانات Validation، إضافة إلى عمليات البحث المعقّدة وربط النماذج المختلفة مع بعضها البعض. ويقدّم إطار العمل Rails توابع متعدّدة تساعد في التحقق من البيانات المرسلة إلى النموذج. افتح الملف app/models/article.rb وأضف إليه الشيفرة التالية: class Article < ApplicationRecord validates :title, presence: true, length: { minimum: 5 } end سيضمن هذا التغيير امتلاك كل مقالة جديدة في المدونة لعنوان يتألف من خمسة أحرف على الأقل. يتيح Rails التحقّق من أمور متنوّعة في النماذج، مثل التحقّق من وجود أو عدم تكرار الأعمدة والتحقّق من تنسيقها ووجود كائنات مرتبطة بها. لنجرّب الآن استدعاء الدالة @article.save في مقالة لا تمتلك عنوانًا وسنلاحظ أن الدالة ترجع القيمة false. لو عدنا إلى المتحكّم في الملف app/controllers/articles_controller.rb مرّة أخرى سنلاحظ بأنّنا لم نتحقّق من النتيجة التي ترجعها الدالة @article.save ضمن الحدث create. إن فشلت الدالة @article.save في أداء عملها، يجب أن نعيد المستخدم إلى استمارة إضافة مقالة جديدة، وللقيام بذلك عدّل حدثي new و create في الملف app/controllers/articles_controller.rb بالصورة التالية: def new @article = Article.new end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end private def article_params params.require(:article).permit(:title, :text) end سينشئ الحدث new متغيّرًا جديدًا من نوع instance يحمل الاسم @article وستتعرّف إلى سبب القيام بذلك بعد قليل. لاحظ أنّنا استخدمنا render داخل الحدث create بدلًا من redirect_to في حال إرجاع الدالة save للقيمة false. يستخدم التابع render لكي يتم تمرير الكائن @article إلى القالب الجديد عند تصييره. وعملية التصيير هذه تتم ضمن نفس الطلب الناتج من إرسال الاستمارة، في حين أن الدالة redirect_to تتسبّب في إرسال طلب آخر. الآن أعد تحميل الصفحة ذات العنوان http://localhost:3000/articles/new وحاول إضافة مقالة جديد دون عنوان، سترى بأنّ Rails يعيدك إلى صفحة الاستمارة، ولكن هذا ليس مفيدًا جدًّا. يجب إخبار المستخدم بحدوث خطأ ما، وللقيام بذلك عدّل الملف app/views/articles/new.html.erb للتحقّق من رسائل الخطأ: <%= form_for :article, url: articles_path do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Back', articles_path %> تحقّقنا في البداية من وجود أي أخطاء من خلال @article.errors.any?، وفي حال وجودها نعرض قائمة الأخطاء المتوفّرة من خلال @article.errors.full_messages. تأخذ الدالة pluralize معاملين الأول رقمي والثاني نصّي. إن كان العدد أكبر من واحد تتحوّل السلسلة النصّية تلقائيًا إلى صيغة الجمع. إن سبب إضافة @article = Article.new إلى المتحكّم ArticlesController هو أنّنا لو لم نقم بذلك لأصبحت قيمة المتغيّر @articl هي nil، وسيؤدي الاستدعاء @article.errores.any? إلى إطلاق خطأ. يحيط Rails الحقول التي تحتوي على أخطاء بوسم <div> مع صنف CSS يحمل الاسم field_with_errors، ويمكنك تعريف صنف CSS هذا لتنسيق الحقول حسب الرغبة. والآن ستتلقّى رسالة خطا مرتّبة عندما تحاول حفظ مقالة لا تتضمن عنوانًا. في الدرس القادم سنواصل العمل على النموذج حيث سنكتب الشيفرة المسؤولة عن تعديل المقالات وحذفها، ثم سنتعرّف على طريقة إنشاء علاقات بين النماذج المختلفة من خلال إضافة نموذج للتعامل مع التعليقات في المدونة. المصدر: توثيقات Ruby on Rails.
  15. Rails هو إطار عمل لتطوير تطبيقات الويب مكتوب بلغة Ruby البرمجية، وقد صُمّم إطار العمل هذا لتسهيل برمجة تطبيقات الويب من خلال وضع بعض الافتراضات المسبقة حول ما يحتاجه المطوّر للشروع في العمل. يتيح إطار العمل هذا كتابة شيفرات أقل وإنجاز أمور أكثر من أي لغة برمجية أو أطر عمل أخرى. يفترض Rails أن هناك طريقة مثلى لإنجاز الأعمال، وقد صمّم لتشجيع المطوّر على اتباع هذه الطرق وفي بعض الأحيان حثّه على ترك البدائل الأخرى، وقد تلاحظ زيادة هائلة في إنتاجيتك إن تعلّمت الأسلوب الذي يتبعه إطار العمل هذا، وقد يؤدي الالتزام بالعادات القديمة المتّبعة في لغات البرمجة الأخرى إلى تجربة غير جيّدة في تطوير التطبيقات باستخدام Rails. تستند فلسفة Rails إلى ركيزتين أساسيتين: لا تكرّر نفسك (Don’t Repeat Yourself): ينصّ هذا المفهوم على وجوب تمثيل أي جزء من أجزاء المعرفة بصورة مفردة وغير مبهمة وموثوقة في النظام. اتّباع هذا المفهوم في كتابة النصوص البرمجية وعدم تكرار المعلومات ذاتها باستمرار يؤدي إلى زيادة قابلية صيانة الشيفرة المكتوبة وامتلاكها القدرة على التوسّع إضافة إلى انخفاض نسبة الأخطاء فيها. مبدأ “Convention Over Configuration”: يمتلك Rails مبادئ خاصّة ترتبط بتحديد الطريقة المثلى في إنجاز الأعمال في تطبيق الويب، ويعتمد على هذه المبادئ بصورة افتراضية بدلًا من إجبار المطوّر على تحديد تفاصيل صغيرة في عمله من خلال عدد كبير من اﻹعدادات. ما الذي تحتاجه للبدء هذه السلسلة مخصصة للمبتدئين الذين يرغبون في الشروع ببناء التطبيقات على إطار العمل Rails، ولا يفترض وجود أي خبرة سابقة في هذا المجال، ولكن هناك بعض الأمور التي يجب تثبيتها قبل الشروع في التعلم: تثبيت الإصدار 2.2.2 من لغة Ruby البرمجية أو أي إصدار أعلى. تثبيت النسخة الملائمة من حزمة التطوير Development Kit إن كنت من مستخدمي نظام التشغيل ويندوز. نظام إدارة الحزم (الجواهر) RubyGems والذي يأتي مع لغة Ruby بصورة افتراضية. تثبيت قواعد بيانات SQLite3. ذكرنا أنّ إطار العمل Rails مبني باستخدام لغة Ruby البرمجية، وإن كنت لا تمتلك خبرة مسبقة بهذه اللغة يمكنك مراجعة الدروس المتوفّرة حول أساسيات لغة Ruby في الأكاديمية إضافة إلى الموقع الرسمي للغة. إنشاء مشروع جديد في Rails الهدف من هذه السلسلة هو بناء مدوّنة بسيطة باستخدام إطار العمل Rails، وقبل البدء في بناء التطبيق يجب التأكد من تثبيت Rails في جهازك. سنستخدم الرمز $ للتعبير عن سطر الأوامر في الأنظمة الشبيهة بـ UNIX. أما في نظام ويندوز فسترى سطر الأوامر يبدأ بشيء مشابه لهذه الصيغة: <C:\source_code. تثبيت Rails توجّه إلى سطر الأوامر في جهازك (في نظام macOS افتح الطرفية Terminal.app، وفي نظام ويندوز اختر “Run” من قائمة ابدأ ثم اكتب cmd.exe). في البداية سنتأكد من إصدار لغة Ruby المثبت في الجهاز: $ ruby -v ruby 2.3.1p112 بالنسبة لقواعد البيانات SQLite3 فعادة ما تكون مثبّتة بشكل افتراضي في الأنظمة الشبيهة بـ UNIX. أما في نظام ويندوز، فإن قمت بتثبيت Rails من خلال مثبت Rails فإن SQLite ستكون مثبتة على جهازك أيضاً. يمكنك كذلك مراجعة موقع SQLite3 للاطلاع على تعليمات تثبيت قاعدة البيانات. يمكن التحقق من سلامة تثبيت SQLite3 من خلال الأمر التالي: $ sqlite3 --version إن كانت SQLite مثبتة في الجهاز فسيظهر رقم الإصدار المثبت في سطر الأوامر. لتثبيت Rails استخدم أمر التثبيت gem install الذي يتيحه RubyGems وبالصورة التالية: $ gem install rails وللتأكد من أن عملية التثبيت قد تمّت بصورة صحيحة، يجب أن تكون قادرًا على تنفيذ الأمر التالي في سطر الأوامر: $ rails --version يجب أن تحصل على نتيجة مشابهة لهذه: Rails 5.1.0. إنشاء تطبيق المدوّنة يقدّم إطار العمل Rails مجموعة من الشيفرات تحمل اسم المولّدات generators، وتهدف هذه الشيفرات إلى تسهيل عمل المطوّر من خلال إنشاء الملفات المطلوبة للشروع في مهمّة معيّنة. مولّد التطبيق الجديد هو أحد هذه المولّدات ويعمل على إنشاء تطبيق Rails جديد وتوفير عناء كتابته من قبل المطور. ولاستخدام المولّد توجّه في سطر الأوامر إلى المجلد الذي ترغب في إنشاء التطبيق فيه واكتب الأمر التالي: $ rails new blog سينشئ هذا الأمر تطبيقًا جديدًا باسم Blog في مجلد blog وسيثبت اعتماديات gem الموجودة في GEMfile باستخدام الأمر bundle install. يمكنك الاطلاع على جميع الخيارات المتاحة في سطر الأوامر والتي يتقبّلها مولّد تطبيقات Rails وذلك من خلال تنفيذ الأمر: rails new -h بعد إنشاء تطبيق المدوّنة، توجّه في سطر الأوامر إلى المجلّد الخاص به: $ cd blog ستلاحظ أنّ مجلّد المدونة يتضمن بعض الملفات والمجلّدات التي تم إنشاؤها بصورة تلقائية والتي تمثّل العمود الفقري لتطبيق Rails. سينحصر الجزء الأكبر من عملنا ضمن مجلد app، ولكن لا بأس في الاطلاع بصورة سريعة على وظيفة هذه الملفات والمجلّدات: الملف أو المجلد الوظيفة /app يتضمن هذا المجلّد: المتحكّمات controllers، النماذج models، العروض views، الدوال المساعدة helpers، دوال البريد اﻹلكتروني mailers، القنوات channels، الوظائف jobs، والأصول assets الخاصّة بالتطبيق. سيتركّز عملنا ضمن هذا المجلد. /bin يتضمّن هذا المجلد شيفرات Rails المسؤولة عن تشغيل التطبيق ويمكن أن يتضمن شيفرات أخرى تستخدم في تثبيت وتحديث ونشر وتشغيل التطبيق. /config يتضمن الإعدادات الخاصة بمسارات التطبيق routes، وقاعدة البيانات وغير ذلك config.ru ملف إعدادات Rack يستخدم في خواديم Rack لتشغيل التطبيق عليها. /db يتضمّن مخطط قاعدة البيانات الحالية، إضافة إلى تهجيرات قاعدة البيانات. Gemfile, Gemfile.lock يتيح هذان الملفان تحديد اعتماديات gem المطلوبة لتطبيق Rails. يستخدم Bundler هذه الملفات. لمزيد من المعلومات توجّه إلى موقع Bundler الإلكتروني. /lib الوحدات الموسّعة الخاصة بالتطبيق. /log ملفات log الخاصة بالتطبيق. /public المجلد الوحيد الذي سيظهر على حاله بعد نشر التطبيق، ويتضمن الملفات الساكنة و ملفات الأصول المجمّعة. Rakefile يحدّد هذا الملف ويحمّل المهامّ التي يمكن تنفيذها بواسطة سطر الأوامر. يتم تعريف المهامّ ضمن مكوّنات Rails. ولإضافة مهامّ جديدة يجب عدم تعديل هذا الملف، بل إضافة ملفات إلى مجلّد lig/tasks. README.md الملفّ التعريفي الخاصّ بالتطبيق، ويمكن من خلاله تقديم نبذة تعريفية عن التطبيق والمهام التي يؤديها وطريقة التثبيت وغير ذلك من المعلومات. /test يضمّ هذا المجلد جميع الأمور المرتبطة بالاختبارات. /tmp يضمّ هذا المجلّد الملفّات المؤقتة (مثل ملفات الذاكرة المخبئية وملفات pid). /vendor ستجد هنا جميع شيفرات الطرف الثالث، وعادة ما يتضمن جواهر مطوّرة من قبل أشخاص أو شركات. gitignore. يخبر هذا الملف نظام التحكم في النسخ Git عن الملفات أو (الأنماط) التي ينبغي عليه تجاهلها. لتعرف المزيد راجع سلسلة دروس Git في الأكاديمية. مرحبًا Rails لنحاول في البداية إظهار بعض النصوص على الشاشة وبصورة سريعة، وللقيام بذلك، ستحتاج إلى تشغيل الخادوم الخاص بـ Rails. تشغيل خادوم Rails يتضمّن إطار العمل Rails خادوم ويب خاصًّا به وكل ما نحتاج إليه هو تشغيله وذلك من خلال تنفيذ الأمر التالي في سطر الأوامر ضمن مجلد blog: $ bin/rails server إن كنت تستخدم نظام ويندوز يجب تمرير الشيفرات في مجلد bin إلى مفسّر Ruby مباشرة: ruby bin\rails server تنفيذ هذا الأمر سيؤدي إلى تشغيل Puma، وهو خادوم ويب مضمّن بصورة افتراضية في إطار العمل Rails. حان الآن وقت الولوج إلى تطبيقنا من خلال المتصفح وذلك بالتوجه إلى الرابط http://localhost:3000/. ستظهر الصفحة التالية لتشير إلى نجاحنا في إنشاء أول مشروع على Ruby on Rails. المصدر: توثيقات Ruby on Rails.
  16. تعرّفنا في الدرس السابق على طريقة تثبيت إطار العمل Rails وبدأنا العمل على مشروعنا الأول وهو عبارة عن مدوّنة بسيطة، وقمنا بتشغيل الخادوم الخاص بإطار العمل. وفي هذا الدرس سنتعرّف على آلية عمل إطار العمل Rails من خلال مثال بسيط، ثم نشرع بعده ببناء مدونتنا البسيطة لنتعرف بصورة أكبر على العديد من المفاهيم التي يستند إليها هذا الإطار. آلية عمل إطار Rails سنتعرّف على آلية عمل إطار Ruby on Rails من خلال مثال بسيط نعرض فيه مجموعة من الكلمات في الصفحة الرئيسية لتطبيقنا، وللقيام بذلك سنحتاج إلى متحكّم Controller وعرض View. وظيفة المتحكّم هي استقبال الطلبات الواردة إلى التطبيق، وتربط المسارات Routes بين الطلبات والمتحكّمات. وغالبًا ما يكون هناك أكثر من مسار واحد لكل متحكّم، ويمكن للمسارات المختلفة أن تؤدّي إلى أحداث Actions مختلفة، ووظيفة الحدث هي جمع المعلومات اللازمة وتقديمها إلى العرض. أمّا وظيفة العرض فواضحة من اسمه، وهي عرض المعلومات التي حصل عليها من الحدث بصورة مقروءة للإنسان. من الضروري هنا الانتباه إلى أن عملية جمع المعلومات تتمّ ضمن المتحكّم وليس ضمن العرض، ومهمّة العرض الوحيدة هي عرض المعلومات. يستخدم إطار Rails لغة قوالب خاصّة في العروض تدعى eRuby (اختصار لـ Embedded Ruby) والتي تُعالج بواسطة دورة الطلب في Rails قبل أن تُرسل إلى المستخدم. سلنجأ إلى أداة المولّد generator لإنشاء متحكّم يحمل اسم Welcome يتضمّن حدثًا باسم index. اكتب الأمر التالي في سطر الأوامر: $ bin/rails generate controller Welcome index سيقوم Rails بإنشاء مسار وعدد من الملفات. create app/controllers/welcome_controller.rb route get 'welcome/index' invoke erb create app/views/welcome create app/views/welcome/index.html.erb invoke test_unit create test/controllers/welcome_controller_test.rb invoke helper create app/helpers/welcome_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/welcome.coffee invoke scss create app/assets/stylesheets/welcome.scss ما يهمّنا من هذه الملفات هما المتحكم والموجود في المسار app/controllers/welcome_controller.rb والعرض الموجود في المسار app/views/welcome/index.html.erb. افتح الملف app/views/welcome/index.html.erb في محرّر النصوص المفضّل لديك، واحذف محتوياته واستبدلها بالشيفرة التالية: <h1>Hello, Rails!</h1> بعد أن أنشئنا المتحكم والعرض يجب علينا إخبار Rails بالمسار الذي سيأخذ المستخدم إلى هذا العرض. نحن نرغب في حالتنا هذه أن يتم توجيه المستخدم إلى العرض عندما يتوجّه إلى العنوان http://localhost:3000، ولكن صفحة الترحيب تشغل هذا المسار في الوقت الحاضر. بعد ذلك يجب إخبار Rails بموقع الصفحة الرئيسية ليتمكّن من عرضها للمستخدم. افتح الملف config/routes.rb في محرّر النصوص: Rails.application.routes.draw do get 'welcome/index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end الشيفرة أعلاه موجودة في ملف المسارات والذي يتضمّن مدخلات DSL خاصّة (DSL اختصار لـ domain-specific language) والتي تخبر Rail بطريقة ربط الطلبات الواردة إلى التطبيق بالمتحكمات والأحداث. عدّل هذا الملف بالصورة التالية: Rails.application.routes.draw do get 'welcome/index' root 'welcome#index' end من خلال السطر root 'welcome#index' يربط إطار العمل Rails الطلبات الواردة إلى المسار الرئيسي في التطبيق مع الحدث index في المتحكّم Welcome، أما السطر get 'welcome/index' فيربط من خلاله Rails الطلبات الواردة إلى العنوان http://localhost:3000/welcome/index بنفس الحدث ونفس المتحكّم، وقد تمّ إنشاء هذه الشيفرة من قبل أداة المولّد. والآن شغّل الخادوم الخاص بـ Rails ثمّ توجّه في المتصفّح إلى العنوان http://localhost:3000، وستشاهد عبارة “Hello, Rails!” الموجودة في ملف app/views/welcome/index.html.erb وهذا يعني أن هذا المسار قد توجّه فعلًا إلى الحدث index في المتحكم Welcome والذي قام بدوره بتصيير العرض بصورة صحيحة. البدء بإنشاء المدوّنة بعد أن تعرّفنا على المتحكّمات والأحداث والعروض، لنبدأ العمل على مدوّنتنا. سننشئ الآن ما يسمى في إطار العمل Rail بالمورد resourse، والمورد هو مصطلح يعبّر عن مجموعة من العناصر المتشابهة، مثل المقالات، الأشخاص أو الحيوانات. ويمكن إنشاء create وقراءة read وتحديث update وإلغاء destroy العناصر في المورد، وتسمى هذه العمليات بعمليات CRUD. يقدّم Rails تابعًا باسم resources يمكن استخدامه للإفصاح عن مورد بنمط REST القياسي. يجب إضافة مورد المقالة إلى ملف config/routes.rb وكما يلي: Rails.application.routes.draw do get 'welcome/index' resources :articles root 'welcome#index' end والآن إن قمت بتنفيذ الأمر bin/rails routes فستشاهد جميع المسارات الخاصّة بجميع الأحداث التي تتّصف بنمط REST. سنتعرّف على معنى عمود prefix وبقية الأعمدة في وقت لاحق، ولكن لاحظ أنّ Rails قد خمّن صيغة المفرد (article) واستخدمها في السياق الصحيح. $ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create new_article GET /articles/new(.:format) articles#new edit_article GET /articles/:id/edit(.:format) articles#edit article GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update PUT /articles/:id(.:format) articles#update DELETE /articles/:id(.:format) articles#destroy root GET / welcome#index سنعمل الآن على إضافة خاصيتي إنشاء المقالات وعرضها، وستكون الاستمارة Form المسؤولة عن ذلك بالشكل التالي: قد تبدو الاستمارة بدائيًّة ولكنّها كافية في الوقت الحاضر، وسنعمل على تحسين مظهرها فيما بعد. إنشاء المتحكّمات والمسارات اللازمة في البداية يجب اختيار المسار الذي سيوجّه المستخدم إلى استمارة إنشاء المقالة الجديدة، وسنستخدم المسار /articles/new للقيام بهذه المهمّة، بعدها يصبح بميسور التطبيق أن يتلقّى الطلبات على هذا المسار. توجّه الآن في متصفحك إلى الرابط http://localhost:3000/articles/new وستتلقّى الخطأ التالي: يحدث هذا الخطأ لأنّ المسار بحاجة إلى متحكّم يرسل إليه الطلب؛ لذا سنقوم بإنشاء متحكّم باسم ArticlesController، وذلك من خلال تنفيذ الأمر التالي: $ bin/rails generate controller Articles افتح الملف الذي قمت بإنشائه app/controllers/articles_controller.rb وسترى متحكّمًا فارغًا: class ArticlesController < ApplicationController end المتحكّم عبارة عن صنف Class موروث من ApplicationController وسنقوم بتعريف التوابع ضمن هذا الصنف والتي ستمثل الأحداث الخاصّة بهذا المتحكم، وهذه الأحداث هي المسؤولة عن تنفيذ عمليات CRUD على المقالات الموجودة في تطبيقنا. إن أعدت تحميل الصفحة ستتلقّى خطأً جديدًا: يشير هذا الخطأ إلى عدم قدرة Rails على إيجاد الحدث new ضمن المتحكّم ArticlesController الذي قمنا بإنشائه للتوّ. وهذا عائد إلى أنّ المتحكّمات تكون فارغة عند إنشائها إلا إذا حدّدنا الأحداث المطلوبة خلال عملية إنشاء المتحكّم. ولتعريف حدث جديد بصورة يدوية، سنحتاج فقط إلى تعريف تابع جديد ضمن المتحكم. افتح الملف app/controllers/articles_controller.rb وضمن الصنف ArticlesController عرّف تابعًا جديدًا وكما يلي: class ArticlesController < ApplicationController def new end end والآن أعد تحميل الصفحة في المتصفّح وستتلقّى خطأً آخر: يظهر هذا الخطأ لأنّ Rails يتوقّع أنه يجب أن تمتلك الأحداث الصرفة المشابهة لهذا الحدث عروضًا ترتبط معها لعرض المعلومات التي تتضمنها، ونظرًا لعدم وجود أي عرض مرتبط بهذا الحدث، أطلق Rails هذا الخطأ. لنطّلع على رسالة الخطأ الكاملة: لنستعرض النص السابق سريعًا، ونفهم مضمونه بصورة جيدة. يحدّد الجزء الأول من نصّ الخطأ القالب المفقود، وفي هذه الحالة القالب المفقود هو articles/new. يبدأ Rails بالبحث عن هذا القالب، وإن لم يفلح في العثور عليه فإنه يحاول تحميل قالب يدعى application/new وذلك لأنّ المتحكّم ArticleController هو صنف موروث من المتحكّم ApplicationController. يتضمن الجزء الثاني من رسالة الخطأ request.formats والذي يحدّد صيغة القالب الذي سيتم عرضه كاستجابة للطلب الذي تلقّاه التطبيق، وقد تم اختيار صيغة text/html لأنّنا طلبنا هذه الصفحة بواسطة المتصفح؛ لذا يبحث Rails عن قوالب HTML. أما request.variant فيحدد طبيعة الأدوات المادّية physical devices التي سيتم تقديمها مع الطلب وتساعد Rails في تحديد القالب الذي سيستخدمه في الاستجابة، وهو فارغ نظرًا لعدم توفّر المعلومات. أبسط قالب يمكن أن يعمل في هذه الحالة هو القالب الموجود في المسار app/views/articles/new.html.erb. هذه اللاحقة مهمّة للغاية: فالجزء الأول من اللاحقة (.html) يعبّر عن صيغة القالب، أمّا الجزء الثاني (.erb) فيمثّل المعالج handler الذي سيتم استخدامه في تصيير القالب. يحاول Rails البحث عن قالب يحمل الاسم articles/new ضمن المجلد app/views. يجب أن تكون صيغة هذا القالب هي HTML حصرًا، وسيكون erb المعالج الافتراضي لـ HTML. يستخدم Rails عددًا من المعالجات مثل: builder والمستخدم في إنشاء قوالب XML، وcoffee الذي يستخدم لغة CoffeeScript لبناء قوالب JavaScript. بما أنّنا نرغب في بناء استمارة HTML جديدة فسنستخدم لغة ERB والتي تتيح لنا تضمين لغة Ruby في HTML. إذًا سيكون اسم القالب articles/new.html.erb وسيكون ضمن المجلد app/views الخاص بالتطبيق. أنشئ ملفًّا جديدًا باسم new.html.erb في المسار app/views/articles وأضف إليه ما يلي: <h1>New Article</h1> أعد تحميل الصفحة وستلاحظ ظهور العنوان في رأس الصفحة، وهذا يعني أن هناك تناغمًا تامًّا بين كلّ من المسار والمتحكم والحدث والعرض. الاستمارة الأولى سنستخدم منشئ النماذج form builder لإنشاء الاستمارة الأولى في هذا القالب. يمكن استخدام منشئ النماذج الرئيسي في Rails باستخدام التابع المساعد form_for. أضف الشيفرة التالية في الملف app/views/articles/new.html.erb: <%= form_for :article do |f| %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> أعد تحميل الصفحة وستلاحظ ظهور نفس الاستمارة التي عرضناها في المثال السابق. كما تلاحظ فإنّ بناء الاستمارات في Rails أمر سهلٌ للغاية. عندما نستدعي التابع form_for فإننا نمرّر إليه عنصرًا يحدّد الهدف من هذه الاستمارة، والهدف في حالتنا هذه هو :article. يُستخدم الكائن FormBuilder والذي مثّلناه بـ f لبناء عنصري label وحقلي نصوص text fields لكلّ من عنوان المقال ومتنها. وفي النهاية استدعينا التابع submit لإنشاء زرّ اﻹرسال الخاصّ بالاستمارة. ولكن تعاني هذه الاستمارة من مشكلة صغيرة. لو تفحّصت شيفرة HTML التي تم توليدها من خلال الشيفرة السابقة فستلاحظ أن خاصية action التابعة للاستمارة تشير إلى المسار articles/new وهذا المسار هو نفسه الذي يقودنا إلى هذه الصفحة، والمفروض أن يستخدم هذا المسار لعرض استمارة إنشاء مقالة جديدة لا غير. إذًا يجب أن تستخدم الاستمارة مسارًا آخر، ويمكن القيام بذلك بسهولة من خلال استخدام الخيار :url في form_for. عادة ما يحمل الحدث المسؤول عن إرسال مقال جديد اسم “create”، لذا يجب توجيه الاستمارة إلى هذا الحدث. عدّل السطر الذي يتضمن form_for في ملف app/views/articles/new.html.erb كما يلي: <%= form_for :article, url: articles_path do |f| %> في هذا المثال تم تمرير الدالة المساعدة articles_path إلى الخيار :url، ولنتعرّف على نتيجة هذا التعديل سنلقي نظرة على مخرجات الأمر bin/rails routes في سطر اﻷوامر: $ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create new_article GET /articles/new(.:format) articles#new edit_article GET /articles/:id/edit(.:format) articles#edit article GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update PUT /articles/:id(.:format) articles#update DELETE /articles/:id(.:format) articles#destroy root GET / welcome#index توجّه الدالة المساعدة articles_pat الاستمارة إلى نمط URI المرتبط لاحقة articles وسيرسل الاستمارة - تلقائيًا - طلبًا من نوع POST إلى المسار، والذي يرتبط بالحدث create التابع للمتحكّم ArticlesController. بعد أن أنشأنا الاستمارة وعرّفنا المسار المرتبط به، أصبح باﻹمكان تعبئة حقول الاستمارة والضغط على زرّ اﻹرسال لبدء عملية إنشاء مقال جديد، ولكن عند إرسال المقال ستتلقّى الخطأ المتوقَّع التالي: علاج هذا الخطأ بسيط وهو إنشاء الحدث create ضمن المتحكّم ArticlesController. إنشاء المقالات سنقوم الآن بتعريف الحدث create ضمن صنف ArticlesController في الملف app/controllers/articles_controller.rb بعد الحدث new وكما يلي: class ArticlesController < ApplicationController def new end def create end end إن أعدت إرسال الاستمارة مرة أخرى فستلاحظ عدم حدوث أي تغيير في الصفحة. لا تقلق، هذا الأمر عائد إلى أنّ Rails يعيد الاستجابة “204 No Content” لأي حدث لا يحدّد الاستجابة المطلوبة. وقد أضفنا الحدث create دون تحديد الاستجابة المطلوبة منه، وهي في حالتنا هذه، إنشاء مقالة جديدة في قاعدة البيانات. عند إرسال الاستمارة يتم إرسال الحقول الخاصة بها إلى Rails على هيئة معاملات parameters، يمكن الإشارة إليها في الأحداث التابعة للمتحكّم وذلك لإنجاز مهّمة ما، ولتعرف كيف تبدو هذه المعاملات عدّل حدث create بالصورة التالية: def create render plain: params[:article].inspect end يأخذ التابع render هنا جدول تقطيع Hash بسيط مع المفتاح :plain والقيمة هي تابع params [:article].inspect. توابع params هي الكائن الذي يمثّل المعامل (أو الحقل) المأخوذ من الاستمارة. ويعيد تابع params كائن من نوع ActionController::Parameters والذي يتيح لنا الوصول إلى مفاتيح جدول التقطيع من خلال السلاسل النصّيّة Strings أو الرموز Symbols. وفي حالتنا هذه، فإن المعاملات المهمّة هي المعاملات المأخوذة من الاستمارة. لتوضيح عمل توابع params، إليك المثال التالي: في عنوان URL هذا: http://www.example.com/?username=dhh&email=dhh@mail.com فإن params[:username] تحمل القيمة “dhh” و params[:email] تحمل القيمة “dhh@mail.com”. والآن أعد إرسال الاستمارة مرة أخرى وستشاهد شيئًا مماثلًا لما يلي: <ActionController::Parameters {"title"=>"First Article!", "text"=>"This is my first article."} permitted: false> يعرض هذا الحدث المعاملات الخاصة بالمقالة والمأخوذة من الاستمارة، ولكن ليس هذا ما نريده بالضبط، فنحن نشاهد المعاملات ولكنّها لا تقدّم أي فائدة تذكر في حالتها هذه. في الدرس القادم سنتعرّف على النماذج Models في إطار العمل Rails وسنستخدمها في إضافة مقالة جديدة إلى قاعدة البيانات التابعة للتطبيق. المصدر: توثيقات Ruby on Rails.
  17. يتناول هذا المقال تثبيت بيئة تطوير Ruby on Rails على الإصدار 16.04 من أوبونتو. في أغلب الأحوال ستُنفَّذ الشفرة البرمجيّة التي تكتبها على خادوم لينكس، وأوبونتو هي إحدى توزيعات لينكس الأسهل استخداما وتوجد الكثير من الموارد عنها على الشبكة. تثبيت Ruby أول ما يجب علينا فعله هو تثبيت الاعتماديات Dependencies التي يحتاجها Ruby للعمل. نبدأ أولا بتحديث فهرس الحزم ثم تثبيت الاعتماديّات: sudo apt update sudo apt install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev nodejs الخطوة الموالية هي تثبيت Ruby. توجد طرق عدّة للتثبيت، سنستخدم rbenv وهي أداة خفيفة وسريعة لإدارة إصدارات Ruby. نبدأ بتثبيت rbenv : cd git clone https://github.com/rbenv/rbenv.git ~/.rbenv echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc echo 'eval "$(rbenv init -)"' >> ~/.bashrc exec $SHELL الأداة rbenv جاهزة الآن. سنثبّت ruby-build وهي إضافة تعمل مع الأداة rbenv وتتيح تثبيت إصدارات عدّة من Ruby في مجلّدات مختلفة على نفس النظام. git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc exec $SHELL ثم نثبّت الإصدار 2.4.0 من Ruby: TMPDIR=~/tmp/ rbenv install 2.4.0 rbenv global 2.4.0 يُثبّت الأمر الأول الإصدار المطلوب ضمن مجلّد العمل حيث نُفِّذ الأمر؛ في ما يضبط الأمر الثاني إصدار Ruby المبدئي، أي الإصدار الذي سيُستخدَم عند عدم تحديد إصدار. للتأكد من تثبيت Ruby والإصدار المثبّت ننفذ الأمر: ruby -v الذي يُظهر نتيجة تشبه التالي: ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux] الخطوة الأخيرة هي تثبيت Bundler الذي يوفّر بيئة تطوير متجانسة لـ Ruby عبر تتبّع الاعتماديات وتثبيت الإصدارات المطلوبة منها بالضبط: gem install bundler وتنفيذ الأمر لأخذ التثبيت الجديد في الحسبان: rbenv rehash تثبيت Rails يتضمّن إطار العمل Rails الكثير من الاعتماديات، لذا سنحتاج لبيئة تنفيذ JavaScript مثل NodeJS التي ستمكّننا من استخدام مكتبات تتيح إمكانيّة جمع وضغط ملفات جافاسكريبت من أجل الحصول على بيئة تطوير أسرع. نثبّت NodeJS من المستودع الرسمي على النحو التالي: curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash - sudo apt install -y nodejs ثم نثبّت Rails: gem install rails -v 5.0.1 ثم ننفذ الأمر التالي ليمكننا استخدام Rails: rbenv rehash Rails مثبّت الآن، يمكننا التأكّد من ذلك بتنفيذ الأمر التالي الذي يُظهر رقم الإصدار المثبت: rails -v إعداد قاعدة بيانات MySQL يأتي Rails مبدئيا بقاعدة بيانات sqlite3؛ إلا أن هذا النوع من قواعد البيانات لا يناسب تطبيقات كثيرة. يمكن في هذه الحالة إعداد Rails للعمل مع قاعدة بيانات MySQL (أو PostgreSQL). نثبّت خادوم وعميل MySQL من المستودعات الرسمية لأوبونتو: sudo apt install mysql-server mysql-client libmysqlclient-dev سيُطلَب منك خلال التثبيت إنشاءُ كلمة سر خاصة بالحساب الإداري root (انتبِه إلى أنّه حساب خاص بنظام MySQL لإدارة قواعد البيانات، وليس لنظام التشغيل أوبونتو). تتضمّن الحزمة libmysqlclient-dev الملفات الضرورية لتثبيت الاعتماديّات التي يحتاجها Rails للاتصال بقاعدة بيانات MySQL. ننفّذ بعد تثبيت MySQL الأمريْن التاليّيْن لتحضير قاعدة البيانات لبيئة الإنتاج (يمكنك تجاوز هذه الخطوة إن كنت تثبّت Rails على حاسوب شخصي): sudo mysql_install_db sudo mysql_secure_installation سيُطلب منك النّظام إدخال كلمة سرّ الحساب الجذر في MySQL؛ ثمّ يسألك ما إذا كنتَ تُريد تغييرها. أدخل حرف n (دلالةً على “no” أي “لا”) إن لم تكن ترغبُ في ذلك. بالنسبة لبقية الأسئلة يمكنك قبول القيم المبدئية بالنقر على زرّ Enter. أول تطبيق Rails حان الوقت الآن لتشغيل أول تطبيق Ruby on Rails. ننشئ تطبيقا باسم myapp يستخدم قاعدة البيانات MySQL: rails new myapp -d mysql انتظر اكتمال إنشاء التطبيق الذي قد يستغرق وقتا، ثم انتقل إلى مجلد التطبيق: cd myapp عدّل الملف config/database.yml بإضافة كلمة سر الحساب الإداري لقاعدة البيانات (أعددناها أثناء تثبيت MySQL، يمكنك تركها فارغة إن لم تكن أعددتَ كلمة سر لـ MySQL): default: &default adapter: mysql2 encoding: utf8 pool: 5 username: root password: socket: /var/run/mysqld/mysqld.sock اكتب كلمة السر أمام التعليمة password ضمن المقطع السابق الموجود أعلى الملف بعد التعليقات الأولى (تبدأ التعليقات بالعلامة #)؛ ثم نفّذ الأمر التالي لإنشاء قاعدة البيانات التي سيعمل عليها التطبيق: rake db:create النتيجة: Created database 'myapp_development' Created database 'myapp_test' إن واجهك خطأ Access denied for user 'root'@'localhost' (using password: NO) فهذا يعني أنك لم تكتب كلمة السر بطريقة صحيحة. نشغل خادوم التطوير: rails server يمكنك بعد تشغيل خادوم التطوير إدخال العنوان http://localhost:3000/ في المتصفح لمعاينة صفحة التطبيق المبدئي في Rails. إعداد Git هذه الخطوة اختيارية ولكنه تفيدك إن كنت تخطّط لاستعمال Git لإدارة إصدارات المشروع وربطه بحسابك على Github. أبدل YOUR NAME وYOUR@EMAIL.com في الأوامر أدناه على التوالي باسمك والبريد الذي استخدمته للتسجيل على Github. git config --global color.ui true git config --global user.name "YOUR NAME" git config --global user.email "YOUR@EMAIL.com" ssh-keygen -t rsa -b 4096 -C "YOUR@EMAIL.com" يُولّد الأمر الأخير في الأوامر أعلاه زوج مفاتيح SSH سنستخدمها في ما بعد للاتصال بحسابنا على Github. يضع الأمر ssh-keygen مبدئيا زوج المفاتيح على المسار ssh./~. نأخذ المفتاح العمومي الذي يوجد بملف المفتاح ذي الامتداد pub، ويُسمّى مبدئيا id_rsa.pub وننسخه ضمن مفاتيح SSH على حسابنا في Github الموجودة هنا. انقر على الزّر New SSH Key، أعط للمفتاح اسما تختاره ثم ألصق في الحقل الثاني محتوى الملف ssh/id_rsa.pub/~ الذي يمكن الحصول عليه بالأمر التالي، ثم انقر على زرّ Add SSH Key لاعتماد المفتاح: cat ~/.ssh/id_rsa.pub يمكنك التأكد من نجاح العملية بتنفيذ الأمر التالي: ssh -T git@github.com يجب أن تظهر لك رسالة تشبه التالي (يُذكَر فيها اسم حسابك على Github): Hi Zeine77! You've successfully authenticated, but GitHub does not provide shell access. ترجمة - بتصرّف - للمقال Setup Ruby On Rails on Ubuntu 16.04 Xenial Xerus.