Rails/getting started

من موسوعة حسوب
< Rails
مراجعة 11:32، 8 يناير 2019 بواسطة جميل-بيلوني (نقاش | مساهمات) (أنشأ الصفحة ب'يغطي هذا الدليل بدء وتشغيل ريلز. بعد قراءة هذا الدليل، ستتعلم: * كيف تثبّت ريلز وتُنشئ تطبيق ر...')
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)
اذهب إلى التنقل اذهب إلى البحث

يغطي هذا الدليل بدء وتشغيل ريلز. بعد قراءة هذا الدليل، ستتعلم:

  • كيف تثبّت ريلز وتُنشئ تطبيق ريلز جديد وتربط تطبيق ريلز بقاعدة بيانات.
  • التخطيط العام لتطبيق ريلز.
  • المبادئ الأساسية للنمط MVC (النموذج [Model]، والواجهة [View]، ووحدة التحكم [Controller]) والتصميم RESTful.
  • كيف تولد سريعًا أجزاء البداية لتطبيق ريلز.

افتراضات الدليل

صُمِّم هذا الدليل للمبتدئين الراغبين ببدء استعمال إطار العمل ريلز من الصفر. ولا يفترض أن لك سابق خبرة بريلز.

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

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

ما هو إطار العمل ريلز؟

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

ريلز هو برنامج "عنيد". إنه يفترض أن هناك طريقة "أفضل" للقيام بالأشياء، وهو مصمم لتشجيع هذه الطريقة - وفي بعض الحالات تثبيط البدائل. إذا تعلمت "طريقة ريلز"، فربما ستكتشف زيادة هائلة في الإنتاجية. إذا كنت لا تزال تتبع العادات القديمة من اللغات الأخرى أثناء التطوير باستخدام ريلز ومحاولة استخدام الأنماط التي تعلمتها في مكان آخر، فربما ستكون تجربتك أقل سعادة.

تتضمن فلسفة ريلز مبدأين توجيهيين أساسيين:

  • لا تكرر نفسك: هو مبدأ تطوير للبرمجيات ينص على أن "كل جزء من المعرفة يجب أن يكون له تمثيل واحد، لا لبس فيه وموثَّق في النظام." من خلال عدم كتابة نفس المعلومات مرارًا وتكرارًا، لتكون شيفراتنا أكثر قابلية للإصلاح  وللاستزادة، وأقل أخطاءً.
  • العرف قبل الضبط: لدى ريلز آراء حول أفضل طريقة للقيام بأشياء كثيرة في تطبيق ويب تجعل مجموعة الأعراف (conventions) تلك في أوضاع افتراضية، بدلًا من أن تتطلب منك تحديد التفاصيل من خلال ملفات إعدادات لا نهاية لها.

إنشاء مشروع جديد بإطار العمل ريلز

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

من خلال متابعة هذا الدليل، ستنشئ مشروع ريلز يسمى blog، وهي مدونة ويب (بسيطة جدًا). قبل أن تتمكن من البدء في إنشاء التطبيق، تحتاج إلى التأكد من تثبيت ريلز نفسه.

تنبيه: تستخدم الأمثلة أدناه الرمز $ لتمثيل محث الطرفية في أنظمة التشغيل الشبيهة بيونكس، على الرغم من أنه قد خُصّص ليظهر بشكل مختلف. إذا كنت تستخدم نظام التشغيل ويندوز، فسيظهر المحثّ الخاص بك بشكل يشبه c:\source_code>‎.

تثبيت ريلز

قبل تثبيت ريلز، يجب عليك التحقق من أن النظام لديك به المتطلبات الأساسية والمناسبة مثبتة. وذلك يشمل روبي و SQLite3.

افتح محثّ سطر الأوامر. في نظام التشغيل ماك، ثم افتح Terminal.app؛ في نظام التشغيل ويندووز، اختر "بحث" من قائمة البدء واكتب "cmd.exe". أي أمر مسبوق بعلامة $ سيشغل في سطر الأوامر.

تحقق من تثبيت الإصدار الحالي من روبي:

$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]

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

تنبيه: هناك عدد من الأدوات التي تُمكنك من تثبيت روبي وريلز في نظامك. يمكن لمستخدمي ويندوز استعمال مُثبِّت ريلز، بينما يمكن لمستخدمي نظام التشغيل ماك استخدام Tokaido. لمزيد من الطرائق حول تثبيت روبي على أنظمة التشغيل الأخرى، يمكنك إلقاء نظرة على هذه الصفحة.

إن كنت تستعمل نظام التشغيل ويندوز، فعليك تثبيت Ruby Installer Development Kit.

ستحتاج أيضًا إلى تثبيت قاعدة البيانات SQLite3. تتضمّن العديد من أنظمة التشغيل الشبيهة بيونكس قاعدة البيانات SQLite3 بإصدار مقبول. في نظام ويندوز، إذا ثبت ريلز عبر مُثبِّت ريلز، فستُثبّت قاعدة البيانات SQLite ضمنيًّا. يمكن للآخرين العثور على إرشادات التثبيت على موقع SQLite3. تحقق من أنها مثبتة بشكل صحيح وفي PATH الخاص بك:

$ sqlite3 --version

يجب أن يظهر الإصدار الحالي المثبَّت لديك. لتثبيت ريلز، استخدم الأمر gem install المقدم بواسطة RubyGems:

$ gem install rails

للتحقق من أن كل شيء لديك مثبت بطريقة صحيحة، عليك باستخدام الأمر التالي:

$ rails --version

إذا كان الإصدار الظاهر شيئًا شبيهًا بالعبارة "Rails 5.2.1"، فأنت الآن مُستعدٌ لإكمال الرحلة.

إنشاء تطبيق المدونة

يأتي ريلز مع عدد من الأوامر النصية التي تسمى مولدات صُمّمت لجعل أمر التطوير أسهل لك من خلال إنشاء كل ما هو ضروري لبدء العمل في مهمة معينة. أحد هذه المولدات هو مولد تطبيق جديد (new application generator)، والذي سيوفر لك أساس تطبيق ريلز جديد حتى لا تضطر إلى كتابته بنفسك.

لاستخدام هذا المولد، افتح الطرفية، وانتقل إلى مجلد تمتلك صلاحيات لإنشاء ملفات ضمنه، واكتب:

$ rails new blog

هذا الأمر سينشئ تطبيق ريلز جديد باسم Blog في المجلد blog ويثبت اعتماديات gem التي ذُكرت بالفعل في Gemfile باستخدام bundle install.

ملاحظة: إذا كنت تستخدم نظام ويندووز فرعي على نظام لينكس، هذا يعني أن هناك بعض القيود بخصوص تنبيهات نظام الملفات وبالتالي يجب تعطيل الجوهرة spring و listen؛ يمكنك فعل ذلك بتنفيذ الأمر rails new blog - skip-spring --skip-listen بدلًا من الأمر السابق.

تنبيه: يمكنك رؤية جميع خيارات سطر الأوامر التي يمكن استعمالها مع منشئ تطبيق ريلز وذلك عبر الأمر rails new -h.

بعد إنشاء التطبيق blog، انتقل إلى مجلده:

$ cd blog

يحتوي المجلد blog على عدد من الملفات والمجلدات التي أُنشأت تلقائيًا والتي تشكل بنية تطبيق ريلز.  معظم علمنا سيتركز في المجلد app، ولكن إليك خلاصة أساسية لوظيفة كل من الملفات والمجلدات التي ينشئها ريلز بشكل افتراضي:

الملف/المجلد الغاية منه
app/‎ يحتوي على وحدات التحكم والنماذج والواجهات والمساعدين ومرسلي رسائل البريد الإلكتروني والقنوات والوظائف والأصول الخاصة بتطبيقك. ستركز على هذا المجلد فيما تبقى من هذا الدليل.
bin/‎ يحتوي على سكريبتات ريلز التي تبدأ تطبيقك، كما يحتوي على نصوص تستخدمها لإعداد وتحديث واستخدام وتشغيل تطبيقك.
config/‎ ضبط مسارات التوجيه وقواعد البيانات الخاصة بتطبيقك وأكثر من ذلك. هناك تفاصيل أكثر تجدها في دليل ضبط تطبيقات ريلز.
config.ru ضبط Rack للخوادم المعتمدة على Rack المُستخدمة لبدء تطبيقك. لمعلومات أكثر عن Rack، الق نظرة على موقع Rack.
db/‎ يحتوي على المُخطط الحالي لقاعدة البيانات، بالإضافة إلى تهجيرات البيانات.
Gemfile

Gemfile.lock

تسمح لك تلك الملفات أن تعين أي اعتمادات الجوهرة (gem) يحتاجها لتطبيقك. تُستخدم تلك الملفات بواسطة Bundler gem. لمعلومات أكثر عن Bundler، الق نظرة على موقع Bundler.
lib/‎ الوحدات الممتدة لتطبيقك.
log/‎ ملفات سجل التطبيق.
package.json يسمح لك هذا الملف أن تعين أية اعتمادات npm يحتاجها لتطبيقك. يُستخدم هذا الملف بواسطة Yarn. لمعلومات أكثر عن Yarn، الق نظرة على موقع Yarn.
public/‎ المجلد الوحيد الذي يُرَى كما هو. يحتوي على ملفات ثابتة وأصول مُجمعة.
Rakefile هذا الملف يُحدِّد موضع المهام ويُحملها والتي من الممكن تشغيلها من سطر الأوامر. تعريفات المهام (task definitions) تُعرَّف من خلال مكونات ريلز. بدلًا من تغيير Rakefile، عليك بإضافة مهامك عن طريق إضافة ملفات إلى المجلد lib/tasks الخاص بتطبيقك.
README.md هذا دليل توجيهي مُختصر لتطبيقك. عليك بتعديل الملف لتخُبر الآخرين بما يقوم به تطبيقك وكيفية ضبطه وهكذا.
test/‎ اختبارات الوحدات والتجهيزات وأدوات الاختبار الأخرى. هذا مُغطى في دليل اختبار تطبيقات ريلز.
tmp/‎ ملفات مؤقتة (مثل ملفات الذاكرة المؤقتة ومعرف العملية [pid] ...إلخ).
vendor/‎ مكان لجميع أكواد الطرف الثالث. في تطبيق ريلز مثالي يشمل هذا الجواهر (gems) التي نُقلت لملف vendor.
gitignore يخبر هذا الملف git بأي الملفات (أو الأنماط) التي يجب أن يُهملها. لمزيد من المعلومات عن إهمال الملفات، الق نظرة على هذه الصفحة.
ruby-version يشمل هذا الملف إصدار روبي الافتراضي.

