اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'ريلز'.

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

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

نوع المحتوى


التصنيفات

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

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

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

التصنيفات

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

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

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

التصنيفات

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

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

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

  1. تعرفنا في الدرس السابق على الارتباطات وأنواعها وسنتابع في هذا الدرس أهم النصائح والخدع والتحذيرات. 3 نصائح، خدع وتحذيرات يجب عليك معرفة هذه الأشياء لتستخدم ارتباطات Active Record بشكل أفضل وأكفأ في تطبيقات Rails: التحكم في التخزين المؤقت. تجنب تضارب الأسماء. تحديث المخطط. التحكم في نطاق (scope) الارتباط. الارتباطات ثنائية الاتجاه. 3.1 التحكم في التخزين المؤقت تبنى جميع الارتباطات حول التخزين المؤقت، والذي يحافظ على نتيجة الاستعلام الأخيرة متاحة للعمليات القادمة، يتم تقاسم ذاكرة التخزين المؤقت عبر الأساليب، على سبيل المثال: author.books # retrieves books from the database author.books.size # uses the cached copy of books author.books.empty? # uses the cached copy of books لكن ماذا لو أردنا إعادة تحميل التخزين المؤقت، لأن البيانات قد تتغير عن طريق أجزاء اخرى من التطبيق؟ فقط أدعو إعادة التحميل في الارتباط: author.books # retrieves books from the database author.books.size # uses the cached copy of books author.books.reload.empty? # discards the cached copy of books # and goes back to the database 3.2 تجنب تضارب الأسماء أنت لا تملك حرية كاملة في اختيار الاسم الذي تريده للارتباطات، لإن إنشاء الارتباط سيضيف أسلوب بهذا الاسم إلى النموذج، ومن السيئ إعطاء اسم ارتباط مستخدم بالفعل لمثيل أسلوب ActiveRecord::Base، فأسلوب الارتباط سيتجاوز أسلوب الأساس وسيكسر الأشياء، من الأسماء السيئة: attributes و connection. 3.3 تحديث المخطط الارتباطات مفيدة للغاية لكنها ليست سحرية، فأنت مسؤول عن المحافظة على مخطط قاعدة البيانات ليطابق الارتباطات، وهذا يعني شيئين في الممارسة العملية حسب نوع الارتباطات التي تصنعها، فارتباطات belongs_to تحتاج إلى إنشاء مفاتيح خارجية، ولارتباطات has_and_belongs_to_many ستحتاج إلى إنشاء جدول الضم المناسب. 3.3.1 إنشاء مفاتيح خارجية لارتباطات belongs_to عندما تعلن عن ارتباط belongs_to، ستحتاج إلى إنشاء مفاتيح خارجية حسب الحاجة، فعلى سبيل المثال، فكر في هذا النموذج: class Book < ApplicationRecord belongs_to :author end يحتاج هذا الإعلان إلى أن يُدعم عن طريق مفتاح خارجي مناسب مُعلن في جدول books: class CreateBooks < ActiveRecord::Migration[5.0] def change create_table :books do |t| t.datetime :published_at t.string :book_number t.integer :author_id end end end إذا أنشئت ارتباط بعد وقت من إنشاء النموذج الأساسي، ستحتاج إلى تذكر إنشاء تهجير add_column لتوفير مفتاح الخارجي الضروري. من الممارسات الجيدة إضافة فهرس (index) إلى المفتاح الخارجي لتحسين أداء الطلبات وإضافة قيد على المفتاح الخارجي لضمان سلامة البيانات المرجعية: class CreateBooks < ActiveRecord::Migration[5.0] def change create_table :books do |t| t.datetime :published_at t.string :book_number t.integer :author_id end add_index :books, :author_id add_foreign_key :books, :authors end end 3.3.2 إنشاء جداول الضم لارتباطات has_and_belongs_to_many إذا أنشئت ارتباط has_and_belongs_to_many، فستحتاج إلى إنشاء جدول الضم (joining table)، إذا لم يحدد اسم جدول الضم بشكل صريح عن طريق خيار join_table:، فسينشئ Active Record الاسم عن طريق استخدام الكتاب المعجمي لأسماء الصنف، فالضم بين نماذج المؤلف والكتاب سيُكوّن اسم جدول الضم بشكل افتراضي هو “authors_books”، لأن A تأتي قبل B في الترتيب المعجمي. مهما كان الاسم، يجب عليك إنشاء جدول الضم يدويا مع التهجير المناسب، فعلى سبيل المثال: class Assembly < ApplicationRecord has_and_belongs_to_many :parts end class Part < ApplicationRecord has_and_belongs_to_many :assemblies end يجب أن تدعم هذه بالتهجير لإنشاء جدول assemblies_parts، والذي يجب أن يُنشئ دون مفتاح رئيسي: class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] def change create_table :assemblies_parts, id: false do |t| t.integer :assembly_id t.integer :part_id end add_index :assemblies_parts, :assembly_id add_index :assemblies_parts, :part_id end end مررنا id: false إلى create_table لأن الجدول لا يُمثل نموذج، وهذا ضروري للارتباط ليعمل بشكل صحيح. قد تلاحظ تصرفات غريبة في ارتباط has_and_belongs_to_many مثل مُعرّفات نموذج سيئة أو استثناءات حول تضارب المعرّفات، لكنها نادرا ما تحدث. يمكنك استخدام أسلوب create_join_table أيضًا. class CreateAssembliesPartsJoinTable < ActiveRecord::Migration[5.0] def change create_join_table :assemblies, :parts do |t| t.index :assembly_id t.index :part_id end end end 3.4 التحكم في نطاق scope الارتباط تبحث الارتباطات افتراضيا عن الكائنات ضمن نطاق الوحدة (module) الحالية، وهذا جيد عندما تعلن عن نماذج Active Record ضمن الوحدة، فعلى سبيل المثال: module MyApplication module Business class Supplier < ApplicationRecord has_one :account end class Account < ApplicationRecord belongs_to :supplier end end end وهذا سيعمل بشكل صحيح، لأن كل من صنف المُورّد والحساب تم تعريفهما داخل نفس النطاق، لكن الشيفرة البرمجية التالية لن تعمل، لأنه عُرّف المُورّد والحساب في نطاقات مختلفة: module MyApplication module Business class Supplier < ApplicationRecord has_one :account end end module Billing class Account < ApplicationRecord belongs_to :supplier end end end لربط نموذج مع نموذج في مساحة اسم (namespace) مختلفة يجب عليك تحديد اسم الصنف كاملا في إعلان الارتباط: module MyApplication module Business class Supplier < ApplicationRecord has_one :account, class_name: "MyApplication::Billing::Account" end end module Billing class Account < ApplicationRecord belongs_to :supplier, class_name: "MyApplication::Business::Supplier" end end end 3.5 الارتباطات ثنائية الاتجاه من الطبيعي أن تعمل الارتباطات في كلا الاتجاهين، وهذا الأمر يتطلب إعلان في نموذجين مختلفين: class Author < ApplicationRecord has_many :books end class Book < ApplicationRecord belongs_to :author end سيحاول Active Record تلقائيا معرفة أن هذيّن النموذجين يشتركان في ارتباط ثنائي الاتجاه بناءا على اسم الارتباط، وبهذه الطريقة سيحمّل Active Record نسخة واحدة من كائن Author وبذلك سيصبح تطبيقك أكثر كفاءة ويمنع البيانات غير المتناسقة: a = Author.first b = a.books.first a.first_name == b.author.first_name # => true a.first_name = 'David' a.first_name == b.author.first_name # => true يدعم Active Record التعرف التلقائي لأغلب الارتباطات مع الأسماء القياسيّة، ومع ذلك لن يُعرّف Active Record الارتباطات ثنائية الاتجاه تلقائيا إذا احتوت على أي من الخيارات التالية: conditions: through: polymorphic: class_name: foreign_key على سبيل المثال، فكر في إعلان النماذج التالية: class Author < ApplicationRecord has_many :books end class Book < ApplicationRecord belongs_to :writer, class_name: 'Author', foreign_key: 'author_id' end لن يتعرف Active Record تلقائيا على الارتباط ثنائي الاتجاه: a = Author.first b = a.books.first a.first_name == b.writer.first_name # => true a.first_name = 'David' a.first_name == b.writer.first_name # => false يوفر Active Record خيار :inverse_of حتى تتمكن من الإعلان الارتباطات ثنائية الاتجاه بشكل صريح: class Author < ApplicationRecord has_many :books, inverse_of: 'writer' end class Book < ApplicationRecord belongs_to :writer, class_name: 'Author', foreign_key: 'author_id' end من خلال تضمين خيار :inverse_of في إعلان ارتباط has_many، سيتعرّف Active Record على ارتباط ثنائي الاتجاه: a = Author.first b = a.books.first a.first_name == b.writer.first_name # => true a.first_name = 'David' a.first_name == b.writer.first_name # => true توجد بعض القيود على دعم inverse_of: : لا تعمل مع ارتباطات through: لا تعمل مع ارتباطات polymorphic: لا تعمل مع ارتباطات as: وسنتابع في الدرس القادم التعرّف على مرجع الارتباط المفصّل. المصدر: توثيقات Ruby on Rails
  2. بعد برمجتك لموقعك باستخدام روبي وإطار ريلز، حان الوقت الآن لنشر التطبيق على خادم (server) ليستطيع زوارك الوصول إليه. سنعتمد في هذا الفيديو على أحد التطبيقات الذي طورناه في دورة تعلم تطوير تطبيقات الويب باستخدام روبي: https://academy.hsoub.com/learn/ruby-web-application-development/ مستودع التطبيق: https://github.com/HsoubAcademy/rails-twitter-web-app نوفر في موسوعة حسوب توثيقًا كاملًا للغة روبي وإطار العمل ريلز باللغة العربية: https://wiki.hsoub.com/Ruby https://wiki.hsoub.com/Rails
  3. تعرفنا في الدرس السابق على أهم الخدع، النصائح والتحذيرات وسنتابع في هذا الدرس مرجع الارتباط المفصّل. 4 مرجع الارتباط المفصّل ستجد في الأقسام التالية تفاصيل حول كل نوع من أنواع الارتباط، بما في ذلك الأساليب التي تضيفها والخيارات التي يمكنك استخدامها عند إعلان الارتباط. 4.1 مرجع ارتباط belongs_to ينشئ ارتباط belongs_to تطابق واحد لواحد (one-to-one) مع نموذج آخر، بمصطلحات قاعد البيانات، يعني هذا الارتباط أن هذا الصنف يحتوي على مفتاح خارجي، إذا كان الصنف الآخر يحتوي على مفتاح خارجي، فيجب عليك في هذه الحالة استخدام has_one بدلًا من ذلك. 4.1.1 أساليب مضافة بواسطة belongs_to عند إعلان ارتباط belongs_to، فسيحصل الصنف المعلن على 5 أساليب مرتبطة بالارتباط: association association=(associate) ({} = build_association(attributes ({} = create_association(attributes ({} = create_association!(attributes reload_association في جميع هذه النماذج، سيُستبدل الارتباط مع symbol الذي مُرر كمعامل أول إلى belongs_to، فعلى سبيل المثال، هذا الإعلان: class Book < ApplicationRecord belongs_to :author end سيحصل كل مثيل نموذج Book على هذه الأساليب: author author= build_author create_author create_author! reload_author 4.1.1.1 association يرجع أسلوب association الكائن المقترن إذا وجد، وإلا فسيُرجع nil. @author = @book.author إذا أُسترد الكائن من قاعدة البيانات لهذا الكائن، ستُرجع النسخة المخبئة، ولتجاوز هذا السلوك (وإجبار القراءة من قاعدة البيانات)، استدعي reload_association# في كائن الأب. @author = @book.reload_author 4.1.1.2 (association=(associate يعيّن أسلوب association= كائن مرتبط لهذا الكائن، في ما وراء الكواليس، يعني هذا استخراج المفتاح الأساسي من الكائن المرتبط وتعيين قيمة المفتاح الخارجي للكائن لنفس قيمته. @book.author = @author 4.1.1.3 ({} = build_association(attributes يُرجع أسلوب build_association كائن جديد لنوع المرتبط، هذا الكائن سيُنشئ من السمات المُمرّرة وسيُعين الارتباط من خلال المفتاح الخارجي لكن لن بعد يحفظ بعد الكائن المرتبط. @author = @book.build_author(author_number: 123, author_name: "John Doe") 4.1.1.4 ({} = create_association(attributes سيرجع أسلوب create_association كائن جديد لنوع المرتبط، هذا الكائن سيُنشئ من السمات المُمررة وسيُعين الارتباط من خلال المفتاح الخارجي، وبمجرد تمرير جميع عمليات التحقيق validations المحددة على النموذج المرتبط، سيُحفظ الكائن المرتبط. @author = @book.create_author(author_number: 123, author_name: "John Doe") 4.1.1.5 ({} = create_association!(attributes يعمل كما create_association في الأعلى، لكنه يُصدر ActiveRecord::RecordInvalid إذا كان السجل record غير صالح. 4.1.2 خيارات لـ belongs_to في حين يستخدم Rails افتراضات ذكية ستعمل بشكل صحيح في أغلب الأحيان، ستحتاج في بعض الأحيان إلى تخصيص مرجع سلوك ارتباط belongs_to، ويمكن تحقيق هذه التخصيصات بسهولة عن طريق تمرير الخيارات وكتل النطاق (scope) عند إنشاء الارتباط، فعلى سبيل المثال، هذا الارتباط يستخدم هذين الخيارين: class Book < ApplicationRecord belongs_to :author, dependent: :destroy, counter_cache: true end يدعم ارتباط belongs_to هذه الخيارات: autosave: class_name: counter_cache: dependent: foreign_key: primary_key: inverse_of: polymorphic: touch: validate: optional: 4.1.2.1 autosave: إذا عيّنت خيار autosave: إلى true، فسيحفظ Rails جميع أعضاء المحمّلين وسيدمر الأعضاء الذين وضع عليهم علامة التدمير كلما حفظت كائن الأب. 4.1.2.2 class_name: إذا لم يكن بالإمكان اشتقاق اسم النموذج الآخر من اسم الارتباط، يمكنك استخدام خيار class_name: لتوفير اسم النموذج، فعلى سبيل المثال، إذا كان الكتاب ينتمي إلى المؤلف، لكن اسم فعلي للنموذج الذي يحتوي على المؤلفين هو Patron: class Book < ApplicationRecord belongs_to :author, class_name: "Patron" end 4.1.2.3 counter_cache: يمكنك استخدام خيار counter_cache: لتجعل عملية العثور على عدد الكائنات التابعة أكثر كفاءة، على سبيل المثال هذه النماذج: class Book < ApplicationRecord belongs_to :author end class Author < ApplicationRecord has_many :books end مع هذه الإعلانات، يتطلب طلب قيمة author.books.size@ الاتصال بقاعدة البيانات لتنفيذ استعلام (*)COUNT، ولنتجنب هذا الاتصال، يمكنك إضافة ذاكرة مؤقتة لتخزين العدد إلى نموذج الانتماء: class Book < ApplicationRecord belongs_to :author, counter_cache: true end class Author < ApplicationRecord has_many :books end مع هذا الإعلان، سيُبقي Rails قيمة الذاكرة المؤقتة مُحدّثة، وسيجيب بتلك القيمة عند الطلب من أسلوب size. على الرغم من تحديد خيار counter_cache: في النموذج الذي يتضمن إعلان belongs_to، يجب إضافة العمود الحالي إلى النموذج المرتبط (has_many)، في الحالة أعلاه، ستحتاج إلى إضافة عمود باسم books_count إلى نموذج Author. يمكنك تجاوز اسم الافتراضي للعمود من خلال تحديد اسم العمود المخصص في إعلان counter_cache بدلا من true، فعلى سبيل المثال، لاستخدام count_of_books بدلا من books_count: class Book < ApplicationRecord belongs_to :author, counter_cache: :count_of_books end class Author < ApplicationRecord has_many :books end 4.1.2.4 :dependent إذا قمت بتعيين خيار dependent: إلى: destroy:، عند تدمير الكائن، ستُستدعى destroy على الكائنات المرتبطة. delete:، عند تدمير الكائن، ستُحذف جميع الكائنات المرتبطة بها مباشرة من قاعدة البيانات دون استدعاء أسلوب destroy. 4.1.2.5 foreign_key: بالاتفاق، يفترض Rails أن العمود المستخدم لحمل المفتاح الخارجي في هذا النموذج هو اسم الارتباط مع إضافة بادئة id_، خيار foreign_key: يُتيح لك تعيين اسم المفتاح الخارجي مباشرة: class Book < ApplicationRecord belongs_to :author, class_name: "Patron", foreign_key: "patron_id" end 4.1.2.6 primary_key: بالاتفاق، يفترض Rails أن عمود id يُستخدم لاحتواء المفتاح الرئيسي للجداول، ويسمح لك خيار primary_key: بتحديد عمود مختلف. على سبيل المثال، إذا كان لدينا جدول users مع guid كمفتاح رئيسي، أردنا جدول todos منفصل لاحتواء المفتاح الخارجي user_id في عمود guid، ثم يمكننا استخدام primary_key لتحقيق ما يشابه هذا: class User < ApplicationRecord self.primary_key = 'guid' # primary key is guid and not id end class Todo < ApplicationRecord belongs_to :user, primary_key: 'guid' end عند تنفيذ user.todos.create@ فستكون قيمة user_id في سجل todo@ كقيمة guid في user@. 4.1.2.7 inverse_of: يحدد خيار inverse_of: اسم عكس هذا الارتباط وهي has_many أو has_one، وهذه لا تعمل في التركيبة مع خيارات polymorphic: class Author < ApplicationRecord has_many :books, inverse_of: :author end class Book < ApplicationRecord belongs_to :author, inverse_of: :books end 4.1.2.8 polymorphic: يشير تمرير true إلى خيار polymorphic: إلى أن الارتباط متعدد الأشكال، وسنتحدث عن الارتباطات متعدد الأشكال لاحقا في هذا الدليل. 4.1.2.9 touch: إذا عيّنت خيار touch: إلى true، فإن timestamp لـ updated_at أو updated_on في كائن المرتبط سيُعيّن إلى الوقت الحالي كلما حُفظ الكائن أو دُمر: class Book < ApplicationRecord belongs_to :author, touch: true end class Author < ApplicationRecord has_many :books end في هذه الحالة، حفظ أو تدمير كتاب سيٌحدّث timestamp في المؤلف المرتبط به، يمكنك أيضًا تحديد سمة timestamp معينة للتحديث: class Book < ApplicationRecord belongs_to :author, touch: :books_updated_at end 4.1.2.10 validate: إذا عيّنت خيار validate: إلى true، فسيتحقق (Validate) الكائنات المرتبطة كلما حفظت هذا الكائن. وهذا الخيار يساوي false بشكل افتراضي، ولن يتحقق الكائنات المرتبطة عند حفظ الكائن. 4.1.2.11 optional: إذا عيّنت خيار optional: إلى true، فلن يتحقق من وجود الكائن المرتبط، وهذا الخيار false بشكل افتراضي. 4.1.3 نطاقات Scopes لـ belongs_to في بعض الأحيان قد تحتاج إلى تخصيص الاستعلام المُستخدم من قبل belongs_to، ويمكن تحقيق هذه التخصيصات عن طريق كتلة scope، فعلى سبيل المثال: class Book < ApplicationRecord belongs_to :author, -> { where active: true }, dependent: :destroy end يمكنك استخدام أي من أساليب الاستعلامات القياسية داخل كتلة scope، وستجد تفاصيل التالية في الأسفل: where includes readonly select 4.1.3.1 where يسمح لك أسلوب where بتحديد شروط كائن المرتبط. class book < ApplicationRecord belongs_to :author, -> { where active: true } end 4.1.3.2 includes يمكنك استخدام أسلوب includes لتحديد الارتباطات من الدرجة الثانية التي تريد تحميلها (eager-loaded) عند استخدام هذا الارتباط، فعلى سبيل المثال، فكر في هذه النماذج: class LineItem < ApplicationRecord belongs_to :book end class Book < ApplicationRecord belongs_to :author has_many :line_items end class Author < ApplicationRecord has_many :books end إذا كنت تسترد المؤلفين مباشرة من سطر العناصر بشكل كثير (line_item.book.author@)، فيمكنك جعل شيفرتك البرمجية أكثر كفاءة من خلال تضمين المؤلفين إلى الارتباط من سطر العناصر إلى الكتب: class LineItem < ApplicationRecord belongs_to :book, -> { includes :author } end class Book < ApplicationRecord belongs_to :author has_many :line_items end class Author < ApplicationRecord has_many :books end 4.1.3.3 readonly سيكون الكائن المرتبط قابل للقراءة فقط عند استرداده عن طريق الارتباط إذا استخدمت readonly. 4.1.3.4 select يسمح لك أسلوب select بتجاوز جملة SELECT (في SQL) والتي تُستخدم لاسترداد البيانات حول الكائن المرتبط، وبشكل افتراضي، سيسترد Rails جميع الأعمدة. 4.1.4 هل توجد أية كائنات مرتبطة؟ يمكنك معرفة هل توجد أية كائنات مرتبطة عن طريقة استخدام أسلوب ?association.nil: if @book.author.nil? @msg = "No author found for this book" end 4.1.5 متى تُحفظ الكائنات؟ إن تعيين كائن إلى ارتباط belongs_to لن يحفظ الكائن بشكل تلقائي، ولن يحفظ كائن المرتبط أيضًا. وسنتابع في الدرس القادم من هذه السلسلة الحديث عن مرجع ارتباط has_one المصدر: توثيقات Ruby on Rails
  4. سنتعرّف في سلسلة الدروس هذه والتي تبدأ بهذا الدرس على ارتباطات 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
  5. سنتابع في هذا الدرس دليل 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
  6. إن التهجير من أهم مزايا 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.
  7. في الدرس السابق ألقينا نظرة عامة على تهجير 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.
  8. في الدرس السابق ألقينا نظرة عامة حول التحقيقات في 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.
  9. في الدرسين السابقين ألقينا نظرة عامة حول التحقيقات في 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.
  10. في الدرسين السابقين ألقينا نظرة عامة حول التحقيقات في 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.
  11. ستتعرّف في هذا الدرس على دورة حياة كائنات Active Record، وستتعلم: دورة حياة كائنات Active Record. كيف تنشئ أساليب دوال الاستدعاء (callback methods) التي تستجيب إلى أحداث في دورة حياة الكائن. كيف تنشئ أصناف خاصة التي تجمع السلوك المشترك لدوال الاستدعاء (callbacks). 1- دورة حياة الكائن يمكن إنشاء، تحديث وتدمير الكائنات أثناء عمل تطبيق Rails، ويوفر Active Record طُرق تُمكنك من التحكم بالتطبيق وبياناته في دورة حياة الكائن . يسمح لك دوال الاستدعاء بإطلاق منطقي (trigger logic) قبل أو بعد تغيير حالة الكائن. 2- نظرة عامة على دوال الاستدعاء callbacks دوال الاستدعاء (callbacks) هي أساليب تُستدعى في لحظات معينة من دورة حياة الكائن وتمكنك من كتابة شيفرة برمجية تعمل عند إنشاء، حفظ، تحديث، حذف، تحقق من صحة أو تحميل كائن Active Record من قاعدة البيانات. 2-1 تسجيل دوال الاستدعاء تحتاج إلى تسجيل دوال الاستدعاء المتوفر حتى تتمكن من استخدامه، فيمكنك وضع دوال الاستدعاء كأساليب عادية، واستخدام أسلوب صنف نمط ماكرو (macro-style) لتسجيلهم كدوال استدعاء: class User < ApplicationRecord validates :login, :email, presence: true before_validation :ensure_login_has_a_value private def ensure_login_has_a_value if login.nil? self.login = email unless email.blank? end end end يمكن لأساليب صنف نمط ماكرو (macro-style) تلقي كتلة (block) لذلك استخدم هذا النمط إذا كانت الشيفرة البرمجية داخل الكتلة قصيرة بحيث يمكن وضعها في سطر واحد: class User < ApplicationRecord validates :login, :email, presence: true before_create do self.name = login.capitalize if name.blank? end end يمكنك تسجيل دوال الاستدعاء ليعمل فقط على بعض أحداث دورة حياة الكائن: class User < ApplicationRecord before_validation :normalize_name, on: :create # :on takes an array as well after_validation :set_location, on: [ :create, :update ] private def normalize_name self.name = name.downcase.titleize end def set_location self.location = LocationService.query(self) end end من الممارسات الجيدة أن تعلن أساليب دوال الاستدعاء كخاص، لأنه يمكن استدعاؤه من خارج النموذج (Model) إذا كان عامََا وبهذا تنتهك مبدأ تغليف الكائن (object encapsulation). 3- دوال الاستدعاء المتوفرة هذه قائمة بجميع دوال استدعاء Active Record المتوفر وهو مرتب بنفس ترتيب استدعاءه خلال العمليات: 3-1 إنشاء كائن before_validation after_validation before_save around_save before_create around_create after_create after_save after_commit/after_rollback 3-2 تحديث كائن before_validation after_validation before_save around_save before_update around_update after_update after_save after_commit/after_rollback 3-3 تدمير كائن before_destroy around_destroy after_destroy after_commit/after_rollback 3-4 after_initialize و after_find ستُستدعى دالة الاستدعاء after_initialize عند إنشاء كائن Active Record، إما مباشرة عن طريق new أو عند تحميل السجل record من قاعدة البيانات، ومن المستحسن تجنب الحاجة إلى تجاوز إنشاء أسلوب Active Record مباشرة. ستُستدعى دالة الاستدعاء after_find في كل مرة يُحمّل فيها Active Record سجل من قاعدة البيانات، وستُستدعى after_find قبل after_initialize إذا تم تعريفهما. لا يملك دوال الاستدعاء after_initialize وafter_find نظرائهم من نوع before_*، لكن يمكن تسجيلهم كباقي دوال الاستدعاء Active Record. class User < ApplicationRecord after_initialize do |user| puts "You have initialized an object!" end after_find do |user| puts "You have found an object!" end end >> User.new You have initialized an object! => #<User id: nil> >> User.first You have found an object! You have initialized an object! => #<User id: 1> 3-5 after_touch ستُستدعى دالة الاستدعاء after_touch كلما يُلمس (touch) كائن Active Record. class User < ApplicationRecord after_touch do |user| puts "You have touched an object" end end >> u = User.create(name: 'Kuldeep') => #<User id: 1, name: "Kuldeep", created_at: "2013-11-25 12:17:49", updated_at: "2013-11-25 12:17:49"> >> u.touch You have touched an object => true يمكن استخدام هذه الدالة جنبا إلى جنب مع belongs_to: class Employee < ApplicationRecord belongs_to :company, touch: true after_touch do puts 'An Employee was touched' end end class Company < ApplicationRecord has_many :employees after_touch :log_when_employees_or_company_touched private def log_when_employees_or_company_touched puts 'Employee/Company was touched' end end >> @employee = Employee.last => #<Employee id: 1, company_id: 1, created_at: "2013-11-25 17:04:22", updated_at: "2013-11-25 17:05:05"> # triggers @employee.company.touch >> @employee.touch Employee/Company was touched An Employee was touched => true 4- تشغيل دوال الاستدعاء هذه أساليب إطلاق (trigger) دوال الاستدعاء: create create! destroy destroy! destroy_all save save! save(validate: false) toggle! update_attribute update update! Valid? بالإضافة إلى ذلك، تُطلق (trigger) دالة الاستدعاء after_find عن طريق أساليب finder التالية: all first find find_by find_by_* find_by_*! find_by_sql last تعمل دالة الاستدعاء after_initialize في كل مرة ينشئ فيها كائن صنف class جديد. 5- تجاوز دوال الاستدعاء كما هو الحال مع عمليات التحقق (validation)، من الممكن أيضا تخطي دوال الاستدعاء عن طريق الأساليب التالية: decrement decrement_counter delete delete_all increment increment_counter toggle touch update_column update_columns update_all update_counters يجب استخدام هذه الأساليب بحذر، لأنه قد يُحتفظ بقواعد الأعمال (generated) الهامة ومنطق التطبيق في دوال الاستدعاء، وقد يؤدي تجاوزها دون فهم الآثار المحتملة إلى بيانات غير صالحة. 6- وقف التنفيذ عند بدء تسجيل دوال استدعاء جديد لنماذجك، سيكون في قائمة الانتظار للتنفيذ، وستشمل هذه القائمة جميع عمليات تحقق (validations) النموذج الخاص بك، ودوال الاستدعاء المسجلة وعمليات قاعدة البيانات التي ستُنفّذ. تُلف سلسلة (chain) دالة الاستدعاء بشكل كامل في العملية (transaction)، إذا أصدرت أي دالة استدعاء استثناء (exception)، فستتوقف السلسلة التي تعمل و يصدر ROLLBACK، وإذا أردت تعمد إيقاف السلسلة، استخدم: throw :abort 7- دوال استدعاء علائقي يعمل دوال الاستدعاء من خلال علاقات النموذج model، ويمكن تعريفه عن طريقه، لنفترض أن المستخدم لديه مقالات عديدة، ويجب حذف مقالات المستخدم في حالة حذف المستخدم، لنضف دالة استدعاء after_destroy إلى نموذج User لترتبط علائقيا بنموذج Model: class User < ApplicationRecord has_many :articles, dependent: :destroy end class Article < ApplicationRecord after_destroy :log_destroy_action def log_destroy_action puts 'Article destroyed' end end >> user = User.first => #<User id: 1> >> user.articles.create! => #<Article id: 1, user_id: 1> >> user.destroy Article destroyed => #<User id: 1> 8- دوال الاستدعاء المشروط كما هو الحال مع عمليات التحقق Validation، يمكن أيضا جعل أسلوب دالة الاستدعاء مشروط بشرط معين عن طريق استخدام خيارات :if و :unless والتي تأخذ رمز (symbol) أو Proc أو مصفوفة (Array). يمكنك استخدام خيار :if عندما تريد تحديد شروط استدعاء دالة الاستدعاء، وإذا أردت وضع شروط لعدم استدعاء دالة الاستدعاء يمكنك استخدام :unless. 8-1 استخدام :if و :unless مع Symbol يمكنك ربط خيارات :if و :unless مع رمز (Symbol) مطابق لاسم الأسلوب الذي سيُستدعى قبل دالة الاستدعاء. لن تُنفّذ دالة الاستدعاء عند استخدام خيار :if إذا ارجع الأسلوب false، وعند استخدام :unless لن تُنفّذ دالة الاستدعاء إذا أرجع الأسلوب true، وهذا الخيار هو الأكثر شيوعا. من الممكن أيضا باستخدام هذا الشكل من التسجيل تسجيل predicates مختلفة، التي يتم استدعاؤها للتحقق ما إذا كان يجب تنفيذ دالة الاستدعاء. class Order < ApplicationRecord before_save :normalize_card_number, if: :paid_with_card? End 8-2 استخدام :if و :unless مع Proc أخيرا، من الممكن ربط :if و :unless مع كائن Proc، وهذا الخيار هو الأنسب عند كتابة أساليب تحقق (validation) قصيرة، وفي العادة من سطر واحد: class Order < ApplicationRecord before_save :normalize_card_number, if: Proc.new { |order| order.paid_with_card? } end 8-3 شروط متعددة لدوال الاستدعاء عند كتابة دوال استدعاء شرطي، من الممكن المزج بين :if و :unless في نفس إعلان دالة الاستدعاء: class Comment < ApplicationRecord after_create :send_email_to_author, if: :author_wants_emails?, unless: Proc.new { |comment| comment.article.ignore_comments? } end 9- أصناف دالة الاستدعاء في بعض الأحيان، تكون أساليب دالة الاستدعاء التي ستكتبها مفيدة وملائمة لإعادة استخدامها من قبل نماذج أخرى، لذا يوفر Active Record إمكانية إنشاء أصناف تغلف أساليب دالة الاستدعاء، لذا يصبح من السهل إعادة استخدامها. هذا مثال أنشئنا فيه صنف مع دالة استدعاء after_destroy لنموذج PictureFile: class PictureFileCallbacks def after_destroy(picture_file) if File.exist?(picture_file.filepath) File.delete(picture_file.filepath) end end end عند الإعلان داخل صنف -كما في المثال أعلاه-، ستتلقى أساليب دالة الاستدعاء كائن النموذج كعامل، ونستطيع الآن استخدام صنف دالة الاستدعاء في النموذج: class PictureFile < ApplicationRecord after_destroy PictureFileCallbacks.new end لاحظ أننا بحاجة إلى إنشاء كائن PictureFileCallbacks جديد لأننا أعلنا دالة الاستدعاء الخاصة بنا على أنها مثيل أسلوب(instance method) ،وستستفيد من هذا خاصة إذا استخدمت دوال الاستدعاء حالة الكائن المثيل، ومع ذلك، في الكثير من الأحيان، سيكون من المنطقي إعلان دوال الاستدعاء كأساليب الصنف: class PictureFileCallbacks def self.after_destroy(picture_file) if File.exist?(picture_file.filepath) File.delete(picture_file.filepath) end end end لن يكون من الضروري إنشاء كائن PictureFileCallbacks إذا أُعلن عن أسلوب دالة الاستدعاء بهذه الطريقة. class PictureFile < ApplicationRecord after_destroy PictureFileCallbacks end يمكنك إعلان العدد الذي تريده من دوال الاستدعاء داخل أصناف دالة الاستدعاء. 10- عمليات دوال الاستدعاء يوجد دالتي استدعاء إضافيتين تعمل عند الانتهاء من عملية قاعدة البيانات: after_commit و after_rollback، وهما مشابهين لـ after_save إلا أنهم لا يعملون حتى يُنفّذ التغيير في قاعدة البيانات أو يتم التراجع عنه، وهما مفيدان للغاية عندما تحتاج نماذج active record إلى التفاعل مع الأنظمة الخارجية والتي هي ليست جزء من عملية قاعدة البيانات. على سبيل المثال، فكر في المثال السابق حيث احتاج نموذج PictureFile إلى حذف ملف بعد تدمير السجل، إذا أُصدر استثناء بعد استدعاء دالة الاستدعاء after_destroy و أُرجعت العملية، فسيحذف الملف وسيبقى النموذج في حالة غير متناسقة، على سبيل المثال، افترض أن picture_file_2 في الشيفرة البرمجية أدناه غير صحيح و أصدر أسلوب save! خطأََ: PictureFile.transaction do picture_file_1.destroy picture_file_2.save! End يمكننا تجنب هذه المشكلة باستخدام دالة الاستدعاء after_commit: class PictureFile < ApplicationRecord after_commit :delete_picture_file_from_disk, on: :destroy def delete_picture_file_from_disk if File.exist?(filepath) File.delete(filepath) end end end بما أنه من الشائع استخدام after_commit عند الإنشاء والتحديث والحذف، فتوجد أسماء مستعارة aliases لهذه العمليات: after_create_commit after_update_commit after_destroy_commit class PictureFile < ApplicationRecord after_destroy_commit :delete_picture_file_from_disk def delete_picture_file_from_disk if File.exist?(filepath) File.delete(filepath) end end end المصدر: توثيقات Ruby on Rails
  12. سنتابع في هذا الدرس من سلسلة تعليم إطار العمل 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
  13. 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.
×
×
  • أضف...