مرحبًا، ريلز!

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

بدء خادم الويب

لديك بالفعل تطبيق ريلز فعال. لرؤيته، تحتاج إلى بدء خادم ويب على جهاز التطوير الخاص بك. يمكنك ذلك عن طريق تنفيذ الأمر التالي في المجلد blog:

$ bin/rails server

تنبيه: إن كنت تستخدم نظام ويندوز، فيجب عليك تمرير السكربتات ضمن المجلد bin مباشرةً إلى مُفسِر روبي مثل ruby bin\rails server.

تنبيه: يتطلب تفسير (Compiling) ضغط أصول CoffeScript و JavaScript أن تكون لديك بيئة تشغيل JavaScript متاحة على نظامك؛ إذا غابت بيئة التشغيل، سترى الخطأ execjs أثناء تجميع الأصول. عادةً ما يحتوي نظام التشغيل ماك ونظام ويندوز على بيئة تشغيل JaveScript مثبتة. يضيف ريلز الجوهرة mini_racer إلى Gemfile الذي أُنشئ في سطر تعليق من أجل التطبيقات الجديدة ويمكنك إلغاء التعليق إذا كنت بحاجة إلى ذلك. إن therubyrhino هي بيئة تشغيل الموصى بها لمستخدمي JRuby وتضاف بشكل افتراضي إلى Gemfile في التطبيقات التي أُنشئت تحت JRuby. يمكنك التحقق من جميع بيئات التشغيل المدعومة في ExecJS.

سيؤدي ذلك إلى إطلاق Puma، وهو خادم ويب يُوزّع باستخدام ريلز بشكل افتراضي. للاطلاع على تطبيقك قيد التنفيذ، افتح نافذة المتصفح وانتقل إلى العنوان http://localhost:3000. يجب أن تشاهد صفحة معلومات ريلز الافتراضية:

[[ملف:rails_welcome.png|بديل=صفحة ريلز الترحيبية|بدون|تصغير|500بك|صفحة ريلز الترحيبية]]

تنبيه: لإيقاف خادم الويب، اضغط على Ctrl + C في نافذة الطرفية حيث يُشغّل الخادم. للتحقق من توقف الخادم، يجب أن ترى مؤشر محثّ الأوامر مرة أخرى. بالنسبة لمعظم الأنظمة الشبيهة بيونكس، بما في ذلك نظام التشغيل ماك، سيكون ظهور الرمز $ كافيًا للدالالة على توقفه. في وضع التطوير (development mode)، لا يتطلب ريلز عادةً إعادة تشغيل الخادم؛ سيسري مفعول التغييرات التي تجريها على الملفات تلقائيًا في الخادم.

إن الصفحة الترحيبية بمثابة كاشف الدخان لتطبيق ريلز جديد، فهي تؤكد أن لديك برنامج مُهيأ جيدًا لخدمة صفحة.

كيف تجعل ريلز يقول "مرحبًا"

لكي تجعل إطار العمل ريلز يقول "مرحبًا"، فعليك بإنشاء وحدة تحكّم (controller) وواجهة (view) على الأقل.

الهدف من وحدة التحكّم استقبال الطلبات الخاصة بالتطبيق. قسم التوجيه (Routing) سيحدد أي الطلبات ستتجه لأي وحدة تحكّم، فغالبًا لكل وحدة تحكّم أكثر من مسار، والمسارات المختلفة يُعمل عليها بإجراءات مختلفة. الهدف من كل إجراء جمع المعلومات لتزويد الواجهة بها.

الهدف من الواجهة عرض المعلومات في صيغة يمكن للإنسان قراءتها. من الفروق المهمة جعل وحدة التحكّم، وليس الواجهة، المسؤولة عن جمع المعلومات. فالواجهة مخصصة فقط لعرض المعلومات. قوالب الواجهة تُكتب بلغة تُسمى eRuby (اختصارًا لكلمة Embedded Ruby) والتي تُعالج بواسطة الدورة الخاصة بالطلبات في ريلز، قبل أن تُرسل للمُستخدم.

لإنشاء وحدة تحكّم جديدة، يجب عليك تشغيل مولد "وحدة التحكّم" (Controller generator) وتخبره بأنك تُريد وحدة تحكّم تُسمى "Welcome" مع إجراء (action) يسمى "index". فقط عليك باتباع التالي:

$ bin/rails generate controller Welcome index

سينشئ ريلز لك العديد من الملفات بالإضافة إلى مسار توجيه.

create  app/controllers/welcome_controller.rb
 route  get 'welcome/index'
invoke  erb
create    app/views/welcome
create    app/views/welcome/index.html.erb
invoke  test_unit
create    test/controllers/welcome_controller_test.rb
invoke  helper
create    app/helpers/welcome_helper.rb
invoke    test_unit
invoke  assets
invoke    coffee
create      app/assets/javascripts/welcome.coffee
invoke    scss
create      app/assets/stylesheets/welcome.scss

أهم تلك الملفات وحدة التحكم بالطبع، الموضوعة في app/controllers/welcome_controller.rb والواجهة الموضوعة في app/views/welcome/index.html.erb. افتح الملف app/views/welcome/index.html.erb في محرر النص الخاص بك. وأزل جميع الأكواد الموجودة في الملف، واستبدلها بذلك السطر المفرد من الأكواد:

<h1>مرحبًا، ريلز!</h1>

إعداد الصفحة الرئيسية للتطبيق

بعد أن أنشأنا وحدة التحكم والواجهة، علينا أن نخبر ريلز متى نُريد أن تُظهِر العنوان "مرحبًأ، ريلز!‎". في حالتنا نُريد أن يظهر العنوان السابق عند الانتقال إلى أصل عنوان موقعنا، الذي هو http://localhost:3000 بدلًا من ظهور الصفحة الترحيبية الافتراضية لريلز.

الخطوة التالية هي أنَّه يجب عليك أن تُخبر ريلز أين الموقع الحقيقي لصفحتك الرئيسية. افتح ملف config/routes.rb في مُحررك:

Rails.application.routes.draw do
  get 'welcome/index'
 
  # المتاح ضمن هذا الملف DSL لمزيد من المعلومات حول
  # https://wiki.hsoub.com/Rails/routing اطلع على الصفحة
end

هذا هو مسار تطبيقك الذي يحمل المدخلات بلغة محدَّدة النطاق DSL (اختصار للعبارة domain-specific language) والتي تخبر ريلز كيف تصل الطلبات القادمة بوحدات التحكم والأوامر. حرّر هذا الملف وأضف السطر 'root 'welcome#index. الأمر سيبدو كالتالي:

Rails.application.routes.draw do
  get 'welcome/index'
 
  root 'welcome#index'
end

يخبر السطر 'root 'welcome#index ريلز بتعيين طلبات جذر التطبيق إلى وحدة التحكم Welcome للإجراء index وتخبر الشيفرة 'get 'welcome/index ريلز بتعيين الطلبات http://localhost:3000/welcome/index إلى وحدة التحكم Welcome للإجراء index. أُنشيء هذا مُسبقًا عندما شغلت مولد وحدة التحكم (لتنفيذ الأمر bin/rails generate controller Welcome index).

شغل خادم الويب مرة أخرى إذا أوقفته لتوليد وحدة التحكم (عبر الأمر bin/rails

server) وانتقل إلى العنوان http://localhost:3000  في مُتصفحك. سترى الرسالة "مرحبًا، ريلز!" التي كتبتها في الملف app/views/welcome/index.html.erb، ما يُشير إلى أنَّ المسار الجديد سيذهب حقًا لوحدة التحكم welcome التي تخص الإجراء index وسيصيِّر الواجهة بشكل صحيح.

تنبيه: لمزيد من المعلومات عن تحديد المسار، اطلع على دليل التوجيه من الخارج والداخل.

المواصلة والتشغيل

الآن وبعد أن رأيت كيف تنشئ وحدة تحكم وإجراء وواجهة، دعنا ننشئ شيئًا أكثر تعقيدًا.

في التطبيق Blog، سننشئ الأن موردًا جديدًا. المورد هو مصطلح يُستخدم لجمع الأشياء المتشابهة مثل المقالات والأشخاص والحيوانات. يُمكنك إنشاء وقراءة وتحديث وتدمير عناصر المورد وتلك العمليات تُسمى عمليات CRUD (اختصار للكلمات create، و read، و update، و destroy).

يقدم ريلز التابع resources الذي يمكن استخدامه للتصريح عن مورد REST قياسي. ستحتاج إلى إضافة مورد المقال (article resource) إلى config/routes.rb وبذلك سيبدو الملف بالشكل التالي:

Rails.application.routes.draw do
  get 'welcome/index'
 
  resources :articles
 
  root 'welcome#index'
end

إذا شغلت الأمر bin/rails routes، سترى أنه عرف مسارات توجيه لجميع إجراءات RESTful القياسيّة. سنتطرق لاحقا إلى معنى العمود البادئ (والأعمدة الأخرى) التي ستظهر لك، ولكن الآن لاحظ أن ريلز تستدل على النموذج الفردي article وتستخدم هذا التميز (distinction) بطريقة ذات معنى.

 $ bin/rails routes
       Prefix Verb   URI Pattern                  Controller#Action
welcome_index GET    /welcome/index(.:format)     welcome#index
     articles GET    /articles(.:format)          articles#index
              POST   /articles(.:format)          articles#create
  new_article GET    /articles/new(.:format)      articles#new
 edit_article GET    /articles/:id/edit(.:format) articles#edit
      article GET    /articles/:id(.:format)      articles#show
              PATCH  /articles/:id(.:format)      articles#update
              PUT    /articles/:id(.:format)      articles#update
              DELETE /articles/:id(.:format)      articles#destroy
         root GET    /                            welcome#index

في القسم التالي، ستُضيف إمكانية إنشاء مقالات جديدة في تطبيقك وستتمكن من عرضهم. هذا ما يشير له الحرف "C" (إنشاء) و"R" (قراءة) من CRUD. سيبدو شكل النموذج الذي جرى تنفيذه وفقًا لما سبق بالشكل التالي:

[[ملف:new_article.png|بديل=شكل نموذج إضافة المقالات الجديدة الذي أُنشِئ والذي يراد عرضه في تطبيق المدونة.|بدون|تصغير|500بك|شكل نموذج إضافة المقالات الجديدة الذي أُنشِئ والذي يراد عرضه في تطبيق المدونة.]]

إرساء الأساسيات

بدايًة، تحتاج إلى مكان داخل التطبيق لتنشئ مقالًا جديدًا. سيكون العنوان articles/new/ مكانًا جيدًا لذلك. مع مسار التوجيه المُعرَّف مسبقًا، يُمكن إنشاء الطلبات للعنوان articles/new/ داخل التطبيق. انتقل إلى العنوان http://localhost:3000/articles/new وسترى خطأ بالمسار:

[[ملف:routing_error_no_controller.png|بديل=خطأ التوجيه الذي سيظهر عند الانتقال إلى العنوان ‎/articles/new في تطبيق المدونة دون وجود وحدة تحكم تخدمه.|بدون|تصغير|500بك|خطأ التوجيه الذي سيظهر عند طلب العنوان ‎/articles/new في تطبيق المدونة دون وجود وحدة تحكم تخدمه.]]

يحدث ذلك الخطأ لأن المسار بحاجة إلى وحدة تحكم مُعرفة بغرض خدمة الطلب. حل تلك المشكلة المحددة بسيط: أنشئ وحدة تحكم تُسمى ArticlesController. يمكنك القيام بذلك بتنفيذ الأمر التالي في سطر الأوامر:

$ bin/rails generate controller Articles

إذا فتحت ملف وحدة التحكم app/controllers/articles_controller.rb المولد حديثًا، سترى وحدة تحكم فارغةً تمامًا:

class ArticlesController < ApplicationController
end

وحدة التحكم هي ببساطة صنف يُعرَّف ليرث من ApplicationController. بداخل ذلك الصنف ستُعرف التوابع التي ستُصبح الإجراءات لوحدة التحكم. تلك الإجراءات ستقوم بعمليات CRUD على المقالات بداخل نظامك.

ملاحظة: هناك توابع عامة وخاصة ومحمية في روبي، ولكن التوابع العامة فقط تستطيع أن تكون إجراءات لوحدات التحكم. لمزيد من المعلومات راجع برمجة روبي.

إذا حدثت http://localhost:3000/articles/new الآن، ستجد خطأ جديدًا:

[[ملف:unknown_action_new_for_articles.png|بديل=ظهور خطأ جديد بعد توليد وحدة التحكم ArticlesController وتحديث الصفحة ‎/articles/new.|بدون|تصغير|500بك|ظهور خطأ جديد بعد توليد وحدة التحكم ArticlesController وتحديث الصفحة ‎/articles/new.]]

يُشير هذا الخطأ إلى أنَّ ريلز لا يستطيع إيجاد الإجراء الجديد بداخل وحدة التحكم ArticlesController التي أنشأتها للتو. هذا بسبب أن وحدات التحكم المولدة في ريلز تكون فارغةً في الحالة الافتراضية، إلا إذا بلغت عن الإجراءات المراد إنشاؤها في عملية التوليد.

لتعريف الإجراءات يدويًا، كل ما عليك هو تعريف تابع جديد داخل وحدة التحكم. افتح الملف app/controllers/articles_controller.rb بداخل الصنف ArticlesController، ثمَّ عرف التابع الجديد. ستبدو وحدة التحكم الخاصة بك بالشكل التالي:

class ArticlesController < ApplicationController
  def new
  end
end

بتعريف التابع الجديد في ArticlesController، إذا حدثت الصفحة http://localhost:3000/articles/new ستجد خطأ أخر:

[[ملف:template_is_missing_articles_new.jpeg|بديل=الخطأ الجديد الذي سيظهر بعد تعريف التابع new الجديد في الصنف ArticlesController وتحديث الصفحة ‎/articles/new.|بدون|تصغير|500بك|الخطأ الجديد الذي سيظهر بعد تعريف التابع new الجديد في الصنف ArticlesController وتحديث الصفحة ‎/articles/new.]]

لقد تلقيت هذا الخطأ الآن نظرًا لأن ريلز يتوقع أن تكون الإجراءات لديها واجهات مرتبطة بها لعرض معلوماتها. مع عدم وجود طريقة عرض، سيعيد ريلز استثناء.

لنلقي نظرة على رسالة الخطأ الكاملة مرة أخرى:

ArticlesController#new is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not… nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot.

هذا نص طويل جدًّا! دعنا نذهب بسرعة ونفهم ما يعنيه كل جزء منه.

يحدد الجزء الأول القالب المفقود. في هذه الحالة، قالب articles/new. سيبحث ريلز  أولاً عن هذا النموذج. إذا لم يعثر عليه، فسيحاول تحميل قالب يسمى application / new. يبحث ريلز عن قالب هنا لأن ArticlesController  محفوظ في ApplicationController.

يحتوي الجزء التالي من الرسالة على request.formats الذي يحدد تنسيق القالب الذي سيُعرض كاستجابة. تم تعيينه على text / html لكوننا طلبنا هذه الصفحة عبر المتصفح، لذلك يبحث ريلز عن قالب HTML. يحدد request.variant أي نوع من الأجهزة المادية سيخدم من خلال الاستجابة ويساعد ريلز لتحديد القالب الذي سيستخدم في الاستجابة. request.variant فارغ لكونك لم تقدم أي معلومات.

سيوجد أبسط قالب للعمل في هذه الحالة في app/views/articles/new.html.erb. امتداد اسم الملف هذا مهم: الامتداد الأول هو تنسيق القالب، والامتداد الثاني هو المعالج الذي سيستخدم لإصدار القالب. يحاول ريلز البحث عن قالب يسمى articles/new  داخل app/views للتطبيق. تنسيق هذا القالب قد يكون html فقط والمعالج الافتراضي لـ HTML هو erb. يستخدم ريلز معالجات أخرى للتنسيقات الأخرى. يستخدم معالج builder لبناء نماذج XML ويستخدم معالج coffee CoffeeScript لبناء قوالب JavaScript. بما أنك تريد إنشاء نموذج HTML جديد، فستستخدم لغة ERB المصممة لتضمين Ruby في HTML.

لذلك يجب أن يسمى الملف  articles/new.html.erb ويجب أن يكون موجودًا داخل الدليل app/views للتطبيق.

امض قدمًا الآن وأنشئ ملفًّا جديدًا في  app/views/articles/new.html.erb واكتب هذا المحتوى فيه:

<h1>New Article</h1>

عند تحديث  http://localhost:3000/articles/new سترى أن الصفحة لها عنوان. المسار ووحدة التحكم والإجراء والواجهة يعملون الآن بانسجام! حان الوقت لإنشاء استمارة لمقال جديد.

الاستمارة الأولى

لإنشاء استمارة داخل ذلك القالب، ستستخدم منشئ استمارات form builder. منشئ الاستمارات الأساسيّ لـ ريلز يُقدم من خلال التابع المساعد form_with. لاستخدام ذلك التابع، أضف ذلك الكود إلى app/views/articles/new.html.erb:

<%= form_with scope: :article, local: true do |form| %>

 <p>

   <%= form.label :title %><br>

   <%= form.text_field :title %>

 </p>

 <p>

   <%= form.label :text %><br>

   <%= form.text_area :text %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

إذا حدثت الصفحة الآن، سترى نفس الاستمارة من المثال أعلاه. بناء الاستمارات في ريلز هو حقًا بهذه السهولة!

عند استدعاء التابع المساعد form_with،  تُمرر له نطاق تعريف الاستمارة. في هذه الحالة، الرمز article: يخبر المساعد form_with هدف الاستمارة. داخلبنية التابع، يُستخدم الكائن FormBuilder والذي يمثل بـ - form لبناء تسميتين (label) وحقلي نص (text field)، واحد للعنوان وآخر لنص المقال. وأخيرًا، طلب الإرسال لكائن الاستمارة سيُنشيء زر الإرسال للإستمارة..

هناك مشكلة واحدة مع هذه الاستمارة رغم ذلك. إذا فحصت  HTML المُولد، عن طريق عرض مصدر الصفحة، سترى أن الأمر الواصف  للاستمارة يُشير إلى articles/new/. هذه مشكلة لأن هذا المسار ينتقل إلى الصفحة التي أنت عليها في الوقت الحالي، ويجب استخدام هذا المسار فقط لعرض الاستمارة لمقال جديد.

تحتاج الاستمارة إلى استخدام عنوان URL مختلف للانتقال إلى مكان آخر. يمكن القيام بذلك ببساطة مع خيار url: الخاص بالتابع المساعد form_with. عادةً في ريلز، يسمى الإجراء المستخدم في عمليات إنشاء استمارة جديدة مثل هذا "create"، وبالتالي يجب أن تُوجه الاستمارة إلى هذا إجراء.

عدّل السطر form_with داخل app/views/articles/new.html.erb ليبدو هكذا:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

في هذا المثال، المُساعد articles_path يُمرر لخيار url:. لنرى ما سيفعله ريلز عند ذلك، علينا بالرجوع إلى المخرج bin/rails routes:

$ bin/rails routes

     Prefix Verb   URI Pattern            Controller#Action

welcome_index GET /welcome/index(.:format) welcome#index

    articles GET /articles(.:format)       articles#index

             POST /articles(.:format)       articles#create

 new_article GET /articles/new(.:format)   articles#new

edit_article GET /articles/:id/edit(.:format) articles#edit

     article GET /articles/:id(.:format)   articles#show

             PATCH /articles/:id(.:format)   articles#update

             PUT /articles/:id(.:format)   articles#update

             DELETE /articles/:id(.:format)   articles#destroy

        root GET /                         welcome#index

سيُخبر المساعد articles_path الإطار ريلز أن يوجه الاستمارة إلى نمط العنوان URL Pattern المُرتبط ببادئة المقال; وسيٌرسل المقال افتراضيًّا طلب post إلى المسار. هذا مُرتبط بإجراء الإنشاء الخاص بوحدة التحكم الحالية، ArticlesController.

بتعريف الاستمارة والمسار المُرتبط بها، ستقدر على ملء الاستمارة وتضغط زر الإرسال لبدء عملية إنشاء مقال جديد، لذا قم بعمل ذلك. وعند تقديم الاستمارة، سيظهر لك خطأ مشابه:

عليك الآن بإنشاء إجراء الإنشاء create action بوحدة التحكم ArticlesController.

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

إنشاء المقالات

للتخلص من رسالة الخطأ الخاصة بإجراء غير معروف "Unknown action"، يُمكنك تعريف إجراء إنشاء داخل الصنف ArticlesController في app/controllers/articles_controller.rb، أسفل الإجراء الجديد new كالتالي:

class ArticlesController < ApplicationController

 def new

 end

 def create

 end

end

إذا أعدت إرسال الاستمارة الآن، فقد لا ترى أي تغيير بالصفحة. لا تقلق! يرجع ذلك إلى أن ريلز بشكل افتراضي تستجيب لإجراء ما بـ 204 No Content إذا لم نحدد الاستجابة الواجبة. لقد أضفنا للتو إجراء إنشاء create، لكن لم نحدد أي شيء عن الاستجابة. في هذه الحالة، إجراء إنشاء create سيحفظ المقال الجديد إلى قاعدة البيانات.

عند إرسال الاستمارة، تُرسل مجالات الاستمارة لـ ريلز كمعاملات. يُشار إلى هذه المعاملات داخل إجراءات وحدة التحكم، لتأدية مهمة محددة. لترى كيف تبدو هذه المعاملات، غير الإجراء إنشاء create كالتالي:

def create

 render plain: params[:article].inspect

end

يأخذ التابع render هاش بسيط جدًا بمفتاح plain: وقيمة params [ :article].inspect. التابع params هو الكائن الممثل للمعاملات (أو المجالات) الأتية من الاستمارة. يُرجِع التابع params الكائن ActionController: :Parameters، والذي يسمح لك بالوصول إلى مفاتيح الهاش باستخدام إما السلاسل النصية أو الرموز. في هذه الحالة، المعاملات ذو الأهمية فقط هي المعاملات من الاستمارة.

تنبيه: تأكد من فهمك القاطع للتابع params، لأنك ستستخدمه عادًة. دعنا نلقي نظرة على عنوان URL لمثال:

http://www.example.com/?username=dhh&email=dhh@email.co

في هذا العنوان، فإن [params [ :username تساوي "dhh" و params[ :email] تساوي dhh@emial.com

إذا أعدت إرسال الاستمارة مرة أخرى، سترى شيء كالتالي:

<ActionController::Parameters {"title"=>"First Article!", "text"=>"This is my first article."} permitted: false>

هذا الإجراء يُظهر المعاملات للمقال القادم من الاستمارة، ولكن هذا ليس مساعد حقًا. صحيح، يُمكنك رؤية المعاملات ولكن لا يمكن القيام بشيء محدد.

إنشاء نموذج المقال Article

تستخدم النماذج في ريلز اسم مفرد بينما تستخدم جداول قاعدة البيانات المقابلة لها اسم جمع. يُقدم ريلز مولد لإنشاء النماذج، يميل معظم المطورون باستخدام ريلز إلى استخدامه عند إنشاء نماذج جديدة. لإنشاء نموذج جديد، شغل هذا الأمر في الطرفية:

$ bin/rails generate model Article title:string text:text

بهذا الأمر نخبر ريلز أننا نريد نموذج المقال Article, مع لاحقة العنوان (title) من النوع السلسلة النصية (string) ولاحقة النص (text) من النوع النصي (text). تُضاف تلك اللواحق تلقائيًا إلى الجدول articles في قاعدة البيانات وتُعين في نموذج المقال Article.

يستجيب ريلز بإنشاء مجموعة من الملفات. إلى الآن، نهتم فقط بـ app/models/article.rb وdb/migrate/20140120191729_create_articles.rb (ربما يكون الاسم لديك مختلف قليلًا). الأخير مسئول عن إنشاء بنية قواعد البيانات، والتي نتطلع إليها جميعًا فيما بعد.

تنبيه: التسجيل الحي (Active Record) ذكي كفاية لتعيين أسماء العمود تلقائيًا في لواحق النموذج، ما يعني أنه ليس عليك مسح اللواحق بداخل نماذج ريلز، لأن هذا سيتم تلقائيًا بواسطة التسجيل الحي (Active Record).

القيام بتهجير

كما رأينا، فإن الترميز bin/rails generate model أنشئ ملف قاعدة بيانات تهجير داخل الدليل db/migrate. التهجيرات هي من أصناف Ruby المصممة لتسهيل إنشاء وتعديل جداول قاعدة البيانات. يستخدم ريلز أوامر rake للقيام بتهجيرات، ومن الممكن إلغاء تهجير بعد تطبيقه على قاعدة البيانات. أسماء ملفات التهجير تشمل  بصمة الوقت timestamp لضمان تنفيذها بالترتيب التي أنشأت به.

إذا نظرت في ملف db/migrate/YYYYMMDDHHMMSS_create_articles.rb (تذكر أن الملف الخاص بك سيكون اسمه مختلف قليلًا)، هذا ما ستجده:

class CreateArticles < ActiveRecord::Migration[5.0]

 def change

   create_table :articles do |t|

     t.string :title

     t.text :text

     t.timestamps

   end

 end

end

سينشئ التهجير بالأعلى تابع يُسمى تغيير change والذي سيُدعى عند القيام بهذا التهجير. الإجراء المُعرف في هذا التابع يمكن الرجوع عنه أيضًا، ما يعني أن ريلز تعرف كيف ترجع عن أي تغيير تم بهذا التهجير، في حالة تُريد الرجوع عنه فيما بعد. عند القيام بهذا التهجير سينشئ جدول articles بعمود سلسلة نصية (string)  واحد وعمود نصي (text). وسينشئ أيضًا حقلي بصمتي زمن timestamp ليسمح لـ ريلز بتتبع إنشاء المقال وأوقات التحديث.

تنبيه: لمعلومات أكثر عن التهجيرات، ألقي نظرة على تهجيرات التسجيل الحي (Active Record)

في تلك الأثناء، يستخدم الأمر bin/rails للتهجير:

$ bin/rails db:migrate

سينفذ ريلز أمر التهجير هذا ويخبرك بإنشاء الجدول Articles.

==  CreateArticles: migrating ==================================================

-- create_table(:articles)

  -> 0.0019s

==  CreateArticles: migrated (0.0020s) =========================================

ملاحظة: لأنك افتراضيًا تعمل في بيئة تطوير، سيُطبق هذا الأمر في قاعدة البيانات المُعرفة في القسم development من الملف config/database. إذا أردت تنفيذ تهجيرات في بيئة أخرى، على سبيل المثال في بيئة إنتاج، عليك بتمريره بوضوح عند إعلان الأمر:

bin/rails db:migrate RAILS_ENV=production.

حفظ البيانات في وحدة التحكم

بالرجوع إلى وحدة التحكم للمقالات ArticlesController، علينا تغيير الإجراء إنشاء create لاستخدام نموذج المقال Articles الجديد لحفظ البيانات في قاعدة البيانات. افتح app/controllers/articles_controller.rb وغير الإجراء إنشاء create ليبدو هكذا:

def create

 @article = Article.new(params[:article])

 @article.save

 redirect_to @article

end

إليك ما يحدث: كل نموذج ريلز يستحدث بلواحقه الخاصة، المعينة تلقائيًا لأعمدة قاعدة بيانات خاصة به. في السطر الأول قمنا بذلك فقط (تذكر أن [params [ :article يحتوي اللواحق التي نهتم بها). ثم الترميز article.save@ مسئول عن حفظ النموذج في قاعدة البيانات. في النهاية، نعيد توجيه المستخدم إلى الإجراء أظهر show، الذي سنُعرفه فيما بعد.

تنبيه: ربما تتساءل لماذا الحرف A في Article.new حرف كبير بالأعلى، بينما معظم المراجع الأخري لـ articles في هذا الدليل استخدمت حرف A صغير. في هذا الصدد، نحن نشير إلى الصنف Article المُعرف في app/models/article.rb. أسماء الصنف في Ruby يجب أن تبدأ بحرف كبير.
تنبيه: كما سنرى جميعًا فيما بعد، يُرجع article.save@ كائن منطقي boolean يُشير هل حُفظ المقال أم لا

إذا ذهبت الآن إلى http://localhost:3000/articles/new، ستقدر على إنشاء مقال. جرب ذلك، ستحصل على خطأ يبدو كهذا:

يمتلك ريلز العديد من سمات الأمان تُساعدك على كتابة تطبيقات آمنة، وستقوم بأحدهم الآن. يسمى هذا التطبيق  strong parameters، يطلب منا أن نخبر ريلز بالضبط أي المعاملات مسموح بها في إجراءات وحدة التحكم.

لماذا عليك أن تهتم؟ إن القدرة على انتزاع وتعيين كل معاملات وحدة التحكم تلقائيًا إلى نموذجك في لقطة واحدة تُسهل مهمة المبرمج، ولكن هذه الراحة تسمح أيضًا باستخدام ضار. ماذا لو تم إنشاء طلب إلى الخادم ليبدو وكأنه استمارة جديدة للمقال، ولكن يتضمن أيضًا حقولًا إضافية ذات قيم تنتهك سلامة تطبيقك؟   "سيعينون جميعًا" في النموذج الخاص بك ، ثم في قاعدة البيانات جنبًا إلى جنب مع الأشياء الجيدة - ما يحتمل تدمير تطبيقك أو ما هو أسوأ.

عليك بوضع معاملات وحدة التحكم في القائمة البيضاء لمنع أي تعيين جماعي خاطئ. في هذه الحالة، نريد أن نسمح ونطلب العنوان والمعاملات النصية لاستخدام فعال للإجراء إنشاء create. قواعد الصياغة لهذا تقدم require و permit. التغيير سيتضمن سطر واحد في الإجراء إنشاء create:

@article = Article.new(params.require(:article).permit(:title, :text))

عادًة ما يُصنع ذلك في تابعه الخاص لإعادة استخدامه في العديد من الإجراءات لنفس وحدة التحكم، على سبيل المثال، الإجراءان إنشاء create وتحديث update. بعيدًا عن مشاكل التعيين الجماعي، يُجعل التابع دائمًا خاص private لتتأكد من عدم استدعائه خارج السياق المراد له. تظهر النتيجة كالتالي:

def create

 @article = Article.new(article_params)

 @article.save

 redirect_to @article

end

private

 def article_params

   params.require(:article).permit(:title, :text)

 end

تنبيه: لمعلومات أكثر، ألقي نظرة على المرجع بالأعلى وهذا المقال عن المعاملات القوية.

إظهار المقالات

إذا أرسلت الاستمارة ثانية الآن، سيشكو ريلز أنه لا يجد الإجراء إظهار  show. هذا ليس مفيدًا جدًا، لذا لنضيف الإجراء إظهار show قبل التقدم.

كما رأينا في الناتج bin/rails routes، فالمسار للإجراء إظهار show كالتالى:

article GET /articles/:id(.:format)   articles#show

قاعدة الصياغة الخاصة id: تُخبر rails أن هذا المسار يتوقع معامل id:، وفي حالتنا سيكون المُعرف للمقال.

كما فعلنا بالسابق، علينا بإضافة الإجراء إظهار show في app/controllers/articles_controller.rb وواجهته الخاصة.

ملاحظة: هناك ممارسة متكررة بوضع الإجراءات CRUD في كل وحدة تحكم بالترتيب التالى: فهرس (index) وإظهار (show) وجديد (new) وتعديل (edit) وإنشاء (create) وتحديث (update) وتدمير (destroy). ربما تستخدم أي ترتيب تختاره ولكن تذكر أن هذه توابع عامة; كما ذُكر سابقًا في هذا الدليل، يجب وضعهم قبل إعلان الرؤية الخاصة private في وحدةالتحكم.

بهذه المعطيات، دعنا نُضيف الإجراء إظهار  show كالتالى:

class ArticlesController < ApplicationController

 def show

   @article = Article.find(params[:id])

 end

 def new

 end

 # snippet for brevity

يجب أن تنتبه لأمرين. نستخدم Article.find لإيجاد المقال المهتمين به، مرورًا بـ [params [:id للحصول على المعامل id: من الطلب. نستخدم أيضًا متغير لحظي (@prefixed with) للإبقاء على مرجع لكائن المقال. نقوم بذلك لأن ريلز يمرر جميع المتغيرات اللحظية للواجهة.

الآن، أنشئ ملف جديد app/views/articles/show.html.erb بالمحتوى التالي:

<p>

 <strong>Title:</strong>

 <%= @article.title %>

</p>

<p>

 <strong>Text:</strong>

 <%= @article.text %>

</p>

بهذا التغيير، يجب أن تقدر على إنشاء مقالات جديدة. ألقى نظرة على http://localhost:3000/articles/new وحاول القيام بذلك.

قائمة بكل المقالات

نحتاج لطريقة لعمل قائمة بكل مقالاتنا، لذا لنقوم بالأمر. المسار لهذا للمخرج bin/rails routs هو:

articles GET /articles(.:format)       articles#index

أضف الإجراء index المقابل إلى ذلك المسار داخل وحدة التحكم للمقالات ArticlesController في ملف app/controllers/articles_controller.rb. عندما نكتب إجراء فهرس  index، الممارسة المعتادة أن تضعها كالتابع الأول في وحدة التحكم. دعنا نقوم بذلك:

class ArticlesController < ApplicationController

 def index

   @articles = Article.all

 end

 def show

   @article = Article.find(params[:id])

 end

 def new

 end

 # snippet for brevity

وأخيرًا، أضف الواجهة لهذا الإجراء، ومحلها app/views/articles/index.html.erb:

<h1>Listing articles</h1>

<table>

 <tr>

   <th>Title</th>

   <th>Text</th>

   <th></th>

 </tr>

 <% @articles.each do |article| %>

   <tr>

     <td><%= article.title %></td>

     <td><%= article.text %></td>

     <td><%= link_to 'Show', article_path(article) %></td>

   </tr>

 <% end %>

</table>

الآن إذا ألقيت نظرة على  http://localhost:3000/articles سترى قائمة بكل المقالات التي أنشأتها.

إضافة روابط

يمكنك الآن إنشاء وإظهار وعمل قائمة بالمقالات. الآن، دعنا نضيف بعض الروابط للتنقل بين الصفحات.

افتح app/views/welcome/index.html.erb وعدلها كالتالي:

<h1>Hello, ريلز!</h1>

<%= link_to 'My Blog', controller: 'articles' %>

التابع link_to أحد مساعدي الواجهة المدمجين بـ ريلز. يُنشئ رابط أعلى بناءً على النص لعرض ولمعرفة إلى أين تذهب - في هذه الحالة، إلى مسار المقالات.

دعنا نضيف روابط إلى الواجهات الأخرى أيضًا، بدايةً بإضافة هذا الرابط "مقال جديد New Article" إلى app/views/articles/index.html.erb بوضعه فوق الإشارة <table>:

<%= link_to 'New article', new_article_path %>

هذا الرابط سيسمح لك بجلب الاستمارة التي تسمح لك بإنشاء مقال جديد.

الآن، أضف رابط جديد إلى app/views/articles/new.html.erb، تحت الاستمارة، لترجع إلى الإجراء فهرس index:

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

 ...

<% end %>

<%= link_to 'Back', articles_path %>

وأخيرً، أضف رابط إلى القالب app/views/articles/show.html.erb لترجع إلى الإجراء فهرس index أيضًا، حتى يمكن لمن يشاهد مقال واحد أن يرجع ويشاهد القائمة الكاملة مجددًا:

<p>

 <strong>Title:</strong>

 <%= @article.title %>

</p>

<p>

 <strong>Text:</strong>

 <%= @article.text %>

</p>

<%= link_to 'Back', articles_path %>

تنبيه: إذا أردت أن تربط إجراء ما في نفس وحدة التحكم، فليس عليك تحديد الخيار controller:، لأن ريلز سيستخدم وحدة التحكم الحالية افتراضيًا.
تنبيه: في وضع التطوير (وهو ما تعمل عليه افتراضيًا)، يُعيد ريلز تحميل تطبيقك مع كل طلب مُتصفح، لذا ليس عليك أن تتوقف وتعيد بدء خادم الويب عند كل تغيير.

إضافة بعض الاعتماد

ملف النموذج app/models/article.rb بكل بساطة عن:

class Article < ApplicationRecord

end

ليس هناك الكثير لهذا الملف ولكن لاحظ أن الصنف Article مستمد من ApplicatioRecord. وApplicationRecord مستمد ActiveRecord: :Base التي تزود نماذج ريلز بوظائف كثيرة بالمجان، بما في ذلك، عمليات قاعدة البيانات الأساسية CRUD (إنشاء (Create) وقراءة (Read) وتحديث (Update) وتدمير (Destroy)) واعتماد البيانات بالإضافة إلى دعم البحث المعقد والقدرة على الربط بين النماذج المتعددة وبعضها البعض.

يشمل ريلز توابع للمساعدة باعتماد البيانات التي تُرسلها إلى النماذج.

افتح الملف app/models/article.rb وعدله:

class Article < ApplicationRecord

 validates :title, presence: true,

                   length: { minimum: 5 }

end

ستضمن هذه التعديلات أن لكل المقالات عنوان بطول لا يقل عن 5 أحرف. يعتمد ريلز الشروط متنوعة في نموذج ما بما في ذلك، وجود أو تفرد الأعمدة وتنسيقهم ووجود الكائنات المصاحبة. الاعتمادات مغطاة بالتفصيل هنا  Active Record Validations.

بوجود الاعتماد الآن، عندما تستدعي article.save@ لمقال غير معتمد، سيرجع إليك خطأ false. إذا فتحت app/controllers/articles_controller.rb ثانيةً، سترى أننا لم نراجع نتيجة استدعاء article.save@ بداخل الإجراء إنشاء create. إذا فشل article.save@ في هذه الحالة، علينا أن نُظهر الاستمارة للمستخدم.

للقيام بذلك، غيّر الإجراءان جديد new وإنشاء create داخل app/controllers/articles_controller.r للتالي:

def new

 @article = Article.new

end

def create

 @article = Article.new(article_params)

 if @article.save

   redirect_to @article

 else

   render 'new'

 end

end

private

 def article_params

   params.require(:article).permit(:title, :text)

 end

الإجراء جديد new ينشيء مُتغير لاحظي جديد الآن يُسمى article@، وسترى لما هذا في لحظات قليلة.

لاحظ أن بداخل الإجراء إنشاء create نستخدم render بدلًا من redirect_to عندما يُستجاب لـ save بـ false. التابع render يُستخدم حتي يُمرر الكائن article@ إلى القالب الجديد عند صدوره. الصدور يتم خلال نفس الطلب عند تسليم الاستمارة، بينما redirect_to سيخبر المتصفح بإصدار أمر أخر.

إذا أعدت تحميل http://localhost:3000/articles/new  وحاولت حفظ مقال بدون عنوان، سيُرجعك ريلز إلى الاستمارة، ولكن هذا ليس مفيد جدًا. عليك بإخبار المستخدم أن هناك شئ خاطئ. لعمل ذلك، ستُغير app/views/articles/new.html.erb للتحقق من رسائل الخطأ.  

<%= form_with scope: :article, url: articles_path, local: true do |form| %>

 <% if @article.errors.any? %>

   <div id="error_explanation">

     <h2>

       <%= pluralize(@article.errors.count, "error") %> prohibited

       this article from being saved:

     </h2>

     <ul>

       <% @article.errors.full_messages.each do |msg| %>

         <li><%= msg %></li>

       <% end %>

     </ul>

   </div>

 <% end %>

 <p>

   <%= form.label :title %><br>

   <%= form.text_field :title %>

 </p>

 <p>

   <%= form.label :text %><br>

   <%= form.text_area :text %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

<%= link_to 'Back', articles_path %>

القليل من الأشياء تحدث. نتحقق من وجود أي أخطاء مع @article.errors.any? وفي تلك الحالة، نُظهر قائمة بكل الأخطاء من خلال @article.errors.full_messages.

إن pluralize هو مساعد ريلز وله رقم وسلسلة نصية كمعاملات له arguments. إذا كان الرقم أكبر من 1، ستُجمع السلسلة النصية تلقائيًا.

السبب من إضافة article = Article.new@ في وحدة تحكم المقالات ArticlesController أنه لولا ذلك لأصبح article@ قيمته العدم nil في واجهتنا واستدعاء ?article.errors.any@ سيُظهر خطأ.

تنبيه: يغلف ريلز تلقائيًا المجالات التي تحتوي على خطأ على شعبة ذات صنف field_with_errors. يمكنك تعريف قاعدة css ليظهروا.

الآن ستحصل على رسالة خطأ عندما تحفظ مقال بدون عنوان، عندما تحاول القيام بذلك على استمارة المقال الجديدة  http://localhost:3000/articles/new:

تحديث المقالات

لقد غطينا جانبي الإنشاء والقراءة (CR) من العمليات CRUD. دعنا الآن أن نركز على جانب تحديث المقالات (U).

الخطوة الأولى التي سنتخذها هي إضافة إجراء تعديل edit إلى وحدة تحكم المقالات ArticlesController عادةً بين الإجرائين إنشاء create وجديد new كما هو موضح:

def new

 @article = Article.new

end

def edit

 @article = Article.find(params[:id])

end

def create

 @article = Article.new(article_params)

 if @article.save

   redirect_to @article

 else

   render 'new'

 end

end

الواجهة ستحتوي على استمارة مشابهة للاستمارة المستخدمة عند إنشاء مقالات جديدة. أنشئ ملف يُسمى app/views/articles/edit.html.erb واجعله يظهر كالتالى:

<h1>Edit article</h1>

<%= form_with(model: @article, local: true) do |form| %>

 <% if @article.errors.any? %>

   <div id="error_explanation">

     <h2>

       <%= pluralize(@article.errors.count, "error") %> prohibited

       this article from being saved:

     </h2>

     <ul>

       <% @article.errors.full_messages.each do |msg| %>

         <li><%= msg %></li>

       <% end %>

     </ul>

   </div>

 <% end %>

 <p>

   <%= form.label :title %><br>

   <%= form.text_field :title %>

 </p>

 <p>

   <%= form.label :text %><br>

   <%= form.text_area :text %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

<%= link_to 'Back', articles_path %>

في هذا الوقت، نوجه الاستمارة للإجراء تحديث update، الذي لم يُعرف بعد ولكنه سيُعرف قريبًا.

تمرير كائن المقال إلى التابع، سينشئ تلقائيًا عنوان ويب url لتسليم الاستمارة المقال مُعدّلة. يُخبر هذا الاختيار ريلز أننا نريد تلك الاستمارة أن تُسلم من خلال التابع PATCH HTTP وهو التابع HTTP الذي ستستخدمه عند تحديث موارد طبقًا للبروتوكول REST.

المعاملات arguments الخاصة بـ form_with قد تكون كائنات نموذج، لنقل: model: @article والذي سيتسبب في تعبئة المساعد للاستمارة بمجالات الكائن. تمرير الرمز scope كـ(scope: :article) يُنشئ فقط المجالات ولكن بدون أي شيء معبأ بهم. لتفاصيل أكثر ألقي نظرة هنا form_with documentation.

التالى، علينا بإنشاء الإجراء تحديث update في app/controllers/articles_controller.rb. أضفه بين الإجراء إنشاء create والتابع private:  

def create

 @article = Article.new(article_params)

 if @article.save

   redirect_to @article

 else

   render 'new'

 end

end

def update

 @article = Article.find(params[:id])

 if @article.update(article_params)

   redirect_to @article

 else

   render 'edit'

 end

end

private

 def article_params

   params.require(:article).permit(:title, :text)

 end

التابع الجديد تحديث update يُستخدم عندما تُريد تحديث تسجيل موجود بالفعل، ويقبل هاش hash يحتوي على اللواحق attributes التي تُريد تحديثها. كما في السابق، إذا كان هناك خطأ في تحديث المقال الذي تُريده، أظهر الاستمارة للمستخدم.

نُعيد استخدام التابع article_params الذي عرفناه سابقًا للإجراء إنشاء create.

تنبيه: ليس من الضروري إمرار كل اللواحق إلى الإجراء تحديث update. على سبيل المثال، إذا تم استدعاء

('article.update(title: 'A new title@، سيُحدث ريلز اللاحقة title الخاصة بالعنوان ويترك جميع اللواحق الأخرى بدون المساس بها.

في النهاية، نُريد أن نُظهر الرابط للإجراء تعديل edit في قائمة كل المقالات، دعنا نُضيف ذلك الآن إلى app/views/articles/index.html.erb لجعله يظهر بجانب الرابط "إظهار show":

<table>

 <tr>

   <th>Title</th>

   <th>Text</th>

   <th colspan="2"></th>

 </tr>

 <% @articles.each do |article| %>

   <tr>

     <td><%= article.title %></td>

     <td><%= article.text %></td>

     <td><%= link_to 'Show', article_path(article) %></td>

     <td><%= link_to 'Edit', edit_article_path(article) %></td>

   </tr>

 <% end %>

</table>

وسنضيف واحد إلى القالب app/views/articles/show.html.erb أيضًا، ويوجد أيضًا رابط "التعديل Edit" في صفحة مقال. ضف ذلك في أسفل القالب:

...

<%= link_to 'Edit', edit_article_path(@article) %> |

<%= link_to 'Back', articles_path %>

وهذا ما يبدو عليه التطبيق الآن:

استخدام ملفات جزئية لإزالة الاستنساخ/التضاعف في الواجهة

صفحتنا للتعديل edit تبدو مشابهة للصفحة new; في الحقيقة كلاهما يتشارك في نفس الترميز لعرض الاستمارة. دعنا نُزيل هذا الاستنساخ/ التضاعف باستخدام ملف جزئي للواجهة. عادةً، ما تبدأ الملفات الجزئية بشرطة تحتية.

تنبيه: يمكنك القراءة أكثر عن الملفات الجزئية في دليل التخطيطات والصدور في ريلز

أنشئ ملف جديد app/views/articles/_form.html.erb بالمحتوى التالى:

<%= form_with model: @article, local: true do |form| %>

 <% if @article.errors.any? %>

   <div id="error_explanation">

     <h2>

       <%= pluralize(@article.errors.count, "error") %> prohibited

       this article from being saved:

     </h2>

     <ul>

       <% @article.errors.full_messages.each do |msg| %>

         <li><%= msg %></li>

       <% end %>

     </ul>

   </div>

 <% end %>

 <p>

   <%= form.label :title %><br>

   <%= form.text_field :title %>

 </p>

 <p>

   <%= form.label :text %><br>

   <%= form.text_area :text %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

كل شيء ماعدا إعلان form_with بقي كما هو. السبب لاستخدام هذا التصريح form_with الأقل والأبسط ليمثل أيًا من الاستمارات الأخرى أن article is a resource@ المقابل لمجموعة كاملة من المسارات RESTful، ويقدر ريلز على الإشارة إلى أي عنوان URL أو تابع ليستخدم. لمزيد من المعلومات عن استخدام form_with  هذا، ألقى نظرة على النمط اعتمادًا على المورد.

الآن، دعنا نُحدث الواجهة app/views/articles/new.html.erb لاستخدام هذا الملف الجزئي الجديد، وإعادة كتابته تمامًا.

<h1>New article</h1>

<%= render 'form' %>

<%= link_to 'Back', articles_path %>

ثم أفعل نفس الشيء للواجهة app/views/articles/edit.html.erb:

<h1>Edit article</h1>

<%= render 'form' %>

<%= link_to 'Back', articles_path %>

مسح المقالات

نحن الآن مستعدون لتغطية جزء المسح "D" من العمليات CRUD، مسح المقالات من قاعدة البيانات. باتباع المبدأ REST، المسار لمسح المقالات تبعُا للناتج bin/rails routes سيكون:

DELETE /articles/:id(.:format)   articles#destroy

التابع الموجِه delete يجب استخدامه في مسارات لتدمير موارد. إذا تُرك ذلك كمسار get النموذجي، قد يصنع الناس عناوين URLS  ضارة كهذا:

<a href='http://example.com/articles/1/destroy'>look at this cat!</a>

نستخدم التابع delete لتدمير موارد، وهذا المسار يُعين للإجراء تدمير destroy داخل app/controllers/articles_controller.rb، الذي لم يوجد بعد. التابع تدمير destroy هو أخر الإجراءات CRUD في وحدة التحكم، وكالإجراءات العامة الأخرى من CRUD، يجب أن يوضع قبل أي توابع خاصة private أو protected محمية. دعنا نُضيفه: 

$def destroy

 @article = Article.find(params[:id])

 @article.destroy

 redirect_to articles_path

end

وحدة التحكم للمقالات ArticlesController الكاملة في الملف app/controllers/articles_controller.rb يجب أن تبدو الآن هكذا:

class ArticlesController < ApplicationController

 def index

   @articles = Article.all

 end

 def show

   @article = Article.find(params[:id])

 end

 def new

   @article = Article.new

 end

 def edit

   @article = Article.find(params[:id])

 end

 def create

   @article = Article.new(article_params)

   if @article.save

     redirect_to @article

   else

     render 'new'

   end

 end

 def update

   @article = Article.find(params[:id])

   if @article.update(article_params)

     redirect_to @article

   else

     render 'edit'

   end

 end

 def destroy

   @article = Article.find(params[:id])

   @article.destroy

   redirect_to articles_path

 end

 private

   def article_params

     params.require(:article).permit(:title, :text)

   end

end

يُستدعى التابع تدمير destroy من كائنات التسجيل الحي Active Record عندما تُريد مسحهم من قاعدة البيانات. لاحظ أننا لا نُريد إضافة واجهة لهذا الإجراء حيث سنعيد التوجيه إلى الإجراء فهرس index.

في النهاية، أضف الرابط 'تدمير Destroy' إلى قالب الإجراء فهرس index (app/views/articles/index.html.erb) لتغليف كل شيء معًا.

<h1>Listing Articles</h1>

<%= link_to 'New article', new_article_path %>

<table>

 <tr>

   <th>Title</th>

   <th>Text</th>

   <th colspan="3"></th>

 </tr>

 <% @articles.each do |article| %>

   <tr>

     <td><%= article.title %></td>

     <td><%= article.text %></td>

     <td><%= link_to 'Show', article_path(article) %></td>

     <td><%= link_to 'Edit', edit_article_path(article) %></td>

     <td><%= link_to 'Destroy', article_path(article),

             method: :delete,

             data: { confirm: 'Are you sure?' } %></td>

   </tr>

 <% end %>

</table>

هنا نستخدم link_to بطريقة أخرى. نُمرر المسار المُسمى كمعامل argument ثاني، والخيارات كمعامل ثاني. ثم الخيارات كمعامل آخر. الخيارات  method: :delete و{'?data: {confirm: 'Are you sure تُستخدم كلواحق HTML5 حتى عند الضغط على الرابط، سيُظهر ريلز نافذة تأكيد حوارية للمستخدم، ثم يرسل الرابط بالتابع delete. هذا مٌشمل في ملف الـ JavaScript وهو rails-ujs المشمول تلقائيًا في تخطيط تطبيقك (app/views/layouts/application.html.erb) عندما ولدت التطبيق. بدون هذا الملف، لن يظهر صندوق التأكيد الحواري.

تنبيه: تعلم أكثر عن الـ JavaScript غير الظاهرة من دليل العمل مع الـ JavaScript في ريلز.

مبروك، يمكنك الآن إنشاء وإظهار وعمل قائمة وتحديث وتدمير المقالات.

تنبيه: عمومًا،يُشجع ريلز على استخدام كائنات موارد بدلًا من إعلان المسارات يدويًا. لمعلومات أكثر عن التوجيه، ألقى نظرة على توثيق التوجيه.

إضافة نموذج ثاني

حان الوقت لإضافة نموذج آخر للتطبيق. النموذج الآخر سيتعامل مع التعليقات على المقالات.

توليد نموذج

سنذهب لنرى نفس المولد الذي استخدمناه من قبل عند إنشاء نموذج المقال Article. هذه المرة سننشئ نموذج التعليق comment ليكون مرجع لمقال. نفذ سطر الأوامر هذا في الطرفية:

$ bin/rails generate model Comment commenter:string body:text article:references

هذا الأمر سيولد أربع ملفات:

الملف الغرض منه
db/migrate/20140120201010_create_comments.rb التهجير لإنشاء جدول التعليقات في قاعدة البيانات الخاصة بك (اسمك سيشمل بصمة وقت أخرى)
app/models/comment.rb نموذج التعليق Comment
test/models/comment_test.rb اختبار التثبيت لنموذج التعليق
test/fixtures/comments.yml عينة التعليقات لاستخدامها للاختبار

أولًا، ألقى نظرة على app/models/comment.rb:

class Comment < ApplicationRecord

 belongs_to :article

end

هذا شبيه جدًا بنموذج المقال Article الذي رأيته من قبل. الاختلاف في السطر belongs_to :article، الذي يُهيئ ارتباط للتسجيل الحي Active Record. ستتعلم قليلًا عن الارتباطات في القسم التالي من هذا الدليل.

الكلمة المفتاحية (reference:) تُستخدم في الأمر bash هو نوع خاص من البيانات للنماذج. تُنشيء عمود جديد في جدول قاعدة البيانات الخاص بك في اسم النموذج المُقدم مُلحق بها id_ الذي يأخذ قيم صحيحة. لفهم أفضل، حلل الملف db/schema.rb بعد تشغيل التهجير.

بالإضافة إلى النموذج، يقوم ريلز بتهجير أيضًا ليُنشيء جدول قاعدة البيانات المناظر.

class CreateComments < ActiveRecord::Migration[5.0]

 def change

   create_table :comments do |t|

     t.string :commenter

     t.text :body

     t.references :article, foreign_key: true

     t.timestamps

   end

 end

end

يُنشيء سطر t.references عمود صحيح يُسمى article_id، وفهرس له وقيد رئيسي خارجي يُشير إلى العمود id للجدول articles.

اذهب ونفذ التهجير:

$ bin/rails db:migrate

إن ريلز ذكي كفاية لينفذ فقط التهجيرات التي لم تُنفذ بالفعل لقاعدة البيانات الحالية، في هذه الحالة سترى فقط:

==  CreateComments: migrating =================================================

-- create_table(:comments)

  -> 0.0115s

==  CreateComments: migrated (0.0119s) ========================================

ربط النماذج

ارتباطات التسجيل الحي Active Record تسمح لك بالإعلان بسهولة عن العلاقة بين نموذجين. في حالة التعليقات والمقالات، يمكنك أن تكتب العلاقات بهذه الطريقة:

  • كل تعليق ينتمي إلى مقال واحد
  • قد يحتوي المقال الواحد على الكثير من التعليقات

في الحقيقة، هذا قريب جدًا من قواعد الصياغة التى يستخدمها ريلز للإعلان عن هذا الارتباط. لقد رأيت بالفعل سطر الترميز داخل نموذج التعليق comment وهو (app/models/comment.rb) الذي يجعل كل تعليق ينتمي إلى مقال:

class Comment < ApplicationRecord

 belongs_to :article

end

ستحتاج إلى تعديل app/models/article.rb لإضافة الجانب الأخر من الارتباط:

class Article < ApplicationRecord

 has_many :comments

 validates :title, presence: true,

                   length: { minimum: 5 }

end

هذان الإعلانان يُمكنان سلوك أوتوماتيكي إلى حد جيد. على سبيل المثال، إذا كنت تمتلك مُتغير لحظى artilce@، يُمكنك استرداد جميع التعليقات المنتمية إلى ذلك المقال كمصفوفة باستخدام article.comments@.

تنبيه: لمعلومات أكثر عن ارتباطات التسجيل الحي Active Record، ألقى نظرة على دليل ارتباطات التسجيل الحي Active Record.

إضافة مسار للتعليقات

كما في وحدة التحكم Welcome، علينا أن نُضيف مسار ليعرف ريلز أين تُريد أن تنتقل لترى التعليقات. افتح ملف config/routes.rb ثانيةً وعدله كالتالي:

resources :articles do

 resources :comments

end

يُنشيء هذا تعليقات comments كمورد مُتشعب خلال المقالات articles. هذا جزء أخر للإمساك بالعلاقة الهرمية الموجودة بين المقالات والتعليقات.

تنبيه: لمعلومات أكثر عن التوجيه، ألقى نظرة على دليل توجيه ريلز.

توليد وحدة تحكم

بالنموذج الذي في المتناول، تحول انتباهك لإنشاء وحدة تحكم متطابقة. مرة أخرى سنستخدم نفس المولد الذي استخدمناه من قبل.

$ bin/rails generate controller Comments

هذا يُنشيء 5 ملفات ودليل فارغ:

الملف/الدليل الغرض منه
app/controllers/comments_controller.rb وحدة التحكم للتعليقات
app/views/comments/ واجهات وحدة التحكم تُخزن هنا
test/controllers/comments_controller_test.rb اختبار وحدة التحكم
app/helpers/comments_helper.rb ملف مساعد الواجهة
app/assets/javascripts/comments.coffee لغة CoffeeScript لوحدة التحكم
app/assets/stylesheets/comments.scss ورقة النمط المتعاقب لوحدة التحكم

كما في أي مدونة، سيكتب قرائنا تعليقاتهم مباشرة بعد قراءة المقال، وبعد إضافة تعليقهم، سيُرسلون إلى صفحة إظهار المقال ليروا تعليقهم مُدرج. لهذا، تقدم وحدة تحكم التعليقات CommentsController تابع لإنشاء تعليقات ومسح التعليقات المُزعجة عندما يصلون.

لذا أولًا، سنفعل قالب إظهار المقال (app/views/articles/show.html.erb) ليسمح لنا بعمل تعليق جديد.

<p>

 <strong>Title:</strong>

 <%= @article.title %>

</p>

<p>

 <strong>Text:</strong>

 <%= @article.text %>

</p>

<h2>Add a comment:</h2>

<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>

 <p>

   <%= form.label :commenter %><br>

   <%= form.text_field :commenter %>

 </p>

 <p>

   <%= form.label :body %><br>

   <%= form.text_area :body %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

<%= link_to 'Edit', edit_article_path(@article) %> |

<%= link_to 'Back', articles_path %>

سيضيف هذا استمارة إلى صفحة إظهار المقال التي تُنشيء تعليق جديد باستدعاء الإجراء إنشاء create الخاص بـ وحدة تحكم التعليقات CommentsController. الاستدعاء form_with هنا يستخدم مصفوفة، التي ستبني مسار مُشعب مثل articles/1/comments/.

دعنا نشغل الإجراء إنشاء في app/controllers/comments_controller.rb:

class CommentsController < ApplicationController

 def create

   @article = Article.find(params[:article_id])

   @comment = @article.comments.create(comment_params)

   redirect_to article_path(@article)

 end

 private

   def comment_params

     params.require(:comment).permit(:commenter, :body)

   end

end

سترى تعقيد أكثر بقليل هنا عما فعلته في وحدة التحكم الخاصة بالمقالات. هذا تأثير التشعب الذي أعدته. كل طلب لتعليق عليه تتبع المقال الذي يرتبط به التعليق، ومن ثم الاستدعاء الأولى للتابع find لنموذج المقال Article للحصول على المقال المقصود.

بالإضافة إلى ذلك، يأخذ الترميز ميزة من التوابع المتاحة للارتباط. نستخدم التابع إنشاء create في article.comments@ لإنشاء وحفظ التعليق. هذا سيربط التعليق تلقائيًا لينتمي إلى المقال المعين.

عند القيام بالتعليق الجديد، نُرجع المُستخدم مرة أخرى للمقال الأصلي باستخدام المساعد (article_path(@article. كما رأينا بالفعل، هذا سيستدعي الإجراء إظهار show من وحدة تحكم المقال ArticleController وسيصدر بدوره القالب show.html.erb. هذا حيث نُريد إظهار التعليق، لذا لنضيف ذلك إلى app/views/articles/show.html.erb.

<p>

 <strong>Title:</strong>

 <%= @article.title %>

</p>

<p>

 <strong>Text:</strong>

 <%= @article.text %>

</p>

<h2>Comments</h2>

<% @article.comments.each do |comment| %>

 <p>

   <strong>Commenter:</strong>

   <%= comment.commenter %>

 </p>

 <p>

   <strong>Comment:</strong>

   <%= comment.body %>

 </p>

<% end %>

<h2>Add a comment:</h2>

<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>

 <p>

   <%= form.label :commenter %><br>

   <%= form.text_field :commenter %>

 </p>

 <p>

   <%= form.label :body %><br>

   <%= form.text_area :body %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

<%= link_to 'Edit', edit_article_path(@article) %> |

<%= link_to 'Back', articles_path %>

الآن، تُضيف مقالات وتعليقات إلى مدونتك بحيث يظهرون في المواضع الصحيحة.

إعادة هيكلة

الآن، لديك مقالات وتعليقات تعمل، ألقى نظرة على القالب app/views/articles/show.html.erb. ستجده طويل وغير ملائم. يمكنك استخدام ملفات جزئية لمعالجة ذلك.

تصدير المجموعات الجزئية

بدايةً، سننشئ ملف تعليق جزئي لإظهار جميع التعليقات للمقال. أنشئ الملف app/views/comments/_comment.html.erb وضع التالي به:

<p>

 <strong>Commenter:</strong>

 <%= comment.commenter %>

</p>

<p>

 <strong>Comment:</strong>

 <%= comment.body %>

</p>

ومن ثم تُغير app/views/articles/show.html.erb لتبدو كالتالي:

<p>

 <strong>Title:</strong>

 <%= @article.title %>

</p>

<p>

 <strong>Text:</strong>

 <%= @article.text %>

</p>

<h2>Comments</h2>

<%= render @article.comments %>

<h2>Add a comment:</h2>

<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>

 <p>

   <%= form.label :commenter %><br>

   <%= form.text_field :commenter %>

 </p>

 <p>

   <%= form.label :body %><br>

   <%= form.text_area :body %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

<%= link_to 'Edit', edit_article_path(@article) %> |

<%= link_to 'Back', articles_path %>

الآن، هذا سيصدر الملف الجزئي app/views/comments/_comment.html.erb مرة لكل تعليق في المجموعة article.comments@. لأن التابع render يتكرر خلال المجموعة article.comments@، يخصص كل تعليق لمتغير محلى له نفس اسم الملف الجزئي، في هذه الحالة، سيكون التابع تعليق comment وهو متاح لنا في الملف الجزئي لنظهره.

تصدير استمارة الملف الجزئي

دعنا ننقل قسم التعليقات الجديد لذلك للخارج لملفه الجزئي. مرة آخرى، تُنشيء ملف app/views/comments/_form.html.erb يحتوي على:

<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>

 <p>

   <%= form.label :commenter %><br>

   <%= form.text_field :commenter %>

 </p>

 <p>

   <%= form.label :body %><br>

   <%= form.text_area :body %>

 </p>

 <p>

   <%= form.submit %>

 </p>

<% end %>

وثم تجعل app/views/articles/show.html.erb تبدو كالتالي:

<p>

 <strong>Title:</strong>

 <%= @article.title %>

</p>

<p>

 <strong>Text:</strong>

 <%= @article.text %>

</p>

<h2>Comments</h2>

<%= render @article.comments %>

<h2>Add a comment:</h2>

<%= render 'comments/form' %>

<%= link_to 'Edit', edit_article_path(@article) %> |

<%= link_to 'Back', articles_path %>

كلمة render الثانية تُعرف قالب الملف الجزئي الذي نُريد تصديره، comments/form. إن ريلز ذكي كفايةً ليضع السلاش الأمامية في تلك السلسلة النصية ويلاحظ أنك تريد تصدير ملف form.html.erb_ في الدليل app/views/comments.

الكائن article@ متاح لأي ملف جزئي صدر في الواجهة لأننا عرفناه كمتغير لحظى.

مسح التعليقات

ميزة هامة أخرى للمدونة هي إمكانية مسح التعليقات المُزعجة. لعمل ذلك، علينا تنفيذ رابط من نوع ما في الواجهة وإجراء تدمير destroy في وحدة تحكم التعليقات CommentsController.

بدايةً، دعنا نضع رابط المسح في الملف الجزئي app/views/comments/_comment.html.erb.

<p>

 <strong>Commenter:</strong>

 <%= comment.commenter %>

</p>

<p>

 <strong>Comment:</strong>

 <%= comment.body %>

</p>

<p>

 <%= link_to 'Destroy Comment', [comment.article, comment],

              method: :delete,

              data: { confirm: 'Are you sure?' } %>

</p>

بالضغط على رابط مسح التعليق "Destroy Comment" الجديد هذا سيطلق DELETE

/articles/:article_id/comments/:id لوحدة تحكم التعليقات CommentsController الخاص بنا، ويُستخدم هذا لإيجاد التعليق الذي نود مسحه، دعنا نضيف إجراء تدمير destroy لوحدة التحكم الخاصة بنا(app/controllers/comments_controller.rb):

class CommentsController < ApplicationController

 def create

   @article = Article.find(params[:article_id])

   @comment = @article.comments.create(comment_params)

   redirect_to article_path(@article)

 end

 def destroy

   @article = Article.find(params[:article_id])

   @comment = @article.comments.find(params[:id])

   @comment.destroy

   redirect_to article_path(@article)

 end

 private

   def comment_params

     params.require(:comment).permit(:commenter, :body)

   end

end

الإجراء تدمير destroy سيجد المقال الذي نبحث عنه، ويحدد موضع التعليق داخل المجموعة article.comments@، ثم يمسحه من قاعدة البيانات ويرسلنا للإجراء إظهار show الخاص بالمقال.

مسح الكائنات المصاحبة

إذا مسحت مقال، فإن تعليقاته المصاحبة بحاجة للمسح أيضًا، وإلا سيشغلون مساحة في قاعدة البيانات. يسمح ريلز لك باستخدام الاختيار dependent ويعني "التابع" لارتباط ما لتحقيق ذلك. عدل نموذج المقال Article app/models/article.rb، كالتالي:

class Article < ApplicationRecord

 has_many :comments, dependent: :destroy

 validates :title, presence: true,

                   length: { minimum: 5 }

end

الأمان

الاستيثاق الأساسي

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

يُقدم ريلز نظام بسيط جدًا للاستيثاق HTTP والذي سيعمل جيدًا في هذا الموقف.

في وحدة تحكم المقالات ArticlesController علينا أن نملك طريقة لمنع الوصول للإجراءات المتعددة إذا لم يكن الشخص موثوق به. نستخدم التابع http_basic_authenticate_with في ريلز، والذي يسمح بالوصول للإجراء المطلوب إذا سمح هذا التابع له.

لاستخدام نظام الاستيثاق، نحدد ذلك في أعلى وحدة تحكم المقالات ArticlesController في app/controllers/articles_controller.rb. في حالتنا، نُريد المستخدم أن يكون موثوقًا لكل إجراء ماعدا الإجراء فهرس index والإجراء إظهار show، لذا نكتب ذلك:

class ArticlesController < ApplicationController

 http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]

 def index

   @articles = Article.all

 end

 # snippet for brevity

نُريد أن نسمح للمستخدمين الموثوقين فقط بمسح التعليقات، لذا في وحدة تحكم التعليقات CommentsController (app/controllers/comments_controller.rb)  ونكتب:

class CommentsController < ApplicationController

 http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy

 def create

   @article = Article.find(params[:article_id])

   # ...

 end

 # snippet for brevity

الآن إذا حاولت إنشاء مقال جديد، سيُرحب بك تحدي للاستيثاق بنظام HTTP الأساسي:

هناك توابع استيثاق أخرى متاحة لتطبيقات ريلز. إضافتان مشهورتان للاستيثاق لـ ريلز هما Devise rails engine و Authlogic gen بالإضافة لعدد من الإضافات الأخرى.

اعتبارات الأمان الأخرى

الأمان، خصوصًا في تطبيقات الويب، منطقة واسعة ومُفصلة. الأمان في تطبيق ريلز الخاص بك مُغطى بعمق أكثر في دليل الأمان في Ruby on ريلز

ما هو التالي؟

الآن رأينا تطبيق ريلز الخاص بك الأول، عليك أن تشعر بالحرية لتحديثه وتجربته بنفسك.

تذكر ليس عليك القيام بكل شيء بدون مساعدة. لأنك تحتاج إلى المساعدة في بدء وتشغيل ريلز، أشعر بالحرية لطلب الاستشارة من تلك الموارد الداعمة:

أدلة Ruby on ريلز

دورة Ruby on ريلز التعليمية

قائمة المراسلة الخاصة بـ Ruby on ريلز

قناة rubyonrails# على irc.freenode.net

تهيئة Gotchas

أسهل طريقة للعمل على ريلز أن تُخزن كل الداتا الخارجية كـ UTF-8. إذا لم تفعل، مكتبات Ruby و ريلز ستكون قادرة على تحويل كل الداتا الأصلية لك إلى UTF8، ولكن هذا لا يعمل بشكل موثوق به، لذا من الأفضل أن تتأكد أن كل البيانات الخارجية UTF-8.

إذا أخطأت في تلك المنطقة، العَرَضْ الشائع ماسة سوداء بداخلها علامة استفهام تظهر في المُتصفح. عَرَضْ أخر، هي الرموز ك "¼A" تظهر بدلًا من "ü". يأخذ ريلز عدد من الخطوات الداخلية لمعالجة الأسباب الشائعة لتلك المشاكل التي يمكن اكتشافها وتصحيحها تلقائيًا. ولكن إذا كان لديك بيانات خارجية غير مخزنة كـ UTF-8، ربما تتسبب عرضيًا في تلك الأنواع من المشاكل التي لا تُكتشف تلقائيًا أو تُصحح بواسطة ريلز.

مصدران شائعان جدًا للبيانات التي ليست URF-8:

محرر النص لديك: معظم محرري النصوص (مثل TextMate)، افتراضيًا يحفظون الملفات كـ UTF-8. إذا لم يكن محررك كذلك، سينتج عن ذلك رموز خاصة تُدخلها في قوالبك (مثل é) أن يظهر كماسة بداخلها علامة استفهام في المُتصفح. ينطبق هذا أيضًا على ملفات الترجمة i18n. معظم المحررين والتي بالفعل ليست افتراضية لـ UTF-8 (مثل بعض الإصدارات من Dreamweaver) تعرض طريقة لتغيير الافتراضي إلى UTF-8. افعل ذلك.

قاعدة بياناتك: اقتراضيًا يُحول ريلز البيانات من قاعدة بياناتك إلى UTF-8. ولكن  إذا لم تكن قاعدة بياناتك تستخدم UTF-8 داخليًا، ربما لا تقدر على حفظ كل الرموز التي يُدخلها مستخدموك.  على سبيل المثال، إذا كانت قاعدة بياناتك تستخدم الرموز اللاتينية Latin-1 داخليًا، ويُدخل مستخدموك رمز روسي أو عبري أو ياباني، ستضيع البيانات إلى الأبد بمجرد دخولها في قاعدة البيانات. إذا أمكن، استخدم UTF-8 للتخزين الداخلي لقاعدة البيانات.

ملاحظات المستخدمين

نُشجعك أن تُساعد في تحسين جودة هذا الدليل.

رجاءً شارك في حال رأيت أي أخطاء كتابية أو تقنية. للبدء، أقرأ قسم مساهمات المستندات.

ربما تجد محتوى غير كامل أو مضمون غير حديث

رجاءً أضف أي مستندات مفقودة من المستند الرئيسي. تأكد من مراجعتك لأدلة Edge أولًا لتتأكد إذا ما كانت المشاكل قد حُلت بالفعل أو لا في فرع المستند الرئيسي. ألقي نظرة على القواعد الإرشادية لأدلة Ruby on ريلز للنمط والأعراف.

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

وأخيرًا وليس أخرًا، لأي مناقشة عن مستندات Ruby on ريلز مرحب بك على قائمة مراسلة مستندات Ruby on ريلز