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



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

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

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

نوع المُحتوى


التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

التصنيفات

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

أسئلة وأجوبة

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

التصنيفات

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

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

  1. بعد برمجتك لموقعك باستخدام روبي وإطار ريلز، حان الوقت الآن لنشر التطبيق على خادم (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
  2. ما هي لغة البرمجة التي يجب عليك تعلمها؟ بايثون، PHP أم روبي؟ ربما أكبر سؤال يواجه مطوري الويب المبتدئين هي ما هي أفضل لغة برمجة يجب تعلمها للوظيفة المستقبلية والآفاق، هنالك العديد من لغات البرمجة التي يجب عليك الاختيار منها وكل واحدة لديها نقاط القوة والضعف، ربما أهم مصدر مقلق هو المدة التي تستغرقه كل لغة للتعلم، وهذا يختلف من لغة إلى أخرى، فما الذي ينبغي تعلمه للحصول على أفضل وظيفة ناجحة في البرمجة؟ عندما بدأتُ تعلم تطوير الويب بحثتُ كثيرا حوله وجعلني هذا مرتبكًا بعض الشيء، فلم أتمكن من الحصول على إجابة نهائية حول ما هي أفضل لغة لأن كل كاتب له وجهة نظر مختلفة، وكان جميع الكتّاب يقارنون اللغات دون إعطاء استنتاج شامل. لذلك، في هذه المقالة، سأقارن بين اللغات البرمجة الحديثة الثلاثة، PHP وبايثون وروبي، وقبل كل شيء، أريد أن أظهر لك بعض البيانات القديمة التي وجدتها عندما بدأت بالتعلم حول تطوير الويب، وهذه البيانات القديمة مأخوذة من هنا وعلى الرغم من قِدَمِهَا إلا أنها تحتوي على بعض الحقائق المفيدة حول تاريخ هذه اللغات. بايثون ضد PHP ضد روبي من هذه المقارنة بين PHP وبايثون وروبي، ستجد أنه من الواضح: الطلب العالي على PHP في السوق. مبرمجي PHP مطلوبين بكثرة. قابلية استخدام PHP ضعيفة. تعلم واستخدام بايثون هو الأسهل. بايثون هي الأسرع. تعلم روبي هو الأصعب. يملك روبي أعلى قابلية استخدام. لكن كما قلت سابقًا، هذه مقارنة مختصرة، ومن هذه المقارنة لا يمكننا الاستنتاج أن PHP هو الأفضل وأن روبي هو الأسوأ، فهنالك أسئلة أخرى لم نرد عليها بعد. ما هي اللغة الأكثر أمانًا؟ ما هي القيود الأخرى على هذه اللغات؟ ما هي مزايا وأشكال الدعم الأخرى لهذه اللغات؟ ما هي نسبة وظيفة/مطور وما متوسط رواتب المطورين؟ ما هي اللغة الأكثر أمانا؟ يعتمد أمن الموقع/التطبيق في المقام الأول على المبرمج وليس لغة البرمجة، فمن المعروف أن PHP هي لغة برمجة غير آمنة، وهذا بسبب أن صياغة PHP ليست بسهولة صياغة بايثون أو روبي، وبسبب هذه الصياغة المعقدة (قابلية القراءة منخفضة) فإن فرص حدوث الأخطاء أكبر. فإذا كان المبرمج/المطور لا يخطئ، فليست PHP لغة غير آمنة، وفي الواقع، لدى PHP5 سمعة طيّبة مقارنة بالإصدارات القديمة، وكنتيجة لذلك، لا يوجد في الواقع استنتاج قوي حول مسألة أمن اللغة. ما هي قيود هذه اللغات؟ PHP: يملك الكثير من الصيغ المطولة من C++/C وبيرل، مع الكثير من الأقواس المعقوفة وعلامات الدولار و “→”. جدول مربك لأسماء الدوال، فتمتلك المكتبة المدمجة مجموعة كبيرة من اتفاقيات التسمية، ويملك الدوال في العادة على بادئة للدلالة على مصدرها (ولكنها لا تمتلك ذلك في كثير من الأحيان). تكون الدوال في العادة في أصناف لمحاكاة مساحات الأسماء (namespace). ليست مناسبة للتطبيقات الكبيرة: صعوبة الصيانة لأنها لا تتكون من وحدات بشكل جيّد. نوع ضعيف: التحويل الضمني قد يفاجئ المبرمجين الغافلين ويؤدي إلى أخطاء غير متوقعة، على سبيل المثال، تتساوى السلاسل “1000” و“1e3” عند المقارنة لأنها تحوّل ضمنيا إلى أعداد كسرية. لغة برمجة سكربتية، ليست لغة برمجة كائنية التوجه. بايثون: لا تملك بيان ‘switch’ ولا هيكل ‘do … while’. عوامل الزيادة والنقصان والتعيين (التعيين هو بيان فقط في بايثون). العامل/البيان الثلاثي (… ? … : …). بيئة مناسبة (مثبتة عادة). لا توجد مبدلات (modifier) خاصة، محمية و عامة للخصائص والأساليب. لا تملك abstract ومبدلات نهائية للأصناف والأساليب. روبي: قد تكون صعبة التعلم. تفتقر إلى موارد المعلومات. وقت المعالج بطيء (وقت CPU) بالمقارنة مع لغات برمجة أخرى. التطوير والتحديث أبطأ. ما هي مزايا وأشكال الدعم الأخرى لهذه اللغات؟ PHP: سهلة التعلم للمبتدئين وللمطورين القادمين من لغات ذات صياغة مشابهة مثل C و C++ و C# وجافا وجافا سكربت، إلخ. تدعم جميع خوادم ويب الرئيسية مثل اباتشي، مايكروسوفت IIS، Netscape و الخوادم الشخصية و خادم iPlanet… الخ. مقبولة للغاية من قبل صناعات عديدة وهي أكثر شعبيّة من بقية لغات البرمجة. سهولة استضافة PHP مباشرة دون أي إعدادات للخادم لأن استضافة المواقع صعبة بالنسبة للمبتدئين. يعتبر إطار zend هو الأشهر في PHP، وبعض الأطر الأخرى مثل Yii وLaravel وCodeigniter أعطت قوة كبيرة لـ PHP. من السهل تطوير أي نوع من CMS بسرعة. بايثون: سهولة الصياغة والقراءة، وسهلة التعلم للذين لا يمتلكون خبرة في البرمجة. لغة برمجة أغراض عامة. لغة برمجة كائنية التوجه. بايثون مناسبة جدا لـ glue. مكتبة قياسية ضخمة، كمثال عشوائي، تملك بايثون محللين لـ XML، قراء وكتّاب لملفات zip وcsv، مكتبات لاستخدام كل بروتوكول إنترنت ونوع بيانات، الخ. هياكل بيانات متطور كالقوائم والقواميس. أطر كبيرة لتطوير الويب مثل جانغو وFlask. روبي: تعتبر روبي لغة كائنية التوجه أكثر من بايثون وPHP. تم تطوير روبي لجعل عمل المطور سهل. تسمح الإضافات الأصناف والوحدات المستدعية بالتوسع والتغيير عن طريق الشيفرة البرمجية الخاصة بالمستخدمة بعد استدعائها، فالأصناف لا تغلق في روبي، حيث يمكنك تغييرها لاحقا. كتل الشيفرة البرمجية الخاصة بروبي أقوى من lambda الخاص ببايثون. كل شيء يرجع قيمة (بما في ذلك بيان ‘if’) ، وبسبب هذا، من السهل كتابة شيفرة برمجية مشابهة للدالة مقارنة بالبايثون وأسهل عند سلسلة الأوامر. مكتبات Gem الخاصة بالروبي، فالكثير من gem المتاحة تجعل فعل أي مهمة سهلةً بأقل جهد. قوة روبي في تطوير الويب هو إطار ريلز، فهو سحري جدًا، فلن تحتاج إلى القلق بشأن الوظائف الأساسية لتطوير الويب مثل التعديل والحذف البسيط، بل يمكنك التركيز على الخوارزميات المعقّدة الأخرى. يعتقد الروبي في إعطاء المبرمج الحرية والقوة. ما هي نسبة وظيفة/مطور وما متوسط رواتب المطورين؟ هذا السؤال شخصي للغاية ولن تكون له إجابة عامة بسيطة، فأغلب الوظائف والمبرمجين يستخدمون PHP، وأدى هذا إلى ضعف الرواتب بصفة عامة، ومع ذلك، فهذا لا يعني أن رواتب جميع المطورين/المبرمجين ضعيف، فبعض المطورين يحصلون على أموالٍ كثيرةٍ. هنالك فرص عمل كثيرة لمبرمجي بايثون وروبي بما أنهم يمتلكون عدد قليل من المبرمجين، لكن الطلب أقل في العادة. استنتاج عام: دعونا نلخص كل هذا باختصار: PHP وبايثون سهل التعلم بالنسبة للمبتدئين. PHP هي الأكثر طلبًا في العمل. PHP جيّدة للمواقع الصغيرة. (على الرغم من أن هنالك الكثير من المواقع الكبيرة مثل فيسبوك و ويكيبيديا مكتوبة بلغة PHP لكن ضمّن إليها لغات أخرى. تغيير موقع من لغة برمجة إلى لغة أخرى ليست مهمة سهلة). بايثون هي أفضل لغة للأغراض العامة. تُفضل إطارات بايثون (جانغو) وروبي (ريلز) أكثر من أي إطار آخر لبرمجة الويب. يمكنك اختيار أي واحدة منهم، فإذا لم تكن تبحث عن هدف كبير فبايثون هي الأفضل ليس فقط بسهولة تعلمها بل لأنها للأغراض العامة وتملك العديد من المميزات الكبيرة مثل هياكل البيانات، لكن جميع لغات البرمجة لديها نطاقات خاصة بها مثل اللاعبين في أي لعبة. ترجمة -وبتصرّف- للمقال PYTHON vs PHP vs RUBY لصاحبه Harish Kumar
  3. الكثير من الناس يسمع عن لغات البرمجة ومدى رواجها في عالم الأعمال. وأصبح هناك توجه كبير نحو تعلّمها، وأصبحت لغات البرمجة هي لغات المستقبل والبرمجة مهنة المستقبل، وهنا السؤال الذي يتبادر إلى الأذهان ما هي اللغة التي يجب الانطلاق منها؟ سأكون معكم اليوم لأجيب عن هذا السؤال. وسوف أتحدث عن لغة البرمجة روبي وما الذي يجعلها خيارا مناسبًا لتبدأ به. تمتلك لغة روبي مجتمعاً كبيراً، وتكمن أهمية المجتمع الكبير بتوفيره الدعم الأفضل للمبرمجين سواء على مستوى المصادر والتوثيقات أو على مستوى المكتبات البرمجية المساعدة، فهي تملك مجتمعًا ناضجًا ومستقرًا. وتحظى لغة روبي بشعبية كبيرة في مجال تطوير الويب بشكلٍ خاص، فباستخدام إطار العمل Ruby on Rails يمكن للمطور البدء في بناء هيكلية الموقع وتنفيذ فكرته ورؤية النتائج عبر بضع أوامر، حيث أحدثَ إطار العمل Rails نقلة نوعية في مفاهيم وتطبيقات الويب، هذه المفاهيم تم تبنيها من قبل أُطر عمل أخرى لتصبح ركيزة لتطبيقات الويب الحديثة، لذا فالعديد من الشركات الناشئة ورواد الاعمال والمستقلين على حدٍ سواء يستخدمونها في بناء مشاريعهم. ويوفر إطار العمل RoR الكثير من الأدوات التي تجعل من تطوير تطبيقات الويب أمرًا منظما ويسيرًا. ويقدم إطار العمل Rails العديد من الحلول التي من شأنها تعزيز العمل المشترك بين المطورين وتقديم الأفكار والأدوات اللازمة لبناء تطبيقات ويب قابلة للتوسع والتطوير في المستقبل.
  4. هذا هو دليل Airbnb لنمط روبي. مستوحى من دليل GitHub ودليل Bozhidar Batsov. المسافات البيضاء المسافة الفارغة استخدم علامات التبويب (tabs) ذات المسافتين. يكون عمق when بقدر عمق case. case when song.name == 'Misty' puts 'Not again!' when song.duration > 120 puts 'Too long!' when Time.now.hour > 21 puts "It's too late" else song.play end kind = case year when 1850..1889 then 'Blues' when 1890..1909 then 'Ragtime' when 1910..1929 then 'New Orleans Jazz' when 1930..1939 then 'Swing' when 1940..1950 then 'Bebop' else 'Jazz' end ترصيف معاملات الدالة إما على نفس السطر أو سطر واحد لكل واحدة. # bad def self.create_translation(phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, allow_verification) ... end # good def self.create_translation(phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, allow_verification) ... end # good def self.create_translation( phrase_id, phrase_key, target_locale, value, user_id, do_xss_check, allow_verification ) ... end ضع علامة التبويب في التعبيرات المنطقية متعددة السطور. # bad def is_eligible?(user) Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) && is_in_program?(user) && program_not_expired end # good def is_eligible?(user) Trebuchet.current.launch?(ProgramEligibilityHelper::PROGRAM_TREBUCHET_FLAG) && is_in_program?(user) && program_not_expired end على السطر لا تترك مسافة فارغة زائدة. عند تضمين التعليقات في السطر، اترك مسافة واحدة بين نهاية الشيفرة البرمجية وبداية تعليقك. # bad result = func(a, b)# we might want to change b to c # good result = func(a, b) # we might want to change b to c استخدم المسافات حول العوامل (operators)، أي بعد الفواصل، بعد النقطتين، الفاصلة المنقوطة، وبعد { وقبل }. sum = 1 + 2 a, b = 1, 2 1 > 2 ? true : false; puts 'Hi' [1, 2, 3].each { |e| puts e } لا تضع مسافة قبل الفاصلة. result = func(a, b) لا تضع مسافة دخل أنابيب (pipe) كتلة المعاملات، بل ضع واحد بين المعاملات في الكتلة وواحدة خارج أنابيب كتلة المعاملات. # bad {}.each { | x, y |puts x } # good {}.each { |x, y| puts x } لا تضع فراغات بين ! ومعاملاتها. !something لا تترك فارغات بعد (، [ وقبل )، ]. some(arg).other [1, 2, 3].length تجنب وضع المسافات عند استيفاء السلاسل النصية. # bad var = "This #{ foobar } is interpolated." # good var = "This #{foobar} is interpolated." لا تستخدم مسافة إضافية في النطاق الحرفي. # bad (0 ... coll).each do |item| # good (0...coll).each do |item| أسطر جديدة أضف سطر جديد بعد شروط if لكي تكون في عدة أسطر ولتساعد في التفريق بين الشروط والجسم. if @reservation_alteration.checkin == @reservation.start_date && @reservation_alteration.checkout == (@reservation.start_date + @reservation.nights) redirect_to_alteration @reservation_alteration end أضف سطر جديد بعد الشروط، الكتل، البيانات، إلخ… if robot.is_awesome? send_robot_present end robot.add_trait(:human_like_intelligence) لا تضع أسطر جديدة بين مناطق مسافات البادئة المختلفة (مثلا حول أجزاء الصنف أو الوحدة). # bad class Foo def bar # body omitted end end # good class Foo def bar # body omitted end end أضف سطر جديد واحد فقط بين الأساليب. def a end def b end استخدم سطر فارغ واحد للكسر بين البيانات لتقسيم الأساليب إلى فقرات منطقية داخليًا. def transformorize_car car = manufacture(options) t = transformer(robot, disguise) car.after_market_mod! t.transform(car) car.assign_cool_name! fleet.add(car) car end ضع سطر فارغ في نهاية كل ملف، ولا تضع أكثر من سطر فارغ. طول السطر اجعل كل سطر قابل للقراءة، ما لم يكن لديك سبب معين، ابقي طول الأسطر أقل من 100 حرف (أساس منطقي). التعليقات على الرغم من أن التعليقات متعبة عند كتابتها، إلا أنها مفيدة للغاية لإبقاء شيفرتك البرمجية قابلة للقراءة، وتصف القواعد التالية ما يجب التعليق عليه وأين، لكن تذكر: على الرغم أن التعليقات مهمة جدًا إلا أن أفضل شيفرة برمجية هي التي توثق ذاتها، فإعطاء أسماء معقولة للأنواع والمتغيرات أفضل بكثير من استخدام أسماء غامضة يجب عليك شرحها من خلال التعليقات. عند كتابة تعليقاتك، اكتب للجمهور: المساهم التالي سيحتاج إلى فهم شيفرتك البرمجية، كن سخيًا، فالتالي قد يكون أنت! تعليقات ملف/مستوى الصنف يجب أن يكون لكل تعريف صنف تعليق مصاحب يصف ما هو وكيف يُستخدم. يجب أن يملك الملف الذي لا يحتوي على أصناف أو يحتوي على أكثر من صنف واحد على تعليق في أعلاه يصف محتوياته. # Automatic conversion of one locale to another where it is possible, like # American to British English. module Translation # Class for converting between text between similar locales. # Right now only conversion between American English -> British, Canadian, # Australian, New Zealand variations is provided. class PrimAndProper def initialize @converters = { :en => { :"en-AU" => AmericanToAustralian.new, :"en-CA" => AmericanToCanadian.new, :"en-GB" => AmericanToBritish.new, :"en-NZ" => AmericanToKiwi.new, } } end ... # Applies transforms to American English that are common to # variants of all other English colonies. class AmericanToColonial ... end # Converts American to British English. # In addition to general Colonial English variations, changes "apartment" # to "flat". class AmericanToBritish < AmericanToColonial ... end يجب أن تملك جميع الملفات بما في ذلك ملفات البيانات والتهيئة، على تعليقات على مستوى الملف. # List of American-to-British spelling variants. # # This list is made with # lib/tasks/list_american_to_british_spelling_variants.rake. # # It contains words with general spelling variation patterns: # [trave]led/lled, [real]ize/ise, [flav]or/our, [cent]er/re, plus # and these extras: # learned/learnt, practices/practises, airplane/aeroplane, ... sectarianizes: sectarianises neutralization: neutralisation … تعليقات الدالة يجب أن يكون لكل إعلان دالة تعليقات تسبقها مباشرة تصف ما تقوم به الدالة وكيفية استخدامها، ويجب أن تكون هذه الملفات وصفية (‘تفتح الملف’) بدلا من أمرية (‘افتح الملف’). التعليقات التي تصف الدالة، لا تقول ما تقوم به الدالة، بشكل عام، هذه التعليقات لا تصف كيفية عمل الدالة، وبدلا من ذلك، يجب ترك هذه للتعليقات التي تتخلل الشيفرة البرمجية للدالة. يجب أن تذكر كل دالة ما هي المدخلات وما هي المخرجات، ما لم تستوف جميع المعايير التالية: غير مرئية من الخارج قصيرة جدا واضحة يمكنك استخدام أي تنسيق تريده، في الروبي، يوجد مخططين لتوثيق الدالة وهما TomDoc وYARD. يمكنك كتابة الأشياء بشكل موجز: # Returns the fallback locales for the_locale. # If opts[:exclude_default] is set, the default locale, which is otherwise # always the last one in the returned list, will be excluded. # # For example: # fallbacks_for(:"pt-BR") # => [:"pt-BR", :pt, :en] # fallbacks_for(:"pt-BR", :exclude_default => true) # => [:"pt-BR", :pt] def fallbacks_for(the_locale, opts = {}) ... end الكتلة والتعليقات المضمّنة المكان النهائي للتعليقات هو في الأجزاء الصعبة من التعليمات البرمجية، فعّلق عليها الآن إذا كان يجب عليك شرحها عند مراجعة الشيفرة البرمجية. يجب أن تحصل العمليات المعقدة على بضعة أسطر من التعليقات قبل بدء العمليات، بالنسبة للأجزاء الغير واضحة تحصل على تعليقات في نهاية السطر. def fallbacks_for(the_locale, opts = {}) # dup() to produce an array that we can mutate. ret = @fallbacks[the_locale].dup # We make two assumptions here: # 1) There is only one default locale (that is, it has no less-specific # children). # 2) The default locale is just a language. (Like :en, and not :"en-US".) if opts[:exclude_default] && ret.last == default_locale && ret.last != language_from_locale(the_locale) ret.pop end ret end من ناحية أخرى، لا تصف شيفرتك البرمجية، افترض أن الشخص الذي يقرأ شيفرتك البرمجية يعرف لغة البرمجة (وإن لم يكن ما تحاول القيام به) أفضل منك. وعلى سياق متصل: لا تستخدم كتل التعليقات، فلا يمكن أن تسبقها بمسافة وليس من السهل رؤيتها كالتعليقات المنتظمة. # bad =begin comment line another comment line =end # good # comment line # another comment line علامات الترقيم، الإملاء والنحو اهتم بعلامات الترقيم، الإملاء والنحو، فمن السهل قراءة التعليقات المكتوبة بشكل جيد من تلك المكتوبة بشكل سيء. ينبغي أن تكون التعليقات قابلة للقراءة كنص سردي، مع وجود الأحرف الكبيرة وعلامات الترقيم. في حالات كثيرة، الجمل الكاملة هي الأكثر قابلية للقراءة من جمل القصيرة. يمكن للتعليقات القصيرة، كالتعليقات في نهاية سطر شيفرة برمجية، أن تكون أقل رسمية، لكن يجب أن تكون متسقة مع نمطك. على الرغم من أنه من المحبط أن يشير مُراجِع شيفرتك البرمجية إلى أنك تستخدم فاصلة في مكان الفاصلة المنقوطة، لذا من المهم أن تحافظ شيفرتك البرمجية على مستوى عال من الوضوح وقابلية القراءة، وتساعد علامة الترقيم، الإملاء والنحو في تحقيق هذا الهدف. تعليقات TODO استخدم تعليقات TODO للشيفرات البرمجية المؤقتة، حل قصير الأجل أو جيد بما يكفي ولكن ليس مثال. يجب أن تتضمن TODO كلمة TODO بالأحرف الكبيرة متبوعة باسم الكامل للشخص الذي يمكنه توفير أفضل سياق حول المشكلة المشار إليها من قبل TODO بين قوسين. يمكنك إضافة النقطتين ومن ثم ضع تعليق يشرح ما يجب القيام به، فالغرض الرئيسي و أن يكون تنسيق TODO قابل للبحث لإيجاد الشخص الذي يمكنه توفير المزيد من التفاصيل عند الطلب، فـ TODO ليس التزاما من الشخص المشار إليه لإصلاح المشكلة، وبالتالي عند إنشاء TODO، تكتب في الغالب اسمك. # bad # TODO(RS): Use proper namespacing for this constant. # bad # TODO(drumm3rz4lyfe): Use proper namespacing for this constant. # good # TODO(Ringo Starr): Use proper namespacing for this constant. شيفرات برمجية بدون تعليقات لا تترك شيفرات برمجية بدون تعليقات في قاعدة الشيفرات البرمجية الخاصة بنا. الأساليب تعريف الأساليب استخدم def مع الأقواس عند وجود معاملات، واحذف الأقواس عندما لا يقبل الأسلوب المعاملات. def some_method # body omitted end def some_method_with_parameters(arg1, arg2) # body omitted end لا تستخدم المعاملات الموضعية الافتراضية، بل استخدم معاملات الكلمات الرئيسية (keyword) – إذا كانت موجودة، في الإصدار 2 من روبي أو أحدث - أو hash من الخيارات. # bad def obliterate(things, gently = true, except = [], at = Time.now) ... end # good def obliterate(things, gently: true, except: [], at: Time.now) ... end # good def obliterate(things, options = {}) options = { :gently => true, # obliterate with soft-delete :except => [], # skip obliterating these things :at => Time.now, # don't obliterate them until later }.merge(options) ... end تجنب الأساليب المتكونة من سطر واحد، على الرغم من انتشارها، إلا أنه توجد بعض الخصوصيات حول صياغتها تجعل استخدامها غير مرغوب فيه. # bad def too_much; something; something_else; end # good def some_method # body end دوال الاستدعاء استخدم الأقواس لدالة الاستدعاء: • إذا كانت الدالة ترجع قيمة. # bad @current_user = User.find_by_id 1964192 # good @current_user = User.find_by_id(1964192) إذا كان المعامل الأول إلى الدالة يستخدم الأقواس. # bad put! (x + y) % len, value # good put!((x + y) % len, value) لا تضع أبدًا مسافة بين اسم الدالة والقوس الأول. # bad f (3 + 2) + 1 # good f(3 + 2) + 1 تجنب الأقواس عند استدعاء الدالة إذا لم يكن يقبل أية معاملات. # bad nil?() # good nil? ستكون الأقواس اختيارية إذا لم تكن الدالة ترجع قيمة (أو لا نهتم بما ترجعه)، خاصة إذا كانت المعاملات في عدة أسطر، فالأقواس ستزيد من قابلية القراءة. # okay render(:partial => 'foo') # okay render :partial => 'foo' في كلا الحالتين: في حالة قبول الدالة لـ hash من الخيارات كمعامل أخير، لا تستخدم { } أثناء الاستدعاء. # bad get '/v1/reservations', { :id => 54875 } # good get '/v1/reservations', :id => 54875 التعبيرات الشرطيّة الكلمات الرئيسية المشروطة لا تستخدم then لـ if/unless متعددة الأسطر. # bad if some_condition then ... end # good if some_condition ... end لا تستخدم do لـ while أو until متعددة الأسطر. # bad while x > 5 do ... end until x > 5 do ... end # good while x > 5 ... end until x > 5 ... end إن الكلمات الرئيسية and وor وnot محظورة، فهي لا تستحق العناء، استخدم && و || و ! بدلا منها. يمكنك استخدام المعدِل if/unless عندما يكون الجسم بسيط، الشرط بسيط وكل شيء في سطر واحد، وخلافًا لذلك، تجنب استخدام if/unless. # bad - this doesn't fit on one line add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) if request_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty? # okay if request_opts[:trebuchet_experiments_on_page] && !request_opts[:trebuchet_experiments_on_page].empty? add_trebuchet_experiments_on_page(request_opts[:trebuchet_experiments_on_page]) end # bad - this is complex and deserves multiple lines and a comment parts[i] = part.to_i(INTEGER_BASE) if !part.nil? && [0, 2, 3].include?(i) # okay return if reconciled? لا تستخدم أبدًا unless مع else، أعد كتابتها مع الحالة الصحيحة الأولى أولًا. # bad unless success? puts 'failure' else puts 'success' end # good if success? puts 'success' else puts 'failure' end تجنب unless مع شروط متعددة. # bad unless foo? && bar? ... end # okay if !(foo? && bar?) ... end تجنب استخدام unless مع عامل مقارنة إذا كان يمكنك استخدام if مع عامل المقارنة المعاكس. # bad unless x == 10 ... end # good if x != 10 ... end # bad unless x < 10 ... end # good if x >= 10 ... end # ok unless x === 10 ... end لا تستخدم أقواس حول شرط if/unless/while. # bad if (x > 10) ... end # good if x > 10 ... end العامل الثلاثي تجنب استخدام العامل الثلاثي (?:) إلا في الحالات التي تكون فيها جميع التعبيرات بسيطة، ومع ذلك لا تستخدمه بدلا من if/then/else/end في الشروط المتكونة من سطر واحد. # bad result = if some_condition then something else something_else end # good result = some_condition ? something : something_else استخدم تعبير واحد لكل فرع في العامل الثلاثي، كذلك لا يجب أن يكون العامل الثلاثي متداخِلا، حيث يُفضّل استخدام بنية if/else في هذه الحالات. # bad some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else # good if some_condition nested_condition ? nested_something : nested_something_else else something_else end تجنب الشروط المتعدّدة في العامل الثلاثي حيثُ يُفضّل استخدام الأخير مع الشروط الفردية. تجنب الأسطر المتعدّدة ?: (العامل الثلاثي)، واستخدم if/then/else/end بدلا منه. # bad some_really_long_condition_that_might_make_you_want_to_split_lines ? something : something_else # good if some_really_long_condition_that_might_make_you_want_to_split_lines something else something_else end الشروط المتداخلة تجنب استخدام الشروط المتداخلة لتدفق السيطرة. يُفضّل استخدام شرط الحراسة (guard clause) عندما يمكنك تأكيد بيانات غير صالحة، فشرط الحراسة هو عبارة مشروطة في أعلى الدالة التي ترّجع في أسرع ما يمكن. المبادئ العامة هي: إرجاع الفوري بمجرد معرفة أن الدالة لا يمكنها أن تفعل أي شيء أكثر من الذي فعلته. تقليل التداخل ومسافة البادئة في الشيفرة البرمجة عن طريق الإرجاع في وقت مبكّر. وهذا يجعل الشيفرات برمجية أسهل للقراءة وتطلّب أقل جهد عقلي في الجزء القراءة عند تتبّع فروع أخرى. ينبغي أن تكون التدفقات الجوهرية أو الأكثر أهمية هي الأقل مسافات البادئة. # bad def compute server = find_server if server client = server.client if client request = client.make_request if request process_request(request) end end end end # good def compute server = find_server return unless server client = server.client return unless client request = client.make_request return unless request process_request(request) end يُفضّل استخدام next في الحلقات بدلا من الكتل الشرطيّة. # bad [0, 1, 2, 3].each do |item| if item > 1 puts item end end # good [0, 1, 2, 3].each do |item| next unless item > 1 puts item end الصياغة لا تستخدم for إلا إذا كنت تعرّف السبب بالضبط، ففي أغلب الأحيان، يجب استخدام التكرارات (iterator) بدلا منها، فتعمل for بنفس شروط each (أي أنك ستضيف مستوى من المراوغة) لكن مع عيب صغير، فلا تُعرّف for نطاق جديد (على عكس each) وستظهر المتغيرات المعرّفة في كتلتها خارجها. arr = [1, 2, 3] # bad for elem in arr do puts elem end # good arr.each { |elem| puts elem } يفضل استخدام {...} بدلا من do...end للكتل المتكونة من سطر واحد، وتجنّب استخدام {...} للكتل المتكونة من عدة أسطر (فسلسلة عدة أسطر هو شيء قبيح)، واستخدم do...end دائما للتحكم في التدفّق وتعريف الأساليب (على سبيل المثال في Rakefile وفي بعض DSL) وتجنب استخدام do...end عند السلّسلة. names = ["Bozhidar", "Steve", "Sarah"] # good names.each { |name| puts name } # bad names.each do |name| puts name end # good names.each do |name| puts name puts 'yay!' end # bad names.each { |name| puts name puts 'yay!' } # good names.select { |name| name.start_with?("S") }.map { |name| name.upcase } # bad names.select do |name| name.start_with?("S") end.map { |name| name.upcase } سيجادلنا البعض ويقول أن السلّسلة للأسطر المتعددة تبدوا جيّدة عند استخدام {...}، لكن هل سُئلوا أنفسهم هل أن هذه الشيفرة البرمجية قابلة للقراءة حقا وما إذا كان يمكن استخلاص محتوى الكتلة إلى أساليب فعّالة. استخدام عوامل التعيين المختصرة كلما كان ذلك ممكنا. # bad x = x + y x = x * y x = x**y x = x / y x = x || y x = x && y # good x += y x *= y x **= y x /= y x ||= y x &&= y تجنب الفواصل المنقوطة باستثناء للتعريفات صنف السطر الواحد، وعندما يكون من الملائم استخدام فاصلة منقوطة، ينبغي أن تكون متاخمة مباشرةً للبيان الذي تنهيه: لا يجب أن يكون هنالك مسافة قبل الفاصلة المنقوطة. # bad puts 'foobar'; # superfluous semicolon puts 'foo'; puts 'bar' # two expressions on the same line # good puts 'foobar' puts 'foo' puts 'bar' puts 'foo', 'bar' # this applies to puts in particular استخدم :: للثوابت المرجعيّة فقط(ويشمل هذا الأصناف والوحدات) والبنائين (مثل Array() أو Nokogiri::HTML()) ولا تستخدم :: لاستدعاء الأسلوب العادي. # bad SomeClass::some_method some_object::some_method # good SomeClass.some_method some_object.some_method SomeModule::SomeClass::SOME_CONST SomeModule::SomeClass() تجنب استخدام return عندما تكون غير مطلوبة. # bad def some_method(some_arr) return some_arr.size end # good def some_method(some_arr) some_arr.size end لا تستخدم القيمة المُرجّعة من = في الجمل الشرطيّة. # bad - shows intended use of assignment if (v = array.grep(/foo/)) ... end # bad if v = array.grep(/foo/) ... end # good v = array.grep(/foo/) if v ... end استخدم ||= كما تريد لتهيئة المتغيرات. # set name to Bozhidar, only if it's nil or false name ||= 'Bozhidar' لا تستخدم ||= لتهيئة المتغيرات المنطقيّة، (فكر في ما سيحدث إذا كانت القيمة الحالية تساوي false). # bad - would set enabled to true even if it was false enabled ||= true # good enabled = true if enabled.nil? استخدم .call الصريحة عند استدعاء lambda. # bad lambda.(x, y) # good lambda.call(x, y) تجنب استخدام متغيرات الخاصة على نمط بيرل (مثل $0-9، KaTeX parse error: Expected 'EOF', got '،' at position 1: ،̲ الخ)، فهي مشفّ…PROGRAM_NAME`. استخدم الاختصار &: عندما تأخذ كتلة الأسلوب معامل واحد فقط ويعمل الجسم على قراءة سمة أو استدعاء أسلوب دون معاملات. # bad bluths.map { |bluth| bluth.occupation } bluths.select { |bluth| bluth.blue_self? } # good bluths.map(&:occupation) bluths.select(&:blue_self?) يفضل استخدام some_method على self.some_method عند استدعاء دالة في المثيل الحالي. # bad def end_date self.start_date + self.nights end # good def end_date start_date + nights end في هذه الحالات الثلاثة، يجب عليك استخدام self. : عند تعريف أسلوب صنف: def self.some_method. عندما تكون self هي نموذج ActiveRecord ويكون الجانب الأيسر هو استدعاء أسلوب تعيين بما في ذلك تعيين سمة: self.guest = user. الإشارة إلى صنف مثيل الحالي: self.class. عند تعريف كائن من أي نوع قابل للتحويل وتريده أن يكون ثابت، تأكد من استدعاء freeze عليه، ومن الأمثلة الشائعة على ذلك هي السلاسل النصية، المصفوفات، وhash. سبب ذلك أن ثوابت روبي هي أنواع قابلة للتحويل، وستتأكد من عدم قابليتهم للتحويل عند استدعاء freeze عليهم لأنه سيصدر استثناء عند محاولة تعديلهم ، وبالنسبة إلى السلاسل النصية، سيسمح هذا بالتعامل معهم لنسخ روبي الأقدم من 2.2. # bad class Color RED = 'red' BLUE = 'blue' GREEN = 'green' ALL_COLORS = [ RED, BLUE, GREEN, ] COLOR_TO_RGB = { RED => 0xFF0000, BLUE => 0x0000FF, GREEN => 0x00FF00, } end # good class Color RED = 'red'.freeze BLUE = 'blue'.freeze GREEN = 'green'.freeze ALL_COLORS = [ RED, BLUE, GREEN, ].freeze COLOR_TO_RGB = { RED => 0xFF0000, BLUE => 0x0000FF, GREEN => 0x00FF00, }.freeze end التسمية استخدم snake_case للأساليب والمتغيرات. استخدم حالة الجمل (CamelCase) للأصناف والوحدات. (احتفظ بالاختصارات مثل HTTP وRFC وXML بحالة أحرف كبيرة). استخدم SCREAMING_SNAKE_CASE للثوابت الأخرى. يجب أن تنتهي الأساليب الإسنادية (التي ترجع قيمة منطقية) بعلامة استفهام. (على سبيل المثال Array#empty?). يجب أن تنتهي أسماء الأساليب التي يُحتمل أن تكون خطرة (مثل الأساليب التي تعدل self أو المعاملات، exit!، الخ) بعلامة تعجب، ويجب أن تتواجد الأساليب الخطرة (bang) إذا لم تتواجد الأساليب غير الخطرة (non-bang) - للمزيد من المعلومات -. سمّي متغيرات الرمي (throwaway) _. version = '3.2.1' major_version, minor_version, _ = version.split('.') الأصناف تجنب استخدام متغيرات (@@) بسبب سلوكهم السيئ في الميراث. class Parent @@class_var = 'parent' def self.print_class_var puts @@class_var end end class Child < Parent @@class_var = 'child' end Parent.print_class_var # => will print "child" كما ترى أنه جميع الأصناف في التسلسل الهرمي للصنف تتشارك في متغير صنف واحد، ويجب أن تُفضّل متغيرات مثيل الصنف على متغيرات الصنف. استخدم def self.method لتعريف الأساليب المنفردة، وسيجعل هذا الأساليب أكثر مقاومة لتغييرات إعادة الهيكلة. class TestClass # bad def TestClass.some_method ... end # good def self.some_other_method ... end • تجنب استخدام `class << self` إلا عند الضرورة، على سبيل المثال، موصول مفرد (`single accessors`) وسمات مستعارة (`aliased attributes`). class TestClass # bad class << self def first_method ... end def second_method_etc ... end end # good class << self attr_accessor :per_page alias_method :nwo, :find_by_name_with_owner end def self.first_method ... end def self.second_method_etc ... end end ضع مسافة البادئة للأساليب public، protected وprivate بقدر تعريف الأسلوب الذي ينتمون إليه، واترك سطر فارغ واحد أعلاهم وأسفلهم. class SomeClass def public_method # ... end private def private_method # ... end end الاستثناءات لا تستخدم الاستثناءات لتدفق التحكم. # bad begin n / d rescue ZeroDivisionError puts "Cannot divide by 0!" end # good if d.zero? puts "Cannot divide by 0!" else n / d end تجنب إنقاذ (rescue) صنف Exception. # bad begin # an exception occurs here rescue Exception # exception handling end # good begin # an exception occurs here rescue StandardError # exception handling end # acceptable begin # an exception occurs here rescue # exception handling end لا تحدد RuntimeError في نسخة المعاملين من raise، ومن الأفضل استخدام خطأ الأصناف الفرعية من أجل الوضوح وشرح خطأ بشكل أفضل. # bad raise RuntimeError, 'message' # better - RuntimeError is implicit here raise 'message' # best class MyExplicitError < RuntimeError; end raise MyExplicitError يُفضّل توفير صنف استثناء والرسالة كمعاملين إلى raise بدلا من مثيل استثناء. # bad raise SomeException.new('message') # Note that there is no way to do `raise SomeException.new('message'), backtrace`. # good raise SomeException, 'message' # Consistent with `raise SomeException, 'message', backtrace`. تجنب استخدام rescue في شكل المُعدّل. # bad read_file rescue handle_error($!) # good begin read_file rescue Errno:ENOENT => ex handle_error(ex) end المجموعات Collections يفضّل استخدام map على collect. يفضّل استخدام detect على find، فاستخدام find غامض بسبب أسلوب find الخاصة بـ ActiveRecord وسيُظهر detect على أنك تعمل مع مجموعة روبي وليس كائن AR. يُفضّل استخدام reduce على inject. يُفضّل استخدام size على length و count لأسباب تتعلّق بالأداء. يفّضل استخدام تدوين الإنشاء (creation notation) للمصفوفة الحرفيّة وللـ hash إلا إذا كنت بحاجة إلى تمرير معاملات إلى منشئيها. # bad arr = Array.new hash = Hash.new # good arr = [] hash = {} # good because constructor requires parameters x = Hash.new { |h, k| h[k] = {} } يُفضّل استخدام Array#join على Array#* لأسباب تتعلّق بالوضوح. # bad %w(one two three) * ', ' # => 'one, two, three' # good %w(one two three).join(', ') # => 'one, two, three' استخدم الرموز (symbols) بدلا من السلاسل النصية كمفاتيح للـ Hash. # bad hash = { 'one' => 1, 'two' => 2, 'three' => 3 } # good hash = { :one => 1, :two => 2, :three => 3 } على نحو مماثل، استخدم رموز واضحة بدلا من رموز السلاسل النصية عندما يكون ذلك ممكنًا. # bad :"symbol" # good :symbol استخدم Hash#key? بدلا من Hash#has_key? وHash#value? بدلا من Hash#has_value?.فوفقًا لماتز (Matz)، تعتبر الأشكال الأطول مُهملة. # bad hash.has_key?(:test) hash.has_value?(value) # good hash.key?(:test) hash.value?(value) استخدم hash متعدّد الأسطر لأنه يجعل التعليمات البرمجية أكثر قابليّة للقراءة واستخدم الفواصل الزائدة للتأكد من أن تغييرات المعامل لا تتسبب بأسطر diff الغريبة عندما لم يتغيّر المنطق خلاف ذلك. hash = { :protocol => 'https', :only_path => false, :controller => :users, :action => :set_password, :redirect => @redirect_url, :secret => @secret, } استخدم الفاصلة الزائدة في المصفوفة التي تمتد لأكثر من سطر واحد. # good array = [1, 2, 3] # good array = [ "car", "bear", "plane", "zoo", ] السلاسل النصية يُفضّل استخدام استيفاء السلسلة النصية (string interpolation) بدلا من سلّسلة السلسلة النصية: # bad email_with_name = user.name + ' <' + user.email + '>' # good email_with_name = "#{user.name} <#{user.email}>" وعلاوة على ذلك، لا تنسى استيفاء نمط روبي 1.9، لنفترض أنك تُنشئ مفاتيح ذاكرة التخزين المؤقت كالتالي: CACHE_KEY = '_store' cache.write(@user.id + CACHE_KEY) يُفضّل استخدام استيفاء السلسلة النصية (string interpolation) بدلا من سَلسلة السِلسلة النصية: CACHE_KEY = '%d_store' cache.write(CACHE_KEY % @user.id) تجنب استخدام String#+ عندما تحتاج إلى إنشاء قطع بيانات كبيرة، واستخدم بدلا من ذلك String#<<. تحور السَلْسلة مثيل السِلسلة النصية في مكانها وهي أسرع دائما من String#+، والذي ينشئ مجموعة من كائنات السلسلة النصية الجديدة. # good and also fast html = '' html << '<h1>Page title</h1>' paragraphs.each do |paragraph| html << "<p>#{paragraph}</p>" end استخدم \ في نهاية السطر بدلا من + أو << لسَلسلة السلاسل النصية متعددة الأسطر. # bad "Some string is really long and " + "spans multiple lines." "Some string is really long and " << "spans multiple lines." # good "Some string is really long and " \ "spans multiple lines." التعبيرات العادية تجنب استخدام $1-9 لأنه من الصعب متابعة ما يحتويه، واستخدم بدلا منه المجموعات المسماة. # bad /(regexp)/ =~ string ... process $1 # good /(?<meaningful_var>regexp)/ =~ string ... process meaningful_var كن حذرا مع ^ و $ لأنها تطابق بداية/نهاية السطر وليس نهايات السلسلة النصية، فإذا أردت مطابقة كامل السلسلة النصية فاستخدم: \A و\z. string = "some injection\nusername" string[/^username$/] # matches string[/\Ausername\z/] # don't match استخدم المُُعدّل x لتعبيرات (regexps) المعقدة، فهذا سيجعلها أكثر قابلية للقراءة ويمكنك إضافة بعض التعليقات المفيدة، فقط كن حذرا لأنه سيتجاهل الفراغات. regexp = %r{ start # some text \s # white space char (group) # first group (?:alt1|alt2) # some alternation end }x محارف النسبة المئوية يفضل استخدام الأقواس على الأقواس المجعدة، الأقواس المربّعة، أو الأنابيب (pipes) عند استخدم محددات محرف % للتناسق ولأن سلوك محارف % أقرب إلى استدعاء الأسلوب منه إلى البدائل. # bad %w[date locale] %w{date locale} %w|date locale| # good %w(date locale) استخدم %w بحريّة. STATES = %w(draft open closed) استخدم %() للسلاسل النصية المتكونة من سطر واحد والتي تتطلّب الاستيفاء (interpolation) وعلامات الاقتباس المزدوجة المضمّنة ، وبالنسبة للسلاسل النصية المتكونة من عدة أسطر، يُفضل استخدام heredocs. # bad - no interpolation needed %(<div class="text">Some text</div>) # should be '<div class="text">Some text</div>' # bad - no double-quotes %(This is #{quality} style) # should be "This is #{quality} style" # bad - multiple lines %(<div>\n<span class="big">#{exclamation}</span>\n</div>) # should be a heredoc. # good - requires interpolation, has quotes, single line %(<tr><td class="name">#{name}</td>) استخدم ٪ r فقط للتعبيرات العادية التي تتطابق مع أكثر من حرف /. # bad %r(\s+) # still bad %r(^/(.*)$) # should be /^\/(.*)$/ # good %r(^/blog/2011/(.*)$) تجنب استخدام %x إلا إذا أردت استدعاء أمر مع علامة الاقتباس الخلفية (`) - وهو أمر مستبعد إلى حد ما -. # bad date = %x(date) # good date = `date` echo = %x(echo `date`) ريلز عند الرجوع فورا بعد استدعاء render أو redirect_to، ضع return في السطر التالي وليس في نفس السطر. # bad render :text => 'Howdy' and return # good render :text => 'Howdy' return # still bad render :text => 'Howdy' and return if foo.present? # good if foo.present? render :text => 'Howdy' return end نطاقات عند تعريف نطاقات نموذج ActiveRecord، لِفْ العلاقة بـ lambda، وخلافًا لذلك ستفرض العلاقة اتصال قاعدة بيانات في وقت تحميل الصنف (مثيل بدء التشغيل). # bad scope :foo, where(:bar => 1) # good scope :foo, -> { where(:bar => 1) } كن منسجمًا إذا كنت تعدل شيفرة برمجية، فخذ بضعة دقائق لإلقاء نظرة على الشيفرة البرمجية الموجودة وحدد أسلوبها، فإذا كانوا يستخدمون مساحات فارغة حول جميع عواملها الرياضية، فقم بذلك أيضا، وإذا كانت تمتلك التعليقات صناديق صغيرة من علامات hash حولها، فاجعل تعليقاتك كذلك أيضا. الهدف من امتلاك مبادئ توجيهية للنمط هي الحصول على مفردات مشتركة من الشيفرات البرمجية حتى يتمكن الناس من فهم الذي تريد قوله بدلا من كيف تقوله، ونحن نعرض هنا قواعد النمط العالمية حتى يعرف الناس المفردات، لكن النمط المحلي مهم أيضا، فإذا كانت الشيفرة البرمجية التي تضيفها تختلف بشكل كبيرا عن الشيفرات البرمجية التي حولها فستجعل قراءة الشيفرة البرمجية صعبة لذلك تجنب ذلك. ترجمة لدليل Ruby Style Guide من Airbnb على GitHub
  5. تعرفنا في الدرس السابق على أهم الخدع، النصائح والتحذيرات وسنتابع في هذا الدرس مرجع الارتباط المفصّل. 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
  6. في الدرسين السابقين ألقينا نظرة عامة حول التحقيقات في 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.
  7. إن التهجير من أهم مزايا Active Record، حيث يسمح لك بتطوير قاعدة البيانات بسهولة مع مرور الزمن. فبدلًا من إعادة كتابة مُخططات قاعدة البيانات بسطور SQL من جديد، سيسمح لك التهجير باستعمال كود Ruby موحد المجال Domain – specific language لوصف التعديلات اللازمة التي ستقوم بها على جداولك. 1 نظرة عامة على التهجير التهجير هو الطريقة الملائمة لتبديل أجزاء و مُخططات قاعدة البيانات بمرور الزمن بطريقة سهلة و متناسقة. ونقوم بإستعمال لُغة Ruby موحدة المجال DSL بدلًا من كتابة العديد من سطور SQL مما يسمح للتغيرات بأن تكون مُستقلة تمامًا عن قاعدة البيانات و لا تؤثر عليها سلبًا. يُمكنك التفكير في التهجير على أنه عمل تحديث جديد لقاعدة البيانات. ففي بداية الإسكيما تكون فارغة، ثُم بعد ذلك تقوم عمليات التهجير بإضافة الجداول، العواميد، أو المدخلات. و Active Record يقوم بعمل هذه التحديثات على الإسكيما من الحالة التي كانت عليها قبل التحديث (أو في النُسخة السابقة). كما سيقوم Active Record بتحديث ملف db/schema.rb ليُناسب بناء قاعدة البيانات بعد التحديث. مثال على التهجير class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end في المثال السابق، قُمنا بعملية تهجير حيث أضفنا جدول جديد يُسمى products يحتوي على عامود نصي يُسمى name و عامود آخر يُسمى description. وسيتم إضافة عامود مفتاح أساسي يُسمى id ضمنيًا، لأنه –كما علمنا من قبل- المفتاح الرئيسي الإفتراضي لجميع نماذج Active Record. آما أمر timestamps يُضيف عامودين، صُنع في created_at وتم تحديثه في updated_at. و تلك العواميد الخاصة يتم إدارتها و تحديثها تلقائيًا بواسطة Active Record. لاحظ أن عملية التغير التي نقوم بها تتحرك أمامًا مع مرور الوقت، فقبل تشغيل التهجير run migration لن يوجد لدينا ذلك الجدول، و بمجرد تشغيل كود التهجير سنحصل على الجدول، و العكس صحيح! فإن Active Record يُمكنه عكس عملية التهجير بإستخدام أمر العودة Roll back و سيُحذف الجدول الجديد. بعض قواعد البيانات تدعم الصفقات Transactions* مع التغيرات التي تحدث للإسكيما. خاصية التهجير مُحاطة بتلك الصفقات Transactions. و لكن إذا كانت قاعدة البيانات لا تسمح بالصفقات، فإن التهجير سيقوم بإلغاء الأجزاء التي لم تطرأ عليها تلك التغيرات. class ChangeProductsPrice < ActiveRecord::Migration[5.0] def change reversible do |dir| change_table :products do |t| dir.up { t.change :price, :string } dir.down { t.change :price, :integer } end end end end و بدلًا من إستخدام أمر change ستستخدم أوامر up و down. class ChangeProductsPrice < ActiveRecord::Migration[5.0] def up change_table :products do |t| t.change :price, :string end end def down change_table :products do |t| t.change :price, :integer end end end 2 إنشاء التهجير 2.1 إنشاء تهجير مُستقل و مُنفرد بنفسه إن عمليات التهجير تكون مُخزنة داخل ملف يوجد في db/migrate، و يوجد ملف لكل عملية تهجير حدثت على فئة مُعينة class. إسم الملف يتخذ الصيغة الآتية YYYYMMDDHHMMSS_creat_products.rb و هي تحتوي على صيغة الوقت التي حدث فيه التهجير، ثم علامة “_” ثم إسم عملية التهجير. إن هذا الإسم يجب أن يُطابق إسم الفئة class التي لحق بها, فعلى سبيل المثال، إن كان إسم التهجير 20080906120000_creat_products.rb فيجب أن يُعرف فئة class إسمها CreatProducts. آما بالنسبة للوقت المُلحق بالإسم، فإن Rails تستعمله لترتيب التهجيرات و ترتيب كيفية عملها. لذلك إذا قُمت بنسخ تهجير ما من تطبيق آخر. فيجب أن تلتفت لهذا الأمر. و حساب الوقت بالضبط أي الدقيقة و الثانية لأمرٌ صعب. لذلك يوفر Active Record مولد لصُنع هذا الأمر. $ bin/rails generate migration AddPartNumberToProducts سيصنع الكود أعلاه تهجير فارغ، لكنه مُسمى بتسمية صحيحة. class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change end end إذا كان التهجير على هيئة AddXXXToYYY أو RemoveXXXfromYYY أي يحتوي على أوامر إضافة أو حذف و يحتوي أيضًا على عواميد تحتوي على الأسماء names و الأنواع types، فسيتم صُنع عواميد مُناسبة مثل add_column و remove_column. و الكود أدناه مثال على ذلك. $ bin/rails generate migration AddPartNumberToProducts part_number:string سينتج عنه: class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string end end و يُمكنك أيضًا عمل فهرسة للعواميد الجديدة. $ bin/rails generate migration AddPartNumberToProducts part_number:string:index سينتج عنه: class AddPartNumberToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_index :products, :part_number end end و بنفس الطريقة يُمكنك عمل تهجير ليزيل عامود بإستخدام هذا الكود. $ bin/rails generate migration RemovePartNumberFromProducts part_number:string سينتج عنه: class RemovePartNumberFromProducts < ActiveRecord::Migration[5.0] def change remove_column :products, :part_number, :string end end كما أنك لست مُقيد بنوع واحد من العواميد لإنتاجه، مثال على ذلك $ bin/rails generate migration AddDetailsToProducts part_number:string price:decimal سينتج عنه class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :part_number, :string add_column :products, :price, :decimal end end لاحظ هُنا تحديد نوع البيانات الذي قُمنا به أما إذا كان التهجير يتخذ صيغة CreateXXX و مُتبع بقائمة بأسماء و أنواع العواميد، فإن التهجير سيقوم بعمل جدول XXX يحتوي على تلك العواميد، مثال: $ bin/rails generate migration CreateProducts name:string part_number:string و هذا سينتج عنه: class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name t.string :part_number end end end وكالعادة، فإنه يُمكنك تعديل ما يتم توليده من قبل التهجير عن طريق الإضافة و الحذف. عند طريق التعديل على هذا الملف db/migrate/YYYMMDDHHMMSS_add_details_to_products.rb، على سبيل المثال: $ bin/rails generate migration AddUserRefToProducts user:references و هذا سينتج عنه: class AddUserRefToProducts < ActiveRecord::Migration[5.0] def change add_reference :products, :user, foreign_key: true end end هذا التهجير سوف يصنع عامود user_id مع فهرس مُناسب. و هُناك أيضًا العديد من الخيارات الأخرى لدالة add_reference سنتطرق إليها فيما بعد. هذا الكود سيولد جداول مُدمجة Join tables إذا إستعملت JoinTable كجزء من إسم التهجير، مثال: الجداول المُدمجة Join Tables هي تجميع لعدد معين من العواميد من جدول واحد أو أكثر من جدول. $ bin/rails g migration CreateJoinTableCustomerProduct customer product و هذا سوف ينتج التهجير الآتي: class CreateJoinTableCustomerProduct < ActiveRecord::Migration[5.0] def change create_join_table :customers, :products do |t| # t.index [:customer_id, :product_id] # t.index [:product_id, :customer_id] end end end 2.2 موالدات النماذج إن مولدات النماذج و الإسكافولد Model and Scaffold Generators يُمكنها صُنع التهجير المُناسب لإضافة نموذج جديد. حيث سيحتوي التهجير على التعليمات اللازمة لصناعة الجداول المُناسبة. فإذا حددت العواميد التي تريدها، سيتم إضافة سطور الكود اللازمة لإضافة تلك العواميد، مثال: $ bin/rails generate model Product name:string description:text و ذلك سوف يصنع هذا التهجير: class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end و بالطبع يُمكنك إضافة أي عدد تشاءه 2.3 تعدية المُعدلات يُمكنك كتابة كود لتعدي المُعدلات Modifiers لأنهم قد تُقيدك. فإذا قُمت بتشغيل هذا الكود: $ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:references{polymorphic} سوف يُنتج هذا التهجير: class AddDetailsToProducts < ActiveRecord::Migration[5.0] def change add_column :products, :price, :decimal, precision: 5, scale: 2 add_reference :products, :supplier, polymorphic: true end end سنتابع في الدروس القادمة بقية أجزاء دليل تعليم Active Record Migrations.. المصدر: توثيقات Ruby on Rails.
  8. أعترف بأنّي لا أمتلك خبرة كبيرة في مكتبة Webpacker الجديدة في إطار العمل Ruby on Rails، ولكنّي قرّرت الاعتماد على هذه المكتبة تمامًا والاستغناء كلّيًا عن مكتبة Sprockets للتعامل مع الأصول assets. وباعتباري أحد متّبعي مبدأ Convention Over Configuration فقد حاولت جاهدًا إيجاد الطريقة التي يمكن الاصطلاح عليها في تشييد تطبيق Webpacker. هذه المكتبة في أيامها الأولى لذا أظنّ أنّ فريق مطوري Rails لم يقوموا بهذا الأمر أيضًا، وأعتقد بأنّ مجتمع المطوّرين سيجد حلًّا لهذه المسألة قريبًا. على أي حال، إليك الطريقة التي اتبعتها في استبدال asset pipeline بـ Webpacker. إن كنت ترغب في العمل على مشروع جديد، فاستخدم الأمر: rails new blank --skip-sprockets --webpack ليتم إنشاء تطبيق Rails جديد مع الاستغناء عن مكتبة Sprockets وإضافة المكتبة Webpacker. أما لو كنت ترغب في إضافة Webpacker إلى مشروع قائم فعليك بمراجعة التوثيقات. بعد ذلك احذف بعض الجواهر gems والتي لم نعد بحاجة إليها من ملف Gemfile، وهي sass-rails، uglifier و coffee-rails. كذلك يمكنك التخلص من المجلد app/assets لأنّنا لم نعد بحاجة إليه بعد الآن. لنلق نظرة في البداية على محتويات ملف application.js الذي يتم إنشاؤه افتراضيًّا بواسطة Webpacker. /* eslint no-console:0 */ // This file is automatically compiled by Webpack, along with any other files // present in this directory. You're encouraged to place your actual application logic in // a relevant structure within app/javascript and only use these pack files to reference // that code so it'll be compiled. // // To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate // layout file, like app/views/layouts/application.html.erb console.log('Hello World from Webpacker') تشير التعليقات الواردة في هذا الملف إلى أن المجلد app/javascript/packs هو نقطة الولوج entry point إلى الحزم المستخدمة في التطبيق، وأنّ عليك وضع التطبيق الحقيقي في المجلد app/javascript. ترتيب الملفّات والمجلّدات لقد نظّمت تطبيق Webpacker الخاصّ بي كما هو موضح أدناه، والتطبيق يحمل اسم blog. سترى أنّي قد أدرجت تطبيق JavaScript الحقيقي في المجلد app/javascript/blog بدلًا من app/javascript ولم أقم بذلك اعتباطًا. أولًا: يعني هذا أن بمقدوري إضافة العديد من التطبيقات إلى المشروع الواحد وحسب الحاجة، بدلًا من تكديس جميع الشيفرات جنبًا إلى جنب. ثانيًا: ستتيح لي هذه الطريقة امتلاك نقطة ولوج حقيقية للحزم وهذا ما سأوضّحه الآن. blog +-- app | +-- javascript | | +-- blog | | | +-- fonts | | | +-- images | | | +-- styles | | | +-- index.js | | +-- packs | | | +-- application.js لنلق نظرة الآن إلى ملف app/javascript/packs/application.js وهو نقطة الولوج إلى حزمتي، وهو ملفّ بسيط للغاية: import 'blog'; سيتم استيراد التطبيق وتشغيل الملف app/javascript/blog/index.js والذي سيصبح نقطة الولوج إلى تطبيق JavaScript الخاصّ بي. بهذه الطريقة أحافظ على نقطة الولوج بسيطة قدر الإمكان أما ما تبقى من الشيفرة فيكون ضمن التطبيق. جدير بالذكر كذلك أنّك لست ملزمًا بتسمية المجلد - والملفّ - باسم blog، بل يمكنك استخدام أي اسم تشاء، ولكنّني توخيت تبسيط الأمور بجعل اسم المجلد مطابقًا لاسم تطبيق Rails. والآن سنستخدم javascript_pack_tag للإشارة إلى تطبيقنا. javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' مرحلة التطوير عند العمل في بيئة التطوير Development استخدم الأمر bin/webpack-dev-server وستتمّ مراقبة التطبيق وإعادة بنائه عند الحاجة، وإرسال التعديلات إلى المتصفّح. وبعد أن يصبح التطبيق جاهزًا للتجميع Compile يمكن استدعاء الأمر bin/webpack أو rails assets:precompile، ولكن سيتولى الخادوم هذه المهمّة على الأرجح. مكتبتا Turbolinks وRails UJS إن كنت ستستخدم Turbolinks و Rails UJS في تطبيقك فعليك إعداد هاتين المكتبتين وتشغيلهما. من السهل استدعاء المكتبتين بواسطة الأمر //= require في حال كنت تستخدم asset pipline ولكن عند استخدام هاتين المكتبتين كوحدات فالأمر مختلف قليلًا. في البداية علينا تثبيت المكتبتين: yarn add rails-ujs turbolinks بعد ذلك علينا استيراد المكتبتين وتشغيلهما في ملف app/javascript/blog/index.js: import Rails from 'rails-ujs'; import Turbolinks from 'turbolinks'; Rails.start(); Turbolinks.start(); كما تلاحظ فقد اتبعنا نفس الأسلوب في استدعاء كلتا المكتبتين وتشغيلهما. متغيرات البيئة Environment Variables يمكن الوصول إلى متغيرات البيئة عبر الشيفرات الخاصة بنا بعد تجميعها. عادة ما أضيف اللاحقة .erb إلى اسم الملف ثم أنفّذ شيئًا مماثلًا لهذا: <%= ENV['X_ENV_VAR'] %> ولكن هناك طريقة أفضل، إذ يمكن تهجير المتغيرات إلى process.env وكما يلي: export const STRIPE_API_KEY = process.env.STRIPE_API_KEY; أوراق الأنماط Stylesheets يمكن وبكل بساطة استيراد ملفات CSS أو Sass التي ترغب باستخدامها في التطبيق. لقد حدّدت الملف app/javascript/blog/styles/app.scss كنقطة ولوج Sass وبهذا أبقي جميع الملفات في مجلد styles، وتصبح عملية استيرادها إلى التطبيق أمرًا سهلًا للغاية: import './styles/app.scss'; يمكن استخدام المحرّف ~ مع import وستبدأ عملية البحث عن الملف من المجلد node_modules، فلو أردت مثلًا استيراد مكتبة Bootstrap إلى التطبيق يمكنك استخدام الشيفرة التالية: @import '~bootstrap/scss/bootstrap'; الصور لا تختلف الصور عن أوراق الأنماط في شيء، إذ يجب استيرادها في البداية لتتمكن من استخدامها في التطبيق. عادة ما أضع الصور في مجلد app/javascript/blog/images ثم أنشئ ملفًّا باسم index.js في نفس المجلد وظيفته استيراد جميع الصور في المجلد. فعلى سبيل المثال: import './logo.svg'; import './menu-open.svg'; import './menu-close.svg'; عليك الانتباه إلى أنّ هذه الطريقة لن تُضمّن الصور في أوراق الأنماط، وإنما تدفعها إلى Webpacker لتكون متاحة للاستخدام في التطبيق. ويمكنك حينئذٍ استخدام الدالة المساعدة asset_pack_path في العرض للإشارة إلى هذه الملفات. فلو أردت مثلًا استخدام إحدى هذه الصور: = image_tag asset_pack_path('logo.svg') إضافة إلى ذلك يمكنك الإشارة إلى الصور في CSS أو Sass وسيتلقّفها Webpacker بصورة تلقائية. وبصورة عامة يكون مسار الجذر نقطة الولوج الخاصّة بأوراق الأنماط لذا لن تكون بحاجة إلى استخدام المسارات المطلقة. ختامًا كما شاهدت فإن الأمر يتطلب الكثير من العمل، وهذا هو الأسلوب الذي أتبعه في استخدام Webpacker الآن. حاولت البحث عن مقالات تعنى بتفصيل طريقة استخدام Webpacker ولكنّي لم أجد شيئًا يذكر في الوقت الحاضر. أنا متحمّس جدًّا لمعرفة طريقة الاستخدام القياسية لهذه المكتبة هذا في حال تمّ تحديدها في المستقبل. ترجمة - وبتصرّف - للمقال Replacing Rails Asset Pipeline with Webpacker لصاحبه Dwight Conrad Watson.
  9. هذا هو الجزء الأخير من سلسلة “مدخل إلى إطار العمل Ruby on Rails” وفي هذا الجزء سنعيد هيكلة الشيفرة التي كتبناها في الأجزاء السابقة من السلسلة، وسنتعرّف إلى نظام الاستيثاق البسيط الذي يقدّمه إطار العمل Rails. إعادة هيكلة الشيفرة بعد أن أصبحت المقالات والتعليقات تعمل بصورة جيدة، لنلقِ نظرة على القالب app/views/articles/show.html.erb . يبدو الملف طويلًا جدًّا، لذا سنستخدم الملفات الجزئية لتنظيف وترتيب الشيفرة البرمجية. تصيير مجموعة الملفات الجزئية في البداية سننشئ ملفًّا جزئيًا خاصًّا بالتعليقات وظيفته عرض جميع التعليقات الخاصّة بالمقالة. أنشئ الملف app/views/comments/_comment.html.erb وأضف إليه الشيفرة التالية: <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> والآن يمكنك تعديل الملف `app/views/articles/show.html.erb` كما يلي: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <%= render @article.comments %> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> بهذه الطريقة سيتم تصيير الملف الجزئي في app/views/comments/_comment.html.erb لكلّ تعليق موجود في مجموعة @article.comments، وعندما يتنقّل التابع render بين عناصر مجموعة التعليقات فإنه يُسند كل تعليق إلى متغيّر محلي local variable يحمل اسم الملف الجزئي ذاته، وفي حالتنا هذه comment والذي يكون متوفّرًا في الملف الجزئي. تصيير الملف الجزئي الخاصّ بالاستمارة لنقم بإزالة قسم التعليقات الجديد إلى ملف جزئي خاصّ به، ومرة أخرى أنشئ ملفًّا باسم _form.html.erb في المجلد app/views/comments/ وأضف إليه ما يلي: <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> ثم عدّل الملف app/views/articles/show.html.erb ليصبح بالصورة التالية: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <%= render @article.comments %> <h2>Add a comment:</h2> <%= render 'comments/form' %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> يعرّف تابع render الثاني القالب الجزئي الذي نرغب في تصييره وهو comments/form، ونظرًا لوجود المحرّف / ضمن هذه السلسلة النصّية سيعرف Rails بأنّك ترغب في تصيير الملف _form.html.erb الموجود في المجلد app/views/comments. أما الكائن @article فسيكون متوفّرًا لأيّ ملفّ جزئي يتم تصييره في العرض لأنّنا عرّفناه كمتغيّر من نوع instance. حذف التعليقات إن القدرة على حذف التعليقات المزعجة هي من الميزات المطلوب توفرها في المدوّنة، ولتنفيذ ذلك سنحتاج إلى إضافة رابط لحذف التعليقات ضمن العرض وإلى حدث destroy في المتحكّم CommentsController. لذا سنضيف أوّلًا رابط الحذف ضمن الملفّ الجزئي app/views/comments/_comment.html.erb وكما يلي: <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> <p> <%= link_to 'Destroy Comment', [comment.article, comment], method: :delete, data: { confirm: 'Are you sure?' } %> </p> سيؤدّي النقر على هذا الرابط إلى إرسال الفعل DELETE متمثّلًا بالرابط /articles/:article_id/comments/:id إلى المتحكّم CommentsController، والذي سيبحث بدوره - مستعينًا بهذا الرابط - عن التعليق المراد حذفه من قاعدة البيانات. لنضِف حدث destroy إلى المتحكّم في الملف app/controllers/comments_controller.rb: class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) redirect_to article_path(@article) end def destroy @article = Article.find(params[:article_id]) @comment = @article.comments.find(params[:id]) @comment.destroy redirect_to article_path(@article) end private def comment_params params.require(:comment).permit(:commenter, :body) end end سيبحث الحدث destroy عن التعليق المراد حذفه، ثم يعيّن موقعه في مجموعة @article.comments ثم يحذفه من قاعدة البيانات ويعيد توجيهنا إلى حدث show الخاصّ بالمقالة. حذف الكائنات المترابطة من البديهي أنّه عند حذف مقالة معيّنة فإن من الواجب أن يتم حذف التعليقات المرتبطة بها، وإلا فستشغل هذه التعليقات مساحة ضمن قاعدة البيانات دون أيّ فائدة. يتيح لنا Rails استخدام الخيار dependent لتحقيق ذلك. توجّه إلى نموذج Article (app/models/article.rb) وعدّله بالصورة التالية: class Article < ApplicationRecord has_many :comments, dependent: :destroy validates :title, presence: true, length: { minimum: 5 } end الاستيثاق Authentication في Rails إن كنت ترغب في نشر المدوّنة على الإنترنت، سيكون بإمكان أي شخص إضافة وتعديل وحذف المقالات والتعليقات فيها. يقدّم Rails نظام استيثاق HTTP بسيط يمكن استخدامه في التطبيقات البسيطة كتطبيقنا هذا. سنحتاج في المتحكّم ArticlesController إلى وسيلة لمنع وصول الشخص غير المستوثق منه إلى الأحداث التي يتضمّنها هذا المتحكّم، ويمكن استخدام تابع http_basic_authenticate_with لتحقيق ذلك. ولاستخدام نظام الاستثياق سنقوم بالإفصاح عنه في بداية ملف المتحكّم ArticlesController in app/controllers/articles_controller.rb وسنستوثق من جميع الأحداث المتوفّرة في هذا المتحكّم عدا حدثي index وshow: class ArticlesController < ApplicationController http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show] def index @articles = Article.all end # بقيّة الشيفرة ... كذلك سنسمح للمستخدمين المستوثق منهم فقط بحذف التعليقات، لذا أضف الشيفرة التالية إلى المتحكّم CommentsController في الملف app/controllers/comments_controller.rb: class CommentsController < ApplicationController http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy def create @article = Article.find(params[:article_id]) # ... end # بقيّة الشيفرة ... والآن إن حاولت إنشاء مقالة جديدة، ستتلقّى طلب استيثاق كهذا: جدير بالذكر أنّ هناك العديد من وسائل الاستيثاق في تطبيقات Rails، أشهرها Devise rails engine و Authlogic. إطار العمل Rails ونظام الترميز UTF-8 إن أسهل طريقة للعمل مع Rails هي تخزين جميع البيانات الخارجية بنظام الترميز UTF-8، وإن لم تفعل ذلك فغالبًا ما تقوم مكتبات Ruby وإطار العمل Rails بتحويل البيانات الأصلية إلى هذا الترميز، ولكن لا يمكن الاعتماد على هذه المكتبات بصورة تامّة، ويفضّل التأكد من أنّ جميع البيانات الخارجية مرمّزة بهذا النظام. وفي حال حدوث أي خطأ في نظام الترميز فإن الحروف ستظهر في المتصفّح غالبًا على هيئة أشكال معينية سوداء بداخلها علامة استفهام، أو قد تظهر الحروف على هيئة رموز غريبة كأن يظهر الرمز “ü” بدلاً من الحرف “ü”. يتّخذ Rails بعض الإجراءات في نظامه الداخلي للتقليل من المسبّبات الشائعة لهذه المشاكل والتي يمكن الكشف عنها وتصحيحها بصورة تلقائية. ولكن، إن كنت تتعامل مع بيانات من مصادر خارجية غير مخزّنة بترميز UTF-8، لن يكون Rails قادرًا على الكشف بصورة تلقائية عن أسباب المشكلة أو تقديم حلّ لها. وهناك مصدران شائعان للبيانات غير المخزّنة بترميز UTF-8: محرر النصوص: تحفظ معظم محرّرات النصوص الملفات البرمجية بصيغة UTF-8، وإن لم يقم محرّر النصوص الذي تستخدمه في كتابة الشيفرات البرمجية بذلك، فقد ينتج عن ذلك تحوّل الحروف الخاصّة أو حروف اللغات الأخرى غير الإنكليزية إلى التحول في المتصفّح إلى أشكال معينية بداخلها علامة استفهام. ينطبق هذا الأمر كذلك على ملفات الترجمة i18n. تجدر الإشارة إلى أنّه تتيح معظم محررات النصوص التي لا تحفظ الملفات البرمجية بهذا الترميز افتراضيًّا (مثل Dreamweaver) إمكانية تغيير الترميز الافتراضي للملفات المحفوظة إلى نظام UTF-8، وننصح بالقيام بذلك. قاعدة البيانات: يحوّل Rails البيانات القادمة من قاعدة البيانات إلى ترميز UTF-8، ولكن إن لم يكن هذا نظام الترميز هذا مستخدمًا من طرف قاعدة البيانات فلن يكون بالإمكان تخزين جميع المحارف المدخلة من قبل المستخدم. فعلى سبيل المثال، إن كان نظام الترميز الداخلي لقاعدة البيانات هو Latin-1 وأدخل المستخدم كلمات باللغة الروسية أو العربية أو اليابانية، فستخسر البيانات إلى الأبد بمجرد دخولها إلى قاعدة البيانات. لذا ينصح دائمًا بتحويل نظام الترميز الداخلي في قاعدة البيانات إلى نظام UTF-8. المصدر: توثيقات Ruby on Rails.
  10. تحدّثنا في الجزء السابق من هذه السلسلة عن النماذج في إطار العمل Ruby on Rails وتعرّفنا على طريقة إنشائها والتعامل معها من خلال كتابة الشيفرة المسؤولة عن حفظ المقالة الجديدة في قاعدة البيانات. في الجزء الثاني من هذا الموضوع سنتعلّم كيفية ربط نموذجين مع بعضهما البعض من خلال إنشاء نموذج جديد خاصّ بالتعليقات. ولكن قبل ذلك سنكمل ما بدأناه في الدروس السابقة من السلسلة في بناء عمليات “CRUD” حيث غطّينا سابقًا عمليتي الإنشاء Create و القراءة Read، وسنغطي اليوم العمليتين المتبقّيتين وهما التحديث Update والحذف Destroy. تحديث المقالات الخطوة الأولى في عملية تحديث المقالات هي إضافة حدث edit إلى المتحكم ArticlesController بين حدثي new و create وكما يلي: def new @article = Article.new end def edit @article = Article.find(params[:id]) end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end سيتضمن العرض استمارة مشابهة لتلك التي استخدمناها في إنشاء المقالات الجديدة. أنشئ ملفًّا باسم app/views/articles/edit.html.erb وأضف إليه الشيفرة التالية: <h1>Edit article</h1> <%= form_for(@article) do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Back', articles_path %> سنوجّه الاستمارة هذه المرة إلى حدث update والذي لم نقم بتعريفه حتى الآن. يؤدي تمرير كائن المقالة للتابع إلى إنشاء عنوان url لإرسال استمارة المقالة التي تم تعديلها، ومن خلال هذا الخيار نخبر Rails بأنّنا نرغب في أن يتم إرسال هذا النموذج من خلال فعل HTTP PATCH وهو أحد أفعال HTTP التي تستخدم في تحديث الموارد حسب بروتوكول REST. يمكن أن يكون المعامل الأول لـ form_for كائنًا، مثلًا @articl، والذي سيؤدي بالدالة المساعدة إلى ملء الاستمارة بالحقول التابعة للكائن، ويؤدي تمرير الرمز (:article) بنفس اسم المتغيّر من نوع instance (@article) إلى نفس النتيجة تلقائيًا. والآن سنقوم بإنشاء الحدث update في المتحكّم app/controllers/articles_controller.rb وسنضيفه بين حدث create والتابع ذي المحدّد الخاصّ private: def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end def update @article = Article.find(params[:id]) if @article.update(article_params) redirect_to @article else render 'edit' end end private def article_params params.require(:article).permit(:title, :text) end يستخدم الحدث update عندما ترغب في تحديث سجل موجود في قاعدة البيانات، ويستقبل هذا الحدث جدول تقطيع hash يحتوي الخصائص التي ترغب في تحديثها. وكما سبق، في حال وجود خطأ في عملية التحديث سنعرض الاستمارة على المستخدم من جديد. سنستخدم التابع article_params الذي عرّفناه في وقت سابق للحدث create. لا حاجة لتمرير جميع الخصائص لغرض تحديثها، فعلى سبيل المثال، إن تم استدعاء @article.update(title: 'A new title') فسيقوم Rails بتحديث خاصية العنوان فقط، ويترك باقي الخصائص دون تعديل. أخيرًا، نرغب في عرض رابط إلى الحدث edit في الصفحة التي نعرض فيها قائمة المقالات، لذا توجّه إلى الملف app/views/articles/index.html.erb وأضف الرابط ليظهر إلى جانب رابط “Show”: <table> <tr> <th>Title</th> <th>Text</th> <th colspan="2"></th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> <td><%= link_to 'Edit', edit_article_path(article) %></td> </tr> <% end %> </table> سنضيف كذلك رابطًا إلى قالب app/views/articles/show.html.erb ليظهر رابط “Edit” في صفحة المقالة أيضًا: ... <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> هذا هو شكل تطبيقنا حتى هذه اللحظة: استخدام الملفات الجزئية partials لإزالة التكرار من العروض تبدو صفحة تحرير المقالة مشابهة تمامًا لصفحة إنشاء مقالة جديدة، وفي الواقع تستخدم الصفحتان الشيفرة ذاتها لعرض الاستمارة. سنقوم الآن بالتخلص من هذا التكرار باستخدام ملفات العرض الجزئية. تحمل هذه الملفات أسماء تبدأ بالمحرف (_). أنشئ ملفًّا جديدًا باسم _form.html.erb ضمن المسار app/views/articles/ وأضف إليه الشيفرة التالية: <%= form_for @article do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> لاحظ أننا لم نغيّر شيئًا باستثناء الإعلان عن التابع form_for وسبب استخدام هذه الأسلوب المختصر والبسيط في الإعلان عن التابع form_for للتعبير عن الاستمارتين هو أن @article عبارة عن مورد يرتبط بمجموعةٍ من مسارات RESTful، وبإمكان Rails أن يخمّن عنوان URI والتابع الذي يجب استخدامه. والآن لنقم بتحديث العرض app/views/articles/new.html.erb لاستخدام الملف الجزئي الذي أنشأناه وسنقوم بإعادة كتابة العرض من جديد وكما يلي: <h1>New article</h1> <%= render 'form' %> <%= link_to 'Back', articles_path %> ثم قم بالأمر عينه في ملف العرض app/views/articles/edit.html.erb: <h1>Edit article</h1> <%= render 'form' %> <%= link_to 'Back', articles_path %> حذف المقالات هذه هي العملية الأخيرة ضمن عمليات CRUD، وبحسب معايير REST فإن المسار الذي يؤدي إلى حذف المقالات وكما يظهر في مخرجات الأمر bin/rails routes هو: DELETE /articles/:id(.:format) articles#destroy يجب استخدام الفعل DELETE في المسار المسؤول عن حذف الموارد، أما في حال استخدام الفعل GET فسيكون بالإمكان إنشاء رابط خبيث كهذا الرابط مثلًا: <a href='http://example.com/articles/1/destroy'>look at this cat!</a> سنستخدم التابع delete لحذف المصادر، وهذا المسار مرتبط بالحدث destroy ضمن المتحكّم app/controllers/articles_controller.rb والذي لم نقم بإنشائه بعد. عادة ما يكون التابع destroy التابع الأخير ضمن المتحكّم، وكما هو الحال مع بقية التوابع العامّة public يجب الإعلان عنه قبل أي توابع خاصّة أو محميّة protected. def destroy @article = Article.find(params[:id]) @article.destroy redirect_to articles_path end الصورة النهائية للمتحكّم ArticleController في الملف app/controllers/articles_controller.rb هي: class ArticlesController < ApplicationController def index @articles = Article.all end def show @article = Article.find(params[:id]) end def new @article = Article.new end def edit @article = Article.find(params[:id]) end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end def update @article = Article.find(params[:id]) if @article.update(article_params) redirect_to @article else render 'edit' end end def destroy @article = Article.find(params[:id]) @article.destroy redirect_to articles_path end private def article_params params.require(:article).permit(:title, :text) end end يمكن استدعاء التابع destroy في كائنات التسجيلة النشطة Active Record عندما ترغب في حذفها من قاعدة البيانات. لاحظ أنّنا لسنا بحاجة لإضافة عرض خاص بهذا الحدث لأنّنا نعيد توجيه المستخدم إلى الحدث index. أخيرًا أضف رابط ‘Destroy’ إلى القالب app/views/articles/index.html.erb لنربط كل الصفحات مع بعضها البعض. <h1>Listing Articles</h1> <%= link_to 'New article', new_article_path %> <table> <tr> <th>Title</th> <th>Text</th> <th colspan="3"></th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> <td><%= link_to 'Edit', edit_article_path(article) %></td> <td><%= link_to 'Destroy', article_path(article), method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </table> استخدمنا هنا التابع link_to بطريقة مختلفة، حيث مررّنا اسم المسار كمعامل ثانٍ، ثمّ مرّرنا الخيارات الأخرى بعد ذلك. تستخدم الخيارات method: :delete وdata: { confirm: 'Are you sure?' } كخصائص HTML5 بحيث يؤدي الضغط على الرابط إلى عرض مربع حوار للتأكد من رغبة المستخدم في حذف المقالة، ثم إرسال الرابط باستخدام التابع delete. تتمّ تعملية التحقّق هذه بواسطة ملف JavaScript الذي يحمل الاسم rails-ujs والموجود بصورة افتراضية في مخطط التطبيق (app/views/layouts/application.html.erb)، وفي حال عدم وجود هذا الملف لن يظهر مربع الحوار التأكيدي للمستخدم. تهانينا أصبح بإمكانك الآن إنشاء وعرض وسرد وتحديث وحذف المقالات في مدوّنتك. إضافة النموذج الخاصّ بالتعليقات سنقوم الآن بإنشاء نموذج جديد في تطبيقنا هذا ستكون وظيفته التعامل مع التعليقات. إنشاء النموذج لإنشاء النموذج الخاص بالتعليقات سنتبع الأسلوب السابق نفسه وذلك باستخدام أداة المولّد لإنشاء نموذج يحمل الاسم Comment ويمثّل مرجعًا إلى المقالة. اكتب الأمر التالي في سطر الأوامر: $ bin/rails generate model Comment commenter:string body:text article:references سينشئ هذا الأمر أربعة ملفات: الملف Purpose الملف/المجلد الوظيفة db/migrate/20140120201010_create_comments.rb ملف التهجير المسؤول عن إنشاء جدول التعليقات في قاعدة البيانات (سيحمل اسم الملف لديك ختمًا زمنيًا مختلفًا) app/models/comment.rb النموذج الخاص بالتعليقات test/models/comment_test.rb ملف الاختبارات الخاص بنموذج التعليقات test/fixtures/comments.yml نماذج تعليقات تستخدم في إجراء الاختبارات لنلق نظرة في البداية على ملف app/models/comment.rb: class Comment < ApplicationRecord belongs_to :article end كما تلاحظ فمحتوى هذا الملف مشابه لنموذج Article الذي أنشأناه سابقًا، والفارق الوحيد هو السطر belongs_to :article والذي ينشئ رابطًا بين النموذجين، وسنتحدّث عن الروابط بعد قليل. أما الكلمة المفتاحية (:references) ضمن الأمر الذي قمنا بتنفيذه في سطر الأوامر، فهي نوع خاص من البيانات بالنسبة للنماذج. تنشئ هذه الكلمة المفتاحية عمودًا في الجدول الموجود في قاعدة البيانات يحمل اسم النموذج الذي تمّ تمريره إلى هذه الكلمة مع إضافة _id والذي يمثّل عددًا صحيحًا. ستتضح الأمور أكثر بالنسبة إليك إن تفحّصت ملف db/schema.rb أدناه. قام Rails - بالإضافة إلى إنشاء النموذج - بإنشاء تهجير وظيفته إنشاء الجدول المقابل للنموذج في قاعدة البيانات: class CreateComments < ActiveRecord::Migration[5.0] def change create_table :comments do |t| t.string :commenter t.text :body t.references :article, foreign_key: true t.timestamps end end end يُنشئ السطر t.references عمودًا من نوع integer باسم article_id إضافة إلى فهرس index خاص بهذا العمود وقيد مفتاح خارجي Foreign Key Constraint والذي يشير إلى عمود id في جدول المقالات. والآن نفذ التهجير باستخدام الأمر التالي: $ bin/rails db:migrate ينفّذ Rails التهجيرات غير المنفّذة فقط؛ لذا ستكون نتيجة الأمر التالي كما يلي: == CreateComments: migrating ================================================= -- create_table(:comments) -> 0.0115s == CreateComments: migrated (0.0119s) ======================================== ربط النماذج مع بعضها البعض تسهّل روابط التسجيلة النشطة تكوين العلاقات بين النماذج، وفي حالتنا هذه سنُنشئ علاقة بين جدولي التعليقات والمقالات، ولو فكّرنا في طبيعة العلاقة التي تربط بينهما فسنجد أنه: ينتمي كل تعليق إلى مقالة واحدة. تمتلك المقالة الواحدة العديد من التعليقات. يستخدم Rails صياغة مشابهة للربط بين النماذج، وقد شاهدنا في نموذج Comment في الملف app/models/comment.rb الشيفرة المسؤولة عن ربط كل تعليق بمقالة واحدة: class Comment < ApplicationRecord belongs_to :article end سنحتاج الآن إلى تكوين الجانب الثاني من الرابطة، أي ربط المقالات بالتعليقات، لذا توجّه إلى الملف app/models/article.rb وعدّله بالصورة التالية: class Article < ApplicationRecord has_many :comments validates :title, presence: true, length: { minimum: 5 } end والآن أصبح النموذجان مرتبطين مع بعضهما البعض تلقائيًا، فعلى سبيل المثال، في حال كان لدينا متغيّر @article والذي يمثّل مقالة معيّنة، يمكن استدعاء جميع التعليقات المرتبطة بتلك المقالة على هيئة مصفوفة وذلك من خلال @article.comments. إضافة مسار خاص بالتعليقات كما هو الحال مع متحكم welcome سنحتاج إلى إضافة مسار نحدّد من خلاله العنوان الذي نرغب في استخدامه لمشاهدة التعليقات؛ لذا افتح ملف config/routes.rb مرة أخرى، وعدّله كما يلي: resources :articles do resources :comments end بهذه الطريقة تصبح التعليقات بمثابة موارد مضمّنة في المقالات، وهذه الطريقة هي جزء من العلاقة الهرمية التي تنشأ بين المقالات والتعليقات. إنشاء المتحكّم الخاصّ بالتعليقات بعد أن انتهينا من إعداد النموذج، أصبح بإمكاننا الآن إنشاء المتحكّم الخاص بالتعليقات، وسنستخدم أداة المولّد كما فعلنا سابقًا: $ bin/rails generate controller Comments سينشئ هذا الأمر خمسة ملفات إضافة إلى مجلّد فارغ: الملف/المجلد الوظيفة app/controllers/comments_controller.rb المتحكّم الخاص بالتعليقات /app/views/comments يتم تخزين العروض الخاصّة بالتعليقات في هذا المجلد test/controllers/comments_controller_test.rb ملف الاختبار الخاصّ بالمتحكّم app/helpers/comments الملف الخاصّ بمساعد العرض app/assets/javascripts/comments.coffee ملف CoffeScript الخاصّ بالمتحكّم app/assets/stylesheets/comments.scss أوراق الأنماط المتتالية CSS الخاصّة بالمتحكّم كما هو الحال مع أي مدوّنة، فإن القرّاء سيكتبون تعليقاتهم بعد قراءة المقالة مباشرة، وبعد أن يرسلوا تعليقاتهم يتم توجيههم إلى صفحة عرض المقالة ليتمكّنوا من مشاهدة التعليقات. وستكون وظيفة المتحكّم CommentsController هي توفير التوابع اللازمة لإنشاء التعليقات وحذف التعليقات المزعجة حال وصولها. سنقوم أولًا بتعديل قالب عرض المقالات app/views/articles/show.html.erb لنتمكن من إضافة تعليق جديد: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> ستضيف الشيفرة السابقة استمارة إلى صفحة عرض المقالات يمكن من خلالها إضافة تعليق جديد من خلال استدعاء الحدث create ضمن المتحكّم CommentsController. ويستخدم الاستدعاء form_for مصفوفة ستعمل على إنشاء مسار متداخل nested route مثل: /articles/1/comments. لنجرِ الآن التعديلات اللازمة على الحدث create في الملفّ app/controllers/comments_controller.rb: class CommentsController < ApplicationController def create @article = Article.find(params[:article_id]) @comment = @article.comments.create(comment_params) redirect_to article_path(@article) end private def comment_params params.require(:comment).permit(:commenter, :body) end end ستتعقّد الأمور هنا قليلًا وذلك بسبب التداخل nesting الحاصل بين المسارات، إذ في كل مرة يتمّ فيها طلب تعليق معيّن يجب أن يتابع ذلك الطلب المقالة التي يرتبط بها هذا التعليق، وبالتالي استدعاء التابع find في نموذج Article والمسؤول عن اختيار المقالة المطلوبة حسب المعرّف المحدّد في المسار. بالإضافة إلى ذلك، استفدنا من بعض التوابع التي تقدّمها عملية الربط بين النموذجين، فقد استخدمنا التابع create على @article.comments لإنشاء التعليق وحفظه، وسيؤدي هذا إلى ربط التعليق الجديد بالمقالة المحدّدة. وبعد إنشاء التعليق الجديد نعيد توجيه المستخدم إلى المقالة الأصلية باستخدام الدالة المساعد article_path(@article). وكما شاهدنا تستدعي هذه الدالة الحدث show ضمن المتحكّم ArticlesController والذي يعمل بدوره على تصيير القالب show.html.erb، وهو المكان الذي نرغب أن تظهر التعليقات فيه؛ لذا سنقوم بإجراء التعديلات اللازمة على الملف app/views/articles/show.html.erb. <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <h2>Comments</h2> <% @article.comments.each do |comment| %> <p> <strong>Commenter:</strong> <%= comment.commenter %> </p> <p> <strong>Comment:</strong> <%= comment.body %> </p> <% end %> <h2>Add a comment:</h2> <%= form_for([@article, @article.comments.build]) do |f| %> <p> <%= f.label :commenter %><br> <%= f.text_field :commenter %> </p> <p> <%= f.label :body %><br> <%= f.text_area :body %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Edit', edit_article_path(@article) %> | <%= link_to 'Back', articles_path %> أصبح بإمكانك الآن إضافة المقالات والتعليقات إلى مدوّنتك وعرضها في الأماكن الصحيحة. في الدرس القادم سنكمل العمل على التعليقات، حيث سنستخدم الملفات الجزئية لترتيب القوالب أوّلًا، ثم نضيف إمكانية حذف التعليقات من قاعدة البيانات، وفي الختام سنتطرّق إلى عملية الاستيثاق Authentication بصورة سريعة ومبسّطة. المصدر: توثيقات Ruby on Rails.
  11. تعرّفنا في الدرس السابق بإيجاز على طريقة عمل إطار العمل Ruby on Rails حيث تعرّفنا على المتحكّمات والعروض وبدأنا بإنشاء تطبيق المدوّنة البسيطة وأنشأنا في الدرس السابق الاستمارة الخاصة بإضافة مقالة جديدة، ولكن وصلنا إلى النقطة التي نحتاج فيها إلى تخزين المقالة في قاعدة البيانات، وهنا يأتي دور النماذج Models. تحمل النماذج في Rails اسمًا بصيغة المفرد في حين يحمل الجدول المرتبط بها في قاعدة البيانات اسمًا بصيغة الجمع. تتيح أداة المولّد generator في Rails إنشاء النماذج ويلجأ أغلب المطوّرين إلى هذه الأداة لإنشاء النماذج. لإنشاء نموذج جديد استخدم الأمر التالي في سطر الأوامر: $ bin/rails generate model Article title:string text:text من خلال هذه الأمر نخبر Rails بأننا نرغب في إنشاء نموذج باسم Article إلى جانب خاصّية title من نوع string، وخاصّية text من نوع text. تضاف هذه الخواص بصورة تلقائية إلى جدول المقالات في قاعدة البيانات ويتم ربطها بنموذج Article. ويستجيب Rails لهذا الأمر بإنشاء عدد من الملفات، وما يهمّنا منها الآن هما app/models/article.rb و db/migrate/20140120191729_create_articles.rb (لاحظ أن اسم الملف الثاني يختلف قليلًا عن هذا الاسم). الملف الثاني مسؤول عن إنشاء بنية قاعدة البيانات، وهو ما سنتحدث عنه بعد قليل. إجراء عملية التهجير Migration كما لاحظنا فإن الأمر bin/rails generate model قد أنشأ ملف تهجير لقاعدة البيانات داخل المجلد db/migrate. والتهجيرات هي عبارة عن أصناف مصمّمة لتسهيل عملية إنشاء الجداول في قواعد البيانات والتعديل عليها. يستخدم Rails أوامر rake لإجراء التهجيرات، ويمكن التراجع عن عملية التجهير بعد إجرائها على قاعدة البيانات. تتضمّن أسماء ملفات التهجير ختمًا زمنيًا لضمان معالجة هذه الملفات حسب التسلسل الزمني لإنشائها. لو ألقينا نظرة في ملف db/migrate/YYYYMMDDHHMMSS_create_articles.rb (تذكّر أن الملف عندك يحمل ختمًا زمنيًّا مختلفًا) فسنجد التالي: class CreateArticles < ActiveRecord::Migration[5.0] def change create_table :articles do |t| t.string :title t.text :text t.timestamps end end end ستنشئ عملية التهجير أعلاه تابعًا يحمل اسم `change` والذي يتم استدعاؤه عند إجراء عملية التهجير. حتى الحدث المُعرّف ضمن التابع قابل للتراجع، ما يعني أن Rails قادر على التراجع عن التغييرات الحاصلة من إجراء عملية التهجير في حال أردت ذلك في وقت لاحق. عند إجراء عملية التهجير هذه سيتم إنشاء جدول باسم `articles` يتضمن عمودًا من نوع `string` وآخر من نوع `text`، إضافة إلى عمودين للختم الزمني يمكن لـ Rails من خلالهما متابعة تواريخ إنشاء وتعديل المقالات. لتنفيذ عمية التهجير توجّه إلى سطر الأوامر ونفذ الأمر التالي: $ bin/rails db:migrate سينفّذ Rails أمر التهجير التالي وسيخبرك بإنشاء جدول Articles. == CreateArticles: migrating ================================================== -- create_table(:articles) -> 0.0019s == CreateArticles: migrated (0.0020s) ========================================= حفظ البيانات بواسطة المتحكّم سنعود الآن إلى المتحكّم ArticlesController، حيث سنعمل على تعديل الحدث create ليستخدم النموذج الجديد Article لحفظ البيانات في قاعدة البيانات. افتح الملف app/controllers/articles_controller.rb وعدّله بالصورة التالية: def create @article = Article.new(params[:article]) @article.save redirect_to @article end يمكن استحداث initialize كل نموذج في Rails مع الخصائص Attributes المرتبطة به، والتي يتم ربطها تلقائيًا مع الأعمدة المقابلة في قاعدة البيانات. وقد قمنا بذلك في السطر الأول في الحدث create (هل تذكر params[:article] والذي يضمّ الخصائص التي نريدها). بعد ذلك يمكن حفظ النموذج في قاعدة البيانات من خلال الدالة @article.save. وفي النهاية نعيد توجيه المستخدم إلى الحدث show الذي سنعرّفه في وقت لاحق. توجّه الآن إلى العنوان http://localhost:3000/articles/new وستتلقّى الخطأ التالي: يدعم Rails العديد من مزايا الأمان التي تساعد في كتابة تطبيقات أمينة، ونحن الآن نتعامل مع إحدى هذه المزايا. تدعى هذه الميزة بالمعاملات القوية strong parameters والتي تجبرنا على تحديد المعاملات المسموح بها في الأحداث الموجودة ضمن المتحكم. ما الفائدة من ذلك؟ صحيح أن القدرة على إضافة جميع المعاملات إلى النموذج دفعة واحدة وبصورة تلقائية يختصر الكثير من الجهد بالنسبة للمبرمج، إلا أنّ البرنامج يكون في هذه الحالة عرضة للاستخدامات الخبيثة. فماذا لو تمّ إنشاء طلب إلى الخادوم يتضمن استمارة إنشاء مقالة جديدة إضافة إلى حقول أخرى تحتوي على معلومات تضرّ بالتطبيق؟ سيتم إسناد المعلومات الإضافية بصورة شاملة “Mass Assignment” إلى النموذج ثم إلى قاعدة البيانات جنبًا إلى جنب مع البيانات الأصلية، وهذا قد يتسبب في تعطيل عمل برنامجك أو قد يحدث ما هو أسوأ من ذلك بكثير. يجب علينا إذًا تحديد المعاملات المسموح بإدخالها إلى النموذج، وفي حالتنا هذه سنسمح بإدراج معاملي title و text ونطلب توفّر قيم لهما. وللقيام بذلك عدّل السطر الأول من حدث create بالصورة التالية: @article = Article.new(params.require(:article).permit(:title, :text)) غالبًا ما يتمّ تحديد المعاملات المسموح بإدخالها إلى النموذج في تابع خاص ليصبح بالإمكان إعادة استخدامها بواسطة عدة أحداث في المتحكّم نفسه مثل حدثي create و update، إضافة إلى ذلك يكون هذا التابع خاصًّا وذلك باستخدام المحدّد private لضمان عدم إمكانية استدعائه من خارج السياق المقرّر له، وبالشكل التالي: def create @article = Article.new(article_params) @article.save redirect_to @article end private def article_params params.require(:article).permit(:title, :text) end عرض المقالات إن قمت بتعبئة استمارة المقالة الجديدة وإرسالها فستتلقّى خطأ مفاده عدم عثور Rails على الحدث show، لذا سنقوم بإنشاء هذا الحدث الآن. كما رأينا سابقًا في مخرجات الأمر bin/rails routes فإن مسار الحدث show هو: article GET /articles/:id(.:format) articles#show تعني الصيغة الخاصة :id أن هذا المسار يطلب وجود معامل :id والذي يمثّل في حالتنا هذه معرّف المقالة. وكما فعلنا سابقًا، يجب علينا إضافة الحدث show إلى ملف المتحكّم app/controllers/articles_controller.rb وتحديد العرض المرتبط به. عادة ما تأخذ أحداث CRUD في المتحكّمات الترتيب التالي: index, show, new, edit, create, update, destroy. ويمكن اتّباع الترتيب الذي يعجبك، ولكن تذكّر أن هذه التوابع هي توابع عامّة public، ويجب الإعلان عنها قبل الإعلان عن التوابع الخاصّة. سنضيف الآن الحدث show آخذين ما سبق بعين الاعتبار: class ArticlesController < ApplicationController def show @article = Article.find(params[:id]) end def new end # بقيّة الشيفرة ..... استخدمنا الدالة Article.find للبحث عن المقالة المطلوبة، وذلك بتمرير المعامل params[:id] للحصول على قيمة المعرّف من الطلب الذي أرسلته صفحة إنشاء مقالة جديدة. كذلك استخدمنا متغيّرًا من نوع instance (مسبوقًا بعلامة @) ليكون مرجعًا لكائن المقالة، وذلك لأنّ Rails يمرّر هذا النوع من المتغيّرات إلى العرض. أنشئ الآن ملفًّا جديدًا باسم show.html.erb في المسار app/views/articles/ وأضف إليه الشيفرة التالية: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> ستكون الآن قادرًا على إنشاء مقالة جديدة؛ لذا توجّه إلى العنوان http://localhost:3000/articles/new وجرّب إضافة مقالة جديدة. عرض قائمة بمقالات المدوّنة نحتاج الآن إلى عرض قائمة بجميع المقالات الموجودة في المدونة، وسيكون المسار المرتبط بهذا الحدث وبحسب مخرجات الأمر bin/rails routes كالتالي: articles GET /articles(.:format) articles#index أضف الحدث index المرتبط بهذا المسار إلى المتحكّم ArticlesController في الملف app/controllers/articles_controller.rb. من الممارسات الشائعة بين المطوّرين هو كتابة الحدث index في بداية المتحكّم: class ArticlesController < ApplicationController def index @articles = Article.all end def show @article = Article.find(params[:id]) end def new end # بقية الشيفرة ... بعدها أضف العرض الخاصّ بهذا الحدث والموجود في المسار app/views/articles/index.html.erb والذي يتضمّن الشيفرة التالية: <h1>Listing articles</h1> <table> <tr> <th>Title</th> <th>Text</th> </tr> <% @articles.each do |article| %> <tr> <td><%= article.title %></td> <td><%= article.text %></td> <td><%= link_to 'Show', article_path(article) %></td> </tr> <% end %> </table> توجّه الآن إلى العنوان http://localhost:3000/articles في المتصفّح وستشاهد قائمة بجميع المقالات التي أنشأتها مسبقًا. إضافة الروابط للتنقل بين صفحات المدوّنة أصبح بمقدورنا الآن إنشاء وعرض وسرد قائمة المقالات المتوفّرة في المدونة، ولكنّنا بحاجة إلى بعض الروابط التي تساعدنا في التنقل بين صفحات الموقع. افتح الملف app/views/welcome/index.html.erb وعدّله كما يلي: <h1>Hello, Rails!</h1> <%= link_to 'My Blog', controller: 'articles' %> التابع link_to هو أحد دوال العروض المساعدة والمضمّنة في Rails، ووظيفة هذا التابع إنشاء رابط تشعّبي بالاستناد إلى النصّ الذي نمرّره إليه، وهو في حالتنا هذه المسار الخاص بسرد قائمة المقالات. لنضف بعض الروابط إلى العروض الأخرى، ولنبدأ بإضافة رابط إنشاء مقالة جديدة إلى الملف app/views/articles/index.html.erb قبل وسم <table>: <%= link_to 'New article', new_article_path %> سيوجّه هذا الرابط المستخدم إلى الصفحة التي تتضمن استمارة إنشاء مقالة جديدة. سنضيف رابطًا آخر إلى الملفّ app/views/articles/new.html.erb بعد الاستمارة مباشرة، ليتمكن المستخدم من العودة إلى الصفحة الرئيسية: <%= form_for :article, url: articles_path do |f| %> ... <% end %> <%= link_to 'Back', articles_path %> وأخيرًا، سنضيف رابطًا إلى القالب app/views/articles/show.html.erb يوجّه المستخدم إلى الصفحة الرئيسية أيضًا، وبهذا يصبح بميسور من يستعرض مقالة معيّنة أن يرجع إلى الصفحة التي تعرض جميع المقالات: <p> <strong>Title:</strong> <%= @article.title %> </p> <p> <strong>Text:</strong> <%= @article.text %> </p> <%= link_to 'Back', articles_path %> التحقّق من المدخلات لو نظرت إلى النموذج الذي أنشأناه سابقًا فسترى أنّ الملف بسيطٌ للغاية: class Article < ApplicationRecord end لاحظ أنّ الصنف Article موروث من الصنف ApplicationRecord وهو بدوره موروث من الصنف ActiveRecord::Base والذي يتضمّن الكثير من الوظائف والإجراءات الخاصة بالنماذج، مثل عمليات CRUD البسيطة (Create, Read, Update, Destroy) والتحقّق من البيانات Validation، إضافة إلى عمليات البحث المعقّدة وربط النماذج المختلفة مع بعضها البعض. ويقدّم إطار العمل Rails توابع متعدّدة تساعد في التحقق من البيانات المرسلة إلى النموذج. افتح الملف app/models/article.rb وأضف إليه الشيفرة التالية: class Article < ApplicationRecord validates :title, presence: true, length: { minimum: 5 } end سيضمن هذا التغيير امتلاك كل مقالة جديدة في المدونة لعنوان يتألف من خمسة أحرف على الأقل. يتيح Rails التحقّق من أمور متنوّعة في النماذج، مثل التحقّق من وجود أو عدم تكرار الأعمدة والتحقّق من تنسيقها ووجود كائنات مرتبطة بها. لنجرّب الآن استدعاء الدالة @article.save في مقالة لا تمتلك عنوانًا وسنلاحظ أن الدالة ترجع القيمة false. لو عدنا إلى المتحكّم في الملف app/controllers/articles_controller.rb مرّة أخرى سنلاحظ بأنّنا لم نتحقّق من النتيجة التي ترجعها الدالة @article.save ضمن الحدث create. إن فشلت الدالة @article.save في أداء عملها، يجب أن نعيد المستخدم إلى استمارة إضافة مقالة جديدة، وللقيام بذلك عدّل حدثي new و create في الملف app/controllers/articles_controller.rb بالصورة التالية: def new @article = Article.new end def create @article = Article.new(article_params) if @article.save redirect_to @article else render 'new' end end private def article_params params.require(:article).permit(:title, :text) end سينشئ الحدث new متغيّرًا جديدًا من نوع instance يحمل الاسم @article وستتعرّف إلى سبب القيام بذلك بعد قليل. لاحظ أنّنا استخدمنا render داخل الحدث create بدلًا من redirect_to في حال إرجاع الدالة save للقيمة false. يستخدم التابع render لكي يتم تمرير الكائن @article إلى القالب الجديد عند تصييره. وعملية التصيير هذه تتم ضمن نفس الطلب الناتج من إرسال الاستمارة، في حين أن الدالة redirect_to تتسبّب في إرسال طلب آخر. الآن أعد تحميل الصفحة ذات العنوان http://localhost:3000/articles/new وحاول إضافة مقالة جديد دون عنوان، سترى بأنّ Rails يعيدك إلى صفحة الاستمارة، ولكن هذا ليس مفيدًا جدًّا. يجب إخبار المستخدم بحدوث خطأ ما، وللقيام بذلك عدّل الملف app/views/articles/new.html.erb للتحقّق من رسائل الخطأ: <%= form_for :article, url: articles_path do |f| %> <% if @article.errors.any? %> <div id="error_explanation"> <h2> <%= pluralize(@article.errors.count, "error") %> prohibited this article from being saved: </h2> <ul> <% @article.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Back', articles_path %> تحقّقنا في البداية من وجود أي أخطاء من خلال @article.errors.any?، وفي حال وجودها نعرض قائمة الأخطاء المتوفّرة من خلال @article.errors.full_messages. تأخذ الدالة pluralize معاملين الأول رقمي والثاني نصّي. إن كان العدد أكبر من واحد تتحوّل السلسلة النصّية تلقائيًا إلى صيغة الجمع. إن سبب إضافة @article = Article.new إلى المتحكّم ArticlesController هو أنّنا لو لم نقم بذلك لأصبحت قيمة المتغيّر @articl هي nil، وسيؤدي الاستدعاء @article.errores.any? إلى إطلاق خطأ. يحيط Rails الحقول التي تحتوي على أخطاء بوسم <div> مع صنف CSS يحمل الاسم field_with_errors، ويمكنك تعريف صنف CSS هذا لتنسيق الحقول حسب الرغبة. والآن ستتلقّى رسالة خطا مرتّبة عندما تحاول حفظ مقالة لا تتضمن عنوانًا. في الدرس القادم سنواصل العمل على النموذج حيث سنكتب الشيفرة المسؤولة عن تعديل المقالات وحذفها، ثم سنتعرّف على طريقة إنشاء علاقات بين النماذج المختلفة من خلال إضافة نموذج للتعامل مع التعليقات في المدونة. المصدر: توثيقات Ruby on Rails.
  12. 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.
  13. تعرّفنا في الدرس السابق على طريقة تثبيت إطار العمل Rails وبدأنا العمل على مشروعنا الأول وهو عبارة عن مدوّنة بسيطة، وقمنا بتشغيل الخادوم الخاص بإطار العمل. وفي هذا الدرس سنتعرّف على آلية عمل إطار العمل Rails من خلال مثال بسيط، ثم نشرع بعده ببناء مدونتنا البسيطة لنتعرف بصورة أكبر على العديد من المفاهيم التي يستند إليها هذا الإطار. آلية عمل إطار Rails سنتعرّف على آلية عمل إطار Ruby on Rails من خلال مثال بسيط نعرض فيه مجموعة من الكلمات في الصفحة الرئيسية لتطبيقنا، وللقيام بذلك سنحتاج إلى متحكّم Controller وعرض View. وظيفة المتحكّم هي استقبال الطلبات الواردة إلى التطبيق، وتربط المسارات Routes بين الطلبات والمتحكّمات. وغالبًا ما يكون هناك أكثر من مسار واحد لكل متحكّم، ويمكن للمسارات المختلفة أن تؤدّي إلى أحداث Actions مختلفة، ووظيفة الحدث هي جمع المعلومات اللازمة وتقديمها إلى العرض. أمّا وظيفة العرض فواضحة من اسمه، وهي عرض المعلومات التي حصل عليها من الحدث بصورة مقروءة للإنسان. من الضروري هنا الانتباه إلى أن عملية جمع المعلومات تتمّ ضمن المتحكّم وليس ضمن العرض، ومهمّة العرض الوحيدة هي عرض المعلومات. يستخدم إطار Rails لغة قوالب خاصّة في العروض تدعى eRuby (اختصار لـ Embedded Ruby) والتي تُعالج بواسطة دورة الطلب في Rails قبل أن تُرسل إلى المستخدم. سلنجأ إلى أداة المولّد generator لإنشاء متحكّم يحمل اسم Welcome يتضمّن حدثًا باسم index. اكتب الأمر التالي في سطر الأوامر: $ bin/rails generate controller Welcome index سيقوم Rails بإنشاء مسار وعدد من الملفات. create app/controllers/welcome_controller.rb route get 'welcome/index' invoke erb create app/views/welcome create app/views/welcome/index.html.erb invoke test_unit create test/controllers/welcome_controller_test.rb invoke helper create app/helpers/welcome_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/welcome.coffee invoke scss create app/assets/stylesheets/welcome.scss ما يهمّنا من هذه الملفات هما المتحكم والموجود في المسار app/controllers/welcome_controller.rb والعرض الموجود في المسار app/views/welcome/index.html.erb. افتح الملف app/views/welcome/index.html.erb في محرّر النصوص المفضّل لديك، واحذف محتوياته واستبدلها بالشيفرة التالية: <h1>Hello, Rails!</h1> بعد أن أنشئنا المتحكم والعرض يجب علينا إخبار Rails بالمسار الذي سيأخذ المستخدم إلى هذا العرض. نحن نرغب في حالتنا هذه أن يتم توجيه المستخدم إلى العرض عندما يتوجّه إلى العنوان http://localhost:3000، ولكن صفحة الترحيب تشغل هذا المسار في الوقت الحاضر. بعد ذلك يجب إخبار Rails بموقع الصفحة الرئيسية ليتمكّن من عرضها للمستخدم. افتح الملف config/routes.rb في محرّر النصوص: Rails.application.routes.draw do get 'welcome/index' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end الشيفرة أعلاه موجودة في ملف المسارات والذي يتضمّن مدخلات DSL خاصّة (DSL اختصار لـ domain-specific language) والتي تخبر Rail بطريقة ربط الطلبات الواردة إلى التطبيق بالمتحكمات والأحداث. عدّل هذا الملف بالصورة التالية: Rails.application.routes.draw do get 'welcome/index' root 'welcome#index' end من خلال السطر root 'welcome#index' يربط إطار العمل Rails الطلبات الواردة إلى المسار الرئيسي في التطبيق مع الحدث index في المتحكّم Welcome، أما السطر get 'welcome/index' فيربط من خلاله Rails الطلبات الواردة إلى العنوان http://localhost:3000/welcome/index بنفس الحدث ونفس المتحكّم، وقد تمّ إنشاء هذه الشيفرة من قبل أداة المولّد. والآن شغّل الخادوم الخاص بـ Rails ثمّ توجّه في المتصفّح إلى العنوان http://localhost:3000، وستشاهد عبارة “Hello, Rails!” الموجودة في ملف app/views/welcome/index.html.erb وهذا يعني أن هذا المسار قد توجّه فعلًا إلى الحدث index في المتحكم Welcome والذي قام بدوره بتصيير العرض بصورة صحيحة. البدء بإنشاء المدوّنة بعد أن تعرّفنا على المتحكّمات والأحداث والعروض، لنبدأ العمل على مدوّنتنا. سننشئ الآن ما يسمى في إطار العمل Rail بالمورد resourse، والمورد هو مصطلح يعبّر عن مجموعة من العناصر المتشابهة، مثل المقالات، الأشخاص أو الحيوانات. ويمكن إنشاء create وقراءة read وتحديث update وإلغاء destroy العناصر في المورد، وتسمى هذه العمليات بعمليات CRUD. يقدّم Rails تابعًا باسم resources يمكن استخدامه للإفصاح عن مورد بنمط REST القياسي. يجب إضافة مورد المقالة إلى ملف config/routes.rb وكما يلي: Rails.application.routes.draw do get 'welcome/index' resources :articles root 'welcome#index' end والآن إن قمت بتنفيذ الأمر bin/rails routes فستشاهد جميع المسارات الخاصّة بجميع الأحداث التي تتّصف بنمط REST. سنتعرّف على معنى عمود prefix وبقية الأعمدة في وقت لاحق، ولكن لاحظ أنّ Rails قد خمّن صيغة المفرد (article) واستخدمها في السياق الصحيح. $ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create new_article GET /articles/new(.:format) articles#new edit_article GET /articles/:id/edit(.:format) articles#edit article GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update PUT /articles/:id(.:format) articles#update DELETE /articles/:id(.:format) articles#destroy root GET / welcome#index سنعمل الآن على إضافة خاصيتي إنشاء المقالات وعرضها، وستكون الاستمارة Form المسؤولة عن ذلك بالشكل التالي: قد تبدو الاستمارة بدائيًّة ولكنّها كافية في الوقت الحاضر، وسنعمل على تحسين مظهرها فيما بعد. إنشاء المتحكّمات والمسارات اللازمة في البداية يجب اختيار المسار الذي سيوجّه المستخدم إلى استمارة إنشاء المقالة الجديدة، وسنستخدم المسار /articles/new للقيام بهذه المهمّة، بعدها يصبح بميسور التطبيق أن يتلقّى الطلبات على هذا المسار. توجّه الآن في متصفحك إلى الرابط http://localhost:3000/articles/new وستتلقّى الخطأ التالي: يحدث هذا الخطأ لأنّ المسار بحاجة إلى متحكّم يرسل إليه الطلب؛ لذا سنقوم بإنشاء متحكّم باسم ArticlesController، وذلك من خلال تنفيذ الأمر التالي: $ bin/rails generate controller Articles افتح الملف الذي قمت بإنشائه app/controllers/articles_controller.rb وسترى متحكّمًا فارغًا: class ArticlesController < ApplicationController end المتحكّم عبارة عن صنف Class موروث من ApplicationController وسنقوم بتعريف التوابع ضمن هذا الصنف والتي ستمثل الأحداث الخاصّة بهذا المتحكم، وهذه الأحداث هي المسؤولة عن تنفيذ عمليات CRUD على المقالات الموجودة في تطبيقنا. إن أعدت تحميل الصفحة ستتلقّى خطأً جديدًا: يشير هذا الخطأ إلى عدم قدرة Rails على إيجاد الحدث new ضمن المتحكّم ArticlesController الذي قمنا بإنشائه للتوّ. وهذا عائد إلى أنّ المتحكّمات تكون فارغة عند إنشائها إلا إذا حدّدنا الأحداث المطلوبة خلال عملية إنشاء المتحكّم. ولتعريف حدث جديد بصورة يدوية، سنحتاج فقط إلى تعريف تابع جديد ضمن المتحكم. افتح الملف app/controllers/articles_controller.rb وضمن الصنف ArticlesController عرّف تابعًا جديدًا وكما يلي: class ArticlesController < ApplicationController def new end end والآن أعد تحميل الصفحة في المتصفّح وستتلقّى خطأً آخر: يظهر هذا الخطأ لأنّ Rails يتوقّع أنه يجب أن تمتلك الأحداث الصرفة المشابهة لهذا الحدث عروضًا ترتبط معها لعرض المعلومات التي تتضمنها، ونظرًا لعدم وجود أي عرض مرتبط بهذا الحدث، أطلق Rails هذا الخطأ. لنطّلع على رسالة الخطأ الكاملة: لنستعرض النص السابق سريعًا، ونفهم مضمونه بصورة جيدة. يحدّد الجزء الأول من نصّ الخطأ القالب المفقود، وفي هذه الحالة القالب المفقود هو articles/new. يبدأ Rails بالبحث عن هذا القالب، وإن لم يفلح في العثور عليه فإنه يحاول تحميل قالب يدعى application/new وذلك لأنّ المتحكّم ArticleController هو صنف موروث من المتحكّم ApplicationController. يتضمن الجزء الثاني من رسالة الخطأ request.formats والذي يحدّد صيغة القالب الذي سيتم عرضه كاستجابة للطلب الذي تلقّاه التطبيق، وقد تم اختيار صيغة text/html لأنّنا طلبنا هذه الصفحة بواسطة المتصفح؛ لذا يبحث Rails عن قوالب HTML. أما request.variant فيحدد طبيعة الأدوات المادّية physical devices التي سيتم تقديمها مع الطلب وتساعد Rails في تحديد القالب الذي سيستخدمه في الاستجابة، وهو فارغ نظرًا لعدم توفّر المعلومات. أبسط قالب يمكن أن يعمل في هذه الحالة هو القالب الموجود في المسار app/views/articles/new.html.erb. هذه اللاحقة مهمّة للغاية: فالجزء الأول من اللاحقة (.html) يعبّر عن صيغة القالب، أمّا الجزء الثاني (.erb) فيمثّل المعالج handler الذي سيتم استخدامه في تصيير القالب. يحاول Rails البحث عن قالب يحمل الاسم articles/new ضمن المجلد app/views. يجب أن تكون صيغة هذا القالب هي HTML حصرًا، وسيكون erb المعالج الافتراضي لـ HTML. يستخدم Rails عددًا من المعالجات مثل: builder والمستخدم في إنشاء قوالب XML، وcoffee الذي يستخدم لغة CoffeeScript لبناء قوالب JavaScript. بما أنّنا نرغب في بناء استمارة HTML جديدة فسنستخدم لغة ERB والتي تتيح لنا تضمين لغة Ruby في HTML. إذًا سيكون اسم القالب articles/new.html.erb وسيكون ضمن المجلد app/views الخاص بالتطبيق. أنشئ ملفًّا جديدًا باسم new.html.erb في المسار app/views/articles وأضف إليه ما يلي: <h1>New Article</h1> أعد تحميل الصفحة وستلاحظ ظهور العنوان في رأس الصفحة، وهذا يعني أن هناك تناغمًا تامًّا بين كلّ من المسار والمتحكم والحدث والعرض. الاستمارة الأولى سنستخدم منشئ النماذج form builder لإنشاء الاستمارة الأولى في هذا القالب. يمكن استخدام منشئ النماذج الرئيسي في Rails باستخدام التابع المساعد form_for. أضف الشيفرة التالية في الملف app/views/articles/new.html.erb: <%= form_for :article do |f| %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> أعد تحميل الصفحة وستلاحظ ظهور نفس الاستمارة التي عرضناها في المثال السابق. كما تلاحظ فإنّ بناء الاستمارات في Rails أمر سهلٌ للغاية. عندما نستدعي التابع form_for فإننا نمرّر إليه عنصرًا يحدّد الهدف من هذه الاستمارة، والهدف في حالتنا هذه هو :article. يُستخدم الكائن FormBuilder والذي مثّلناه بـ f لبناء عنصري label وحقلي نصوص text fields لكلّ من عنوان المقال ومتنها. وفي النهاية استدعينا التابع submit لإنشاء زرّ اﻹرسال الخاصّ بالاستمارة. ولكن تعاني هذه الاستمارة من مشكلة صغيرة. لو تفحّصت شيفرة HTML التي تم توليدها من خلال الشيفرة السابقة فستلاحظ أن خاصية action التابعة للاستمارة تشير إلى المسار articles/new وهذا المسار هو نفسه الذي يقودنا إلى هذه الصفحة، والمفروض أن يستخدم هذا المسار لعرض استمارة إنشاء مقالة جديدة لا غير. إذًا يجب أن تستخدم الاستمارة مسارًا آخر، ويمكن القيام بذلك بسهولة من خلال استخدام الخيار :url في form_for. عادة ما يحمل الحدث المسؤول عن إرسال مقال جديد اسم “create”، لذا يجب توجيه الاستمارة إلى هذا الحدث. عدّل السطر الذي يتضمن form_for في ملف app/views/articles/new.html.erb كما يلي: <%= form_for :article, url: articles_path do |f| %> في هذا المثال تم تمرير الدالة المساعدة articles_path إلى الخيار :url، ولنتعرّف على نتيجة هذا التعديل سنلقي نظرة على مخرجات الأمر bin/rails routes في سطر اﻷوامر: $ bin/rails routes Prefix Verb URI Pattern Controller#Action articles GET /articles(.:format) articles#index POST /articles(.:format) articles#create new_article GET /articles/new(.:format) articles#new edit_article GET /articles/:id/edit(.:format) articles#edit article GET /articles/:id(.:format) articles#show PATCH /articles/:id(.:format) articles#update PUT /articles/:id(.:format) articles#update DELETE /articles/:id(.:format) articles#destroy root GET / welcome#index توجّه الدالة المساعدة articles_pat الاستمارة إلى نمط URI المرتبط لاحقة articles وسيرسل الاستمارة - تلقائيًا - طلبًا من نوع POST إلى المسار، والذي يرتبط بالحدث create التابع للمتحكّم ArticlesController. بعد أن أنشأنا الاستمارة وعرّفنا المسار المرتبط به، أصبح باﻹمكان تعبئة حقول الاستمارة والضغط على زرّ اﻹرسال لبدء عملية إنشاء مقال جديد، ولكن عند إرسال المقال ستتلقّى الخطأ المتوقَّع التالي: علاج هذا الخطأ بسيط وهو إنشاء الحدث create ضمن المتحكّم ArticlesController. إنشاء المقالات سنقوم الآن بتعريف الحدث create ضمن صنف ArticlesController في الملف app/controllers/articles_controller.rb بعد الحدث new وكما يلي: class ArticlesController < ApplicationController def new end def create end end إن أعدت إرسال الاستمارة مرة أخرى فستلاحظ عدم حدوث أي تغيير في الصفحة. لا تقلق، هذا الأمر عائد إلى أنّ Rails يعيد الاستجابة “204 No Content” لأي حدث لا يحدّد الاستجابة المطلوبة. وقد أضفنا الحدث create دون تحديد الاستجابة المطلوبة منه، وهي في حالتنا هذه، إنشاء مقالة جديدة في قاعدة البيانات. عند إرسال الاستمارة يتم إرسال الحقول الخاصة بها إلى Rails على هيئة معاملات parameters، يمكن الإشارة إليها في الأحداث التابعة للمتحكّم وذلك لإنجاز مهّمة ما، ولتعرف كيف تبدو هذه المعاملات عدّل حدث create بالصورة التالية: def create render plain: params[:article].inspect end يأخذ التابع render هنا جدول تقطيع Hash بسيط مع المفتاح :plain والقيمة هي تابع params [:article].inspect. توابع params هي الكائن الذي يمثّل المعامل (أو الحقل) المأخوذ من الاستمارة. ويعيد تابع params كائن من نوع ActionController::Parameters والذي يتيح لنا الوصول إلى مفاتيح جدول التقطيع من خلال السلاسل النصّيّة Strings أو الرموز Symbols. وفي حالتنا هذه، فإن المعاملات المهمّة هي المعاملات المأخوذة من الاستمارة. لتوضيح عمل توابع params، إليك المثال التالي: في عنوان URL هذا: http://www.example.com/?username=dhh&email=dhh@mail.com فإن params[:username] تحمل القيمة “dhh” و params[:email] تحمل القيمة “dhh@mail.com”. والآن أعد إرسال الاستمارة مرة أخرى وستشاهد شيئًا مماثلًا لما يلي: <ActionController::Parameters {"title"=>"First Article!", "text"=>"This is my first article."} permitted: false> يعرض هذا الحدث المعاملات الخاصة بالمقالة والمأخوذة من الاستمارة، ولكن ليس هذا ما نريده بالضبط، فنحن نشاهد المعاملات ولكنّها لا تقدّم أي فائدة تذكر في حالتها هذه. في الدرس القادم سنتعرّف على النماذج Models في إطار العمل Rails وسنستخدمها في إضافة مقالة جديدة إلى قاعدة البيانات التابعة للتطبيق. المصدر: توثيقات Ruby on Rails.
  14. جدول التّقطيع Hash هي بنية بيانات Data Structure مشابهة إلى حد كبير للمصفوفات التي تحدّثنا عنها في الدّرس السّابق (حتّى أنّ جداول التّقطيع تسمّى في بعض اللُّغات الأخرى Associative Arrays). يندرج الاثنان تحت ما يسمّى بالمجموعات Collections. يتيح لنا النوعان حفظ بيانات مختلفة مرّة واحدة دون الحاجة إلى استخدام الكثير من المتغيّرات لحفظها. إلا أنّ جداول التّقطيع تختلف عن المصفوفات بعض الشيء. فمثلاً يمكننا الإشارة إلى عنصر في المصفوفة عن طريق مكان هذا العنصر أو ما يسمّى بالدليل Index، أمّا بالنسبة لجدول التّقطيع فنستخدم معرّفًا Identifier للإشارة إلى العنصر الذي نريد الوصول إليه. يمكن اعتبار جداول التّقطيع على أنّها قوائم مكوّنة من أزواج مفاتيح Key وقيمة Value. يمكن للمعرّف أو المفتاح أن يكون أي نوع من أنواع بيانات روبي التي تعرّفنا عليها في الدروس السّابقة. ولكن أكثر الأنواع المستخدمة شيوعًا هو السلاسل، الأرقام و الرموز Symbols (هذا الأخير هو أكثرها شيوعًا على الإطلاق). سنتعرّف في هذا الدّرس على كيفيّة إنشاء والتّعامل مع جداول التّقطيع. إنشاء جدول التقطيع لنبدأ العمل مع جداول التّقطيع الآن، ابدأ جلسة روبي جديدة في سطر أوامر روبي التفاعليّ عن طريق كتابة أمر irb في الطرفيّة. عمليّة إنشاء جدول التّقطيع تشبه نوعًا ما عمليّة إنشاء مصفوفة. هناك طرق مختلفة لإنشاء جدول تقطيع. يمكن تهيئة Instantiate جدول تقطيع باستخدام الحاضنات Curly Braces أو تهيئة نسخة من صنف Hash. لنتعرّف أوّلاً على الطريقة الأولى. وهي عبارة عن إنشاء جدول تقطيع فارغ ومن ثمّ إضافة عناصر إليه فيما بعد. فمثلاً إذا أردنا إنشاء جدول تقطيع فارغ وتعيينه إلى متغيّر اسمه myHash نفعل ذلك كالتّالي: myHash = Hash.new تأكّد من كتابة Hash بحرف كبير. هناك طريقة أقصر من ذلك أيضًا وذلك بتعيين حاضنات فارغة للمتغيّر هكذا: myHash = {} بعد الضغط على Enter في الحالتين ستجد أن روبي قد قامت بإرجاع حاضنات فارغة معلنةً عن إنشاء جدول تقطيع فارغ. ولكن ماذا سنفعل بجدول تقطيع فارغ على كل حال؟ جدول تقطيع فارغ ليس بالشيء المفيد على الإطلاق. الآن لو أردنا إنشاء جدول تقطيع باستخدام الحاضنات وتعيينه إلى متغيّر اسمه marge ثم بداخل جدول التّقطيع قمنا بإضافة العناصر المتكوّنة من مفتاح وقيمة. نفعل ذلك هكذا: marge = { :name => "Marge Simpson" } ماذا فعلنا هنا؟ أنشأنا متغيّرًا باسم marge وقمنا بتعيين قيمة جدول التّقطيع الذي أنشأناها في الطرف الأيمن لذلك المتغيّر. وضعنا بجدول التّقطيع عنصرًا واحدًا مكوّنًا من المفتاح name: والذي هو من نوع Symbol (عرّفنا النّوع عن طريق النقطتين Colon الموجودتان قبله) والقيمة التي هي عبارة عن سلسلة، قمنا بتعيين القيمة إلى المفتاح عن طريقة ما يسمّى بسهم جدول التّقطيع Hash Rocket (<=). يعدّ استخدام الرموز (ٍSymbol) كمفاتيح في جداول التّقطيع مثلما فعلنا في المثال السّابق أمرًا شائعًا جدًا في روبي. قد تتساءل عن السبب؟ السبب الرئيسي هو أنّ الرموز تستخدم الذّاكرة بطريقة أفضل وأكثر إفادة بكثير من السلاسل مثلاً. هناك طريقة أخرى مختصرة لإنشاء جداول التّقطيع ولتوضيح الطريقة سنقوم بإنشاء جدول تقطيع جديد ونعيّنه إلى المتغيّر homer كالتّالي: homer = { name: "Homer Simpson" } هل لاحظت الفرق بين هذه الطّريقة والسّابقة؟ لا تختلف هذه الطريقة كثيرًا عن السّابقة، فقط حذفنا سهم جدول التّقطيع ونقلنا النقطتين لتصبحا بين المفتاح والقيمة. لا زال المفتاح رمزًا كما هو لم يتغيّر نوعه. لاحظ عند الضّغط على Enter ستقوم روبي بإرجاع جدول تقطيع بالصورة الافتراضيّة لها (باستخدام سهم جدول التّقطيع ) رغم استخدامنا للطريقة المختصرة، كالتّالي: {:name => "Homer Simpson"} يمكننا إنشاء جدول تقطيع يحتوي على أكثر من زوج مفتاح/قيمة. مثلاً لنعد إنشاء جدول التّقطيع homer من جديد هكذا: homer = { name: "Homer Simpson", job: "Nuclear Safety Inspector", children: ["Bart", "Lisa", "Maggie"] } كما تلاحظ يمكننا استخدام أنواع مختلفة من الكائنات كعناصر في جدول التّقطيع . لسنا مضطرّين لاستخدام السلاسل فقط. فدلينا في المثال السّابق قيم العنصرين name و job من نوع سلسلة والعنصر الثالث الذي يحتوي على مفتاح children قيمته من نوع مصفوفة مكوّنة من ثلاثة عناصر. التعامل مع جداول التقطيع إذا أردنا التحقّق إذا كان هناك مفتاح معيّن موجود في جدول التّقطيع أم لا يمكننا فعل ذلك باستخدام دالّة include. فمثلاً إذا أردنا معرفة إذا كان لدى homer وظيفة يمكننا التحقّق كالتّالي: homer.include?(:job) إذًا فقد استخدمنا اسم جدول التّقطيع أو المتغيّر الذي قمنا بتعيين جدول تقطيع له، ثم لدينا استدعاء دالّة include، وحيث كما ذكرنا في الدروس السّابقة أنّ include دالّة منطقيّة Boolean Method والدوال المنطقيّة تنتهي بعلامة استفهام لأن ناتج استدعائها true أو false (باختصار هي كأن تسأل مثلاً، هل لهومر وظيفة؟ لن تجد إطلاقًا إجابة غير نعم أو لا). إذًا ناتج هذا الأمر السّابق سيكون true إذا وجدت روبي قيمة للمفتاح job أو false إذا لم تجد. وبالنّظر إلى جدول التّقطيع التي لدينا فنتوقّع إجابة روبي بـ true حيث أنّه موجود وظيفة (المفتاح job) لـ homer بالفعل في جدول التّقطيع . إذًا نحن نعرف أنّ العنصر موجود، الآن نريد معرفة كيف يمكننا استخدام هذه القيمة (سواء لطباعتها على الشّاشة أو لو استخدامها في عمليّات أخرى). للوصول إلى القيمة الفعليّة بدلاً من التحقّق من وجودها فقط ولمعرفة أن هذه القيمة خاصّة بالمفتاح المحدّد كل ما نفعله هو استخدام المفتاح بطريقة مشابهة جدًا لتلك التي استخدمناها مع الدليل Index في المصفوفات. إذًا إذا أردنا معرفة وظيفة homer يمكننا فعل ذلك كالتّالي: homer[:job] إذًا كلّ ما فعلناه أعلاه هو كتابة اسم جدول التّقطيع واسم المفتاح بين قوسين مربّعين. بعد تنفيذ الأمر سترجع لك روبي الوظيفة. لكن ماذا لو استدعينا مفتاحًا ليس موجودًا في جدول التّقطيع أصلاً؟ مثلاً لو أردنا معرفة عمر homer باستخدام مفتاح age:. لم نقم بحفظ قيمة كهذه في جدول التّقطيع لذلك إذا استدعيناها فلن ترجع روبي شيئًا غير nil. ولكن ماذا تعني nil؟ nil هو كائن في روبي يمثّل اللاشيء إذا أردت تعريفه كذلك. متى أخفقت دالّة بإرجاع قيمة مفيدة أو ذات معنى فإنّها تقوم بإرجاع nil. إذا أردنا معرفة عدد القيم الموجودة في جدول تقطيع يمكننا استخدام دالّة length. جرّب تنفيذ الأمر التالي وستحصل على طول جدول التّقطيع الخاصّة بك: homer.length قد تحتاج إلى إضافة قيم جديدة إلى جدول التّقطيع (فارغة كانت أو تحتوي على عناصر) بعد إنشائها، يمكنك فعل ذلك عن طريقة كتابة اسم المفتاح الجديد وجعله مساويًا للقيمة التي تريد تعيينها إلى ذلك المفتاح. فمثلاً إذا أردنا إضافة مفتاح age إلى homer والذي أخبرتنا روبي أعلاه بأنّه ليس موجودًا في جدول التّقطيع عن طريقة إرجاعها nil. فيمكننا فعل ذلك كالتّالي: homer[:age] = 38 الآن للتأكّد من أن العنصر قد تمّت إضافته فإنّ كلّ ما علينا فعله هو كتابة اسم جدول التّقطيع (homer في هذه الحالة) والضغط على Enter. ستجد أنّ المفتاح age وقيمته موجودين بنهاية جدول التّقطيع . والآن أيضًا يمكنك سؤال روبي عن قيمة المفتاح age وتوقّع ظهور قيمة بدلاً من اللاقيمة nil. يمكن أيضًا إضافة جدول تقطيع ليكون أحد القيم في جدول تقطيع آخر. مثلاً إذا أردنا إضافة بيانات زوجة homer إلى جدول التّقطيع الخاصّ به ولدينا فعلاً جدول تقطيع باسم زوجته marge فيمكننا فعل ذلك كالتّالي: homer[:wife] = marge ماذا حدث؟ قمنا هنا بتعيين جدول التّقطيع الموجودة بالمتغيّر marge إلى المفتاح الجديد wife المضاف إلى جدول التّقطيع homer. بعد كتابة هذا والضّغط على Enter ستجد أنّ روبي قد أعادت القيمة الموجودة في marge وهي جدول تقطيع مكوّنة من عنصر واحد. {:name => "Marge Simpson"} هل توقّعت أن يكون الأمر أكثر صعوبة؟ لا تتوقع أن تواجه صعوبة في روبي لحسن الحظّ. الآن إذا أردنا النظر في جدول تقطيع homer ستجد أنّ جدول التّقطيع قد أضيف. ما فعلناه هنا هو أنّنا قمنا بإنشاء جدول تقطيع متداخل Nested Hash، بمعنى أنّ لدينا جدول تقطيع داخل جدول تقطيع آخر. الآن إذا أردنا الوصول إلى قيمة جدول تقطيع المتداخل نفعل ذلك عن طريق استدعاء كل مفتاح بالترتيب بدءًا من جدول التّقطيع المحتوي على الآخر. فمثلاً إذا أردت معرفة اسم زوجة هومر فنستدعي المفتاح wife كما تعوّدنا ولكن نضيف إلى ذلك المفتاح أيضًا مفتاح name الموجود في جدول تقطيع marge كالتّالي: homer[:wife][:name] إذًا فالمفتاح الأوّل هو الموجود بجدول تقطيع homer والمفتاح الثّاني هو الموجود في جدول تقطيع marge. بالضغط على Enter ستقوم روبي بإرجاع القيمة الموجودة في المفتاح name الموجود في marge وليس homer، لا تنس ذلك. "Marge Simpson" دوال جداول التقطيع الكثير من الدوال الخاصّة بجداول التّقطيع هي نفسها الخاصّة بالمصفوفات تعرّفنا على اثنين منها (include و length). لحذف عنصر في جدول تقطيع نستخدم دالّة delete والتي تستقبل مفتاح العنصر المراد حذف كمعطى. تقوم الدّالة أيضًا بإرجاع القيمة التي تمّ حذفها. homer.delete(:age) # 38 لحذف أوّل عنصر في جدول تقطيع نستخدم دالّة shift والتي تحذف أوّل زوج مفتاح/قيمة بجدول تقطيع وتقوم بإرجاعهما في مصفوفة. homer.shift # [:name, "Homer Simpson"] الدوال الخاصة بجداول التقطيع فقط رغم ذكرنا سابقًا أنّ دوال جداول التّقطيع تشبه دوال المصفوفات إلا أن جداول التّقطيع تختصّ ببعض الدوال عن المصفوفات نظرًا لاحتوائها على مفاتيح وقيم. فمثلاً دالّة keys تقوم بإرجاع مصفوفة بها جميع المفاتيح الموجودة بجدول تقطيع . homer.keys # [:job, :children, :wife] دالّة values تقوم بإرجاع مصفوفة تحتوي على جميع القيم الموجودة في جدول تقطيع . homer.values # ["Nuclear Safety Inspector", ["Bart", "Lisa", "Maggie"], { :name => "Marge Simpson" }] لمعرفة إذا كانت قيمة معيّنة موجودة في جدول تقطيع يمكننا استخدام دالّة ?has_value أو اختصارها ?value. homer.value?("Homer Simpson") # false قد تتساءل لماذا قامت روبي بإرجاع false رغم إنشائنا للمفتاح في البداية؟ دعني أُذكّرك أنّه قد تمّ حذف العنصر باستخدام دالّة shift أعلاه. لمعرفة إذا كان مفتاح معيّن موجود في جدول تقطيع أم لا يمكننا استخدام دالّة ?has_key أو اختصارها ?key. homer.key?(:job) # true خاتمة تعرّفنا في هذا الدّرس على جداول التّقطيع، كيفيّة إنشائها وكيفيّة التعامل معها وإضافة عناصر إليها وبعض الدوال الخاصّة بها. حان دورك للتطبيق على ما تعلّمت. يمكنك معرفة المزيد عن جداول التّقطيع عن طريق قراءة توثيق روبي. ربما تريد الآن تحسين قائمة المشتريات (التي استخدمناها كمصفوفة في الدّرس السّابق) عن طريق وضعها في جدول تقطيع بدلاً من مصفوفة. إذا استعصى عليك أمر أو لديك سؤال لا تتردّد في طرحه في قسم التعليقات أدناه.
  15. بعض أنواع البيانات التي تحدّثنا عنها في الدروس السّابقة هي السلاسل و الأرقام. تتيح لنا السلاسل إمكانيّة التعامل مع النصوص الخاصّة ببرنامجنا والأرقام تمكننا من التعامل مع البيانات الرقميّة. لنفترض مثلاً أنّنا نريد إنشاء قائمة مشتريات، بداخل تلك القائمة لدينا أسماء المشتريات. يمكننا إنشاء تلك القائمة على شكل سلسلة String مفصول بين كل عنصر فيها بفاصلة. أو يمكننا استخدام ثلاثة سلاسل محفوظة بمتغيّرات مسمّاة item1, item2 وهكذا. ولكن لحسن الحظّ فإنّ روبي تُوفّر لنا نوعًا يمكنه التعامل مع هذا النّوع من البيانات فعلاً يسمّى بالمصفوفة Array. المصفوفة هي عبارة عن حاوية للبيانات. تُستخدم المصفوفات لتخزين أنواع مختلفة من البيانات مثل السلاسل، الأرقام وأيّ نوع آخر من كائنات روبي. سنتعرّف في هذا الدّرس على كيفيّة إنشاء المصفوفات والتّعامل معها في روبي. إنشاء مصفوفة سنستخدم سطر أوامر روبي التفاعليّ الآن للتعرّف على المصفوفات. ابدأ جلسة روبي في الطرفيّة عن طريق كتابة irb والضّغط على Enter. يمكن إنشاء مصفوفة حرفيّة Array Literal عن طريق وضع الكائنات داخل أقواس مربّعة Square Brackets مفصول بينها بفاصلة. كالمثال أدناه، أنشأنا قائمة من الأعداد الأوّليّة: [2, 3, 5, 7] لا تشترط المصفوفات بأن تقوم بتخزين أرقام فقط بها. يمكنك أيضًا إنشاء مصفوفة مكوّنة من سلاسل هكذا: ["apples", "oranges"] هناك أيضًا طريقة أقصر لإنشاء مصفوفة من السلاسل. نبدأ ذلك بكتابة علامة النّسبة المئويّة متبوعة بحرف w ثمّ بعد ذلك يمكنك الاختيار إذا كنت تريد استخدام الأقواس Parentheses أو الحاضنات Curly Brackets لبدء المصفوفة، الأمر الإيجابي حول هذه الطريقة هو أنّه باستخدامها لن تصبح في حاجة إلى وضع كل سلسلة بين علامات اقتباس وأيضًا لن تحتاج إلى الفصل بين عناصر المصفوفة باستخدام الفاصلة، استخدام المسافات يفي بالغرض. %w(apples, oranges) %w{apples oranges} عند كتابة المصفوفة بأحد الطريقتين أعلاه والضّغط على Enter ستلاحظ أنّ روبي ستضعها في الصّورة الافتراضيّة للمصفوفة، أقواس مربّعة ومفصول بين السلاسل بفاصلة. ليس مفروضًا عليك استخدام كائنات من نوع بيانات واحد في المصفوفة فيمكنك إنشاء مصفوفة تحتوي على أكثر من نوع من العناصر، كما تلاحظ في المثال أدناه فقد أنشأنا مصفوفة تحتوي على عدد صحيح Integer، سلسلة وعدد عشري Float: [1, "two", 3.0] هناك طريقة أخرى لإنشاء مصفوفة وذلك باستخدام دالّة new كالتالي: fruits = Array.new ما فعلناه هنا هو إنشاء متغيّر باسم fruits وتعيين مصفوفة جديدة فارغة إلى ذلك المتغيّر باستخدام دالّة new. قد تتساءل، إذا كان بالإمكان تعيين مصفوفة إلى متغيّر فهل يمكن استخدام متغيّر كعنصر في مصفوفة؟ نعم يمكن ذلك ويكون نوع العنصر هو نوع البيانات الموجودة في المتغيّر، جرّب إنشاء متغيّر ثم ضعه في المصفوفة كعنصر كما فعلت سابقًا مع السلاسل، تسمّى هذه العمليّة بالاستيفاء Code Interpolation. item = "apples" fruits = %W(#{item} oranges) لاحظ أنّنا استخدمنا حرف W كبير بدلاً من الصّغير الذي استخدمناه سابقًا. المصفوفات متعددة الأبعاد يمكنك إنشاء مصفوفة تحتوي على مصفوفات أخرى بداخلها، تسمّى بالمصفوفة متعدّدة الأبعاد Multidimensional Array. يعد هذا النوع من المصفوفات مفيدًا لإنشاء مستوى إحداثيّات. يمكن إنشاء مصفوفة متعدّدة الأبعاد هكذا: [[1, 3], [5, 7]] الوصول إلى عنصر أو عدة عناصر بالمصفوفة تعدّ المصفوفات أحد أدوات روبي المهمّة. وتمتلك المصفوفات بعض الدوال المفيدة للوصول إلى عنصر معيّن بها. للتعرف على بعض تلك الدوال سنقوم بإنشاء مصفوفة بقائمة المشتريات تحتوي على 5 عناصر. لنقم بإنشاء متغيّر باسم list وتعيين المصفوفة التي نريد إلى ذلك المتغيّر. list = %w(apples oranges milk bread sugar) للوصول إلى قيمة معيّنة في المصفوفة نكتب مكان وجود هذه القيمة في أقواس مربّعة، مكان تواجد القيمة يسمّى دليل index. إذًا إذا أردت أن أجد العنصر الموجود في المكان الأوّل من المصفوفة نكتب اسم المصفوفة، الأقواس المربّعة ورقم الدليل بداخلها هكذا: list[1] ولكن ماذا أعادت لنا روبي عند تنفيذ هذا؟ أعرف أنّك لم تتوقّع أن تعيد orangesبدلاً من apple. لماذا حدث ذلك لأنّ الترقيم في المصفوفات يبدأ من 0 وليس 1 فإذا أردنا الوصول إلى أوّل عنصر على الإطلاق في المصفوفة نكتب ذلك كالتالي: list[0] القيم السالبة يستخدم نظام الترقيم الخاص بالمصفوفات قيمًا سالبة أيضًا وذلك إذا أردنا بدء العدّ من آخر المصفوفة. إذًا إذا أردت الوصول إلى آخر عنصر في المصفوفة يمكنك استخدام -1. list[-1] هناك أيضًا دوال مدمجة جاهزة للوصول إلى أوّل وآخر عنصر في المصفوفة. list.first list.last دالة fetch يمكن استخدام دالّة fetch مع الدليل لإرجاع قيمة العنصر الموجود بهذا الدليل. list.fetch(2) # "milk" إذا تم كتابة معطى ثاني في الدّالّة ولا يوجد هناك العنصر الذي يشير إليه الدليل المحدّد فسيتمّ إرجاع القيمة الموجودة في المعطى الثّاني كقيمة افتراضيّة. مثال: list.fetch(20, "Not found") # "Not found" المصفوفة الجزئية يمكننا أيضًا إرجاع مصفوفة جزئيّة Subset Array من مصفوفة عن طريق كتابة معطى Parameter إضافي مع الدليل. يشير هذا المعطى إلى طول المصفوفة الجزئيّة التي نريد إرجاعها. إذًا إذا أردنا إرجاع أوّل ثلاثة عناصر في المصفوفة نكتب ذلك كالتالي: list[0, 3] # ["apples", "oranges", "milk"] طريقة أخرى لإرجاع مصفوفة جزئيّة هو استخدام مجال معيّن range. list[0..2] # ["apples", "oranges", "milk"] دالة include لمعرفة إذا كانت مصفوفة تحتوي على كائن معيّن نستخدم دالّة include والتي تستقبل معطى باسم الكائن الذي نريد التحقّق من وجوده. وحيث أنّ الدّالّة منطقية (بمعنى أنّها ترجع true أو false فقط) فإنّنا نكتب في نهايتها علامة استفهام، ?include. إذًا لو أردنا معرفة إذا كانت تحتوي مصفوفة list على سلسلة apples نكتب ذلك كالتالي: list.include?("apples") معرفة عدد عناصر المصفوفة إذا أردت معرفة كم عدد العناصر بالمصفوفة فيمكن استخدام دالّة size والتي تقوم بإرجاع عددًا صحيحًا يمثّل عدد العناصر الموجودة بالمصفوفة. list.size اسم آخر لدالّة size هو length. جرّب كتابة الأمر التالي ولاحظ كيف تمّ إرجاع نفس النتائج. list.length إضافة عناصر إلى المصفوفة دالة push ماذا لو أردنا إضافة شيء إلى المصفوفة؟ يمكننا إضافة سلسلة تحتوي على "cheese" إلى نهاية المصفوفة باستخدام دالّة push. تقوم الدّالّة بإلحاق السلسلة التي نريد إضافتها بنهاية المصفوفة. list.push("cheese") هناك عامل اختصار Shorthand Operator لفعل نفس الوظيفة وهي أقواس الزاوية المزدوجة Double Angle Brackets (<<). فمثلاً إذا أردنا إضافة سلسلة "juice" إلى مصفوفة list بطريقة أسرع نكتب ذلك هكذا: list << "juice" أسهل وأسرع بكثير من دالّة push أليس كذلك؟ والأمر اللّطيف أيضًا حول ذلك أن هذه الأسهم تشير إلى مصفوفة list كما لو كانت تخبر روبي بإضافة السلسلة إلى تلك المصفوفة. طريقة أخرى لإضافة عناصر إلى آخر المصفوفة هو استخدام معامل =+ والذي يعني أن تجعل المصفوفة تساوي نفسها (=) بالإضافة إلى (+) القيمة الموجودة في الطرف الأيمن. list += ["bananas", "cereals"] الشيفرة البرمجيّة أعلاه تطلب من روبي بأن تضيف السلسلتين "bananas" و"cereals" إلى نهاية المصفوفة list. دالة unshift إذا أردنا إضافة عنصر إلى بداية المصفوفة وليس نهايتها فيمكننا استخدام دالّة unshift والتي تعمل عمل push مع فرق أنّها تضيف العنصر إلى بداية المصفوفة. list.unshift("carrots") حذف عناصر من المصفوفة دالة pop يمكننا استخراج آخر عنصر من المصفوفة باستخدام دالّة pop. لنقل مثلاً أنّني قد غيّرت رأيي ولا أريد شراء العصير. يمكن إخراج العصير وهو العنصر الأخير في المصفوفة هكذا: list.pop كما تلاحظ لا نحتاج إلى معطيات لهذه الدّالّة فهي تقوم آليًّا بإخراج العنصر الأخير في المصفوفة. بعد تنفيذ الأمر في سطر أوامر روبي التفاعليّ ستلاحظ أنّ روبي قامت بإرجاع قيمة العنصر المحذوف ولم ترجع المصفوفة نفسها. هذه الميزة مفيدة إذا أردنا استخدام العنصر الأخير/المحذوف. لكن لا تستخدم هذه الدّالّة للوصول إلى آخر عنصر إذا لم تكن تريد حذفه فعلاً حيث أنّك لو تحقّقت من المصفوفة من جديد ستجد أنّ العنصر الأخير قد اختفى. دالة shift دالّة shift مشابهة لدالّة unshift والتي تضيف عنصر إلى بداية الدّالّة، الفرق الوحيد هو أنّ دالّة shift تقوم بإرجاع وحذف العنصر الأوّل من المصفوفة. أو بعبارة أخرى، هي تقوم بنفس عمل دالّة pop لكن على بداية المصفوفة. list.shift دالة drop يمكننا استخدام دالّة drop لحذف عدد من العناصر من مصفوفة معًا. المعطى الخاصّ بالدّالّة هو عدد العناصر المطلوب حذفها من بداية الدّالّة. list.drop(2) # تقوم بحذف عنصرين من بداية الدّالّة دالة !slice قد تتساءل ماذا لو أردت حذف عناصر ليست في بداية أو نهاية المصفوفة. يمكننا في هذه الحالة استخدام دالّة !slice. تأخذ دالّة !slice المعطى الأول هو رقم الدليل المطلوب البدء منها والمعطى الثّاني هو عدد العناصر المطلوب حذفها. list.slice!(0, 3) لاحظ أنّه في حال ما إذا لم نضف علامة التّعجّب في نهاية اسم الدّالة فإننا سنحصل على جزء من المصفوفة مثلما هو مُتوقّع، لكنّه لن يتم حذف العناصر من المصفوفة الأصلية دالة sort يمكننا ترتيب مصفوفة باستخدام دالّة sort. الأمر التالي سيؤدّي إلى ترتيب المصفوفة استنادًا إلى الحروف الأبجديّة: list.sort ستلاحظ أنّه تمّ إرجاع المصفوفة وعناصرها مرتّبة أبجديًّا. ولكن لم يغيّر ذلك شيئًا في ترتيب المصفوفة الأصلي، يمكنك التحقّق من ذلك بنفسك. فقط اكتب اسم المصفوفة واضغط Enter. ستجد أنّ المصفوفة الأصليّة لم تتغيّر. ولكن إذا أردت ترتيب المصفوفة وتغيير المصفوفة الأصليّة ماذا نفعل؟ يمكننا الآن الرجوع إلى دوال Bang التي تحدّثنا عنها في درس السلاسل والتي تقوم بتعديل القيمة الأصليّة للكائن ونستدعيها عن طريقة كتابة اسم الدالّة الأصليّة متبوعة بعلامة تعجّب. list.sort! دالة reverse يمكننا استخدام دالّة reverse من أجل طباعة المصفوفة معكوسة. هذه المصفوفة أيضًا لا تغيّر من قيمة المصفوفة الأصليّة للأبد، ولكن إن أردنا تغيير المصفوفة الأصليّة فلدينا دالّة reverse!. list.reverse دالة join دالّة أخيرة سنتعرّف عليها هي دالّة join. هذه الدّالّة مفيدة جدًّا لأنّه يمكن استخدامها لإنشاء سلسلة تقوم بدمج جميع عناصر مصفوفة معًا. تستقبل الدّالّة معطى يحتوي على الرّمز الذي تريد استخدامه كفاصل. مثلاً الأمر التالي: list.join(",") ماذا فعلنا هنا؟ لقد استدعينا دالّة join على مصفوفة list ومعطى الدّالّة هو سلسلة تحتوي على فاصلة والتي ستستخدم كفاصل بين عناصر المصفوفة. عند تنفيذ هذا الأمر ستطبع لك روبي على الشّاشة سلسلة تحتوي على جميع العناصر وبين كل عنصر والآخر الفاصلة التي أردنا استخدامها. خاتمة تعرّفنا في هذا الدّرس على أحد المفاهيم المهمّة في روبي وأيّ لغة برمجة عمومًا وهي المصفوفات. مع تعمّقك أكثر في تعلّم روبي ستلاحظ فوائد استخدام المصفوفات في شيفراتك البرمجيّة. تعرّفنا على الدوال الرئيسيّة المستخدمة مع المصفوفات ولكن إذا أردت معرفة معلومات أكثر عن ذلك يمكنك قراءة التوثيق الخاص بالمصفوفات في روبي.
  16. تعرّفنا في الدّرس السّابق على جداول التّقطيع Hash وكيفيّة التعامل معها والدوال الخاصّة بها. سنتعرّف في هذا الدّرس على نوع آخر من المجموعات Collections وهو المجالات Ranges. تعدّ المجالات شكلًا آخر من المصفوفات Arrays التي تحدّثنا عنها في الدّرس الخامس إلا أنّها متسلسلة على نحوٍ محدّد وأسهل بكثير من حيث الإنشاء عن المصفوفات. أمثلة على المجالات، الأعداد من 0 لـ 9، الأيّام من السبت إلى الجمعة وغيرها من الأمثلة التي تقدّم مجالات محدّدة ومتسلسلة. إنشاء مجال سنتعرّف الآن على كيفيّة إنشاء المجالات في سطر أوامر روبي التفاعليّ. يمكن استخدام المجال لتمثيل متسلسلة من القيم بترتيب معيّن. ربّما تكون قد لاحظت استخدامنا للمجالات في دروس سابقة مثلما فعلنا عند البحث عن مصفوفة جزئيّة Subset من مصفوفة أخرى في درس المصفوفات. أوّلاً إحدى طرق إنشاء مجال هي نفس الطريقة التي استخدمناها لإنشاء مصفوفات وجداول التّقطيع من قبل عن طريق استخدام دالّة new. numbers = Range.new(1, 10) انتبه إلى أنّ دالّة new في حالة المجالات يجب أن تستقبل معطيات تمثّل بداية ونهاية المجال ولا يمكن إنشاء مجال فارغ مثلما كان ممكنًا إنشاء مصفوفات أو جداول تجزئة فارغة. جرّب كتابة الأمر السّابق مع عدم إعطاء روبي بداية ونهاية المجال، ماذا حدث؟ طبعت روبي رسالة خطأ Error تخبرك بأنّ عدد المعطيات خاطئة حيث أنّك زوّدت الدّالّة بلا شيء رغم انتظارها لمعطيين أو ثلاثة. المعطى الثالث exclude_end وهو لإخبار روبي هل عليها تضمين نهاية المجال بالمجال أم لا. إذا لم نعطي روبي ذلك المعطى الثّالث أو كانت قيمته false فستقوم روبي بتضمين النهاية بالمجال، أمّا في حالة غير ذلك فسيتمّ عدم تضمين النهاية. يمكنك إنشاء مجال عن طريقة الفصل بين بداية المجال ونهايتها باستخدام نقطتين كالتّالي: numbers = 1..10 سيقوم الأمر السّابق بإنشاء مجال من كلّ الأرقام الموجودة بين 1 و 10 مشتملة على 1 و 10 أيضًا وتعيينها إلى المتغيّر numbers. على الجانب الآخر فإنّه إذا استخدمنا ثلاث نقاط بدلاً من اثنتين ستقوم روبي بإنشاء مجال من العناصر من 1 إلى 9 مع تجاهل 10. 1...10 إذا فنستخدم النقطتان إذا أردنا أن يشتمل المجال على نقطة النّهاية وثلاث نقاط إذا لم نرده ذلك. يسمّى النّوع الأوّل Inclusive والثّاني Exclusive. يمكننا أيضًا إنشاء مجال محدّد من الأحرف. فمثلاً يمكننا إنشاء مجال للأبجديّة الإنجليزيّة مكوّن من الأحرف الصغيرة Lower Case ثمّ تعيينها إلى المتغيّر alphabet عن طريق كتابة الشيفرة البرمجيّة أدناه: alpahabet = "a".."z" إذًا لدينا المتغيّر alphabet والذين قمنا بتعيين المجال إليه. ومجالنا المحدّد هنا هو الأحرف من a إلى z مع تضمين z في المجال. الوصول إلى قيم بالمجال لاحظ كيف قامت روبي في كلّ مرّة بإرجاع المجال بالشّكل التي أُنشئت عليه دون طباعة عناصر المجال. هذا هو أحد الأمور المثيرة للاهتمام حول المجالات، فمتى أردت إظهار المجال حتّى ولو باستخدام الأمر puts فلن تحصل على شيء غير الهيئة التي أنشأت المجال عليها. قد تتساءل ولكن ماذا لو أردت استخدام عناصر المجال؟ للأسف لا يمكنك الوصول إلى العناصر أو القيم الموجودة في المجال مثلما كان بإمكانك مع المصفوفات والتجزئات. فمثلاً إذا أردت معرفة ما هو الحرف الموجود في الموقع الثّاني من المجال وكتبت [alphabet[2 مثلما كنت تفعل مع المصفوفة ستقوم روبي بإرجاع رسالة خط إليك. لأنّ هذه الدّالّة ليست دالّة للوصول إلى قيم مفردة في المجال. ولكن لحسن الحظ فهناك دالّة تسمّى to_a والتي تعني To Array. يمكن لهذه الدّالّة تحويل المجال الخاصّ بنا إلى مصفوفة ومن ثمّ يمكننا الوصول إلى العناصر بسهولة. alphabet_array = alphabet.to_a إذًا الآن لدينا المتغيّر alphabet_array والذي سيحتوي على المصفوفة الجديدة الناتجة عن تحويل المجال alphabet إلى مصفوفة باستخدام دالّة to_a. لاحظ كيف قامت روبي بإرجاع مصفوفة تحتوي على جميع الأحرف من a إلى z على هيئة عناصر من نوع سلسلة String. الآن يمكنك استخدام جميع الدوال الخاصّة بالمصفوفات والتي من بينها الوصول إلى العناصر. alphabet_array[2] ستقوم روبي بعد تنفيذ هذا الأمر بإرجاع "c". هذا لأنّه كما ذكرنا سابقًا أنّ المصفوفات تقوم بالعدّ بدءًا من 0 وليس 1. إذًا فباستخدامنا 2 فإنّنا قد قمنا بالبحث عن العنصر الثالث في المصفوفة. لمعرفة إذا كان المجال يحتوي على قيمة معيّنة أو لا يمكننا استخدام الدّالّة المنطقيّة ?include. numbers.include?(5) # true ستقوم روبي بإرجاع true حيث أنّ المجال (قمنا بإنشائه سابقًا في البداية) يحتوي بالفعل على القيمة 5. فأنت هنا تسأل روبي هل القيمة 5 موجودة في المجال numbers؟ لمعرفة إذا كان المجال الخاصّ بنا يتضمّن نهاية المجال مع القيم أم لا يمكننا فعل ذلك عن طريق دالّة ?exclude_end حيث تقوم بإرجاع true إذا لم يشتمل المجال على النّهاية وfalse إذا كان العكس. numbers.exclude_end? استخدامات المجالات تستخدم المجالات في كثير من الأحيان في المقارنة أو التكرار. مثال على المقارنة، التحقّق من وجود قيمة معيّنة في مجال محدّد من القيم. مثال على التكرار، تنفيذ حلقة Loop لفعل أمر ما 10 مرّات. يمكن إنشاء مجال باستخدام أيّ نوع من البيانات ولكن في أغلب الأحيان ستجد نفسك تستخدم المجالات من نوع السلاسل أو الأرقام فقط. السبب وراء ذلك هو قدرة روبي على ترتيب القيم في هذين النّوعين. فمثلاً، من السّهل إنشاء مجال 1 إلى 10، التحقّق من ما إذا كانت قيمة معيّنة موجودة في هذا المجال وأيضًا من السّهل طباعة كلّ رقم في المجال. الأمر ذاته مشابه مع السلاسل. تعرّفنا سابقًا أنّه بإمكاننا إنشاء مجال من الأرقام الصحيحة Integer من 0 إلى 10. الأمر الجيّد أيضًا أنّه بإمكاننا إنشاء مجالات من الأعداد العشريّة Float فمثلاً لو أردنا إنشاء مجال لنظام المعدّل التراكمي GPA فيمكننا إنشاؤه كالتّالي: gpa = 0.0..4.0 الآن إذا أردنا معرفة إذا كانت قيمة عشريّة معيّنة موجودة في هذا المجال أو لا (لنفرض مثلاً 2.5) فيمكننا فعل ذلك باستخدام دالّة ?include كالتّالي: gpa.include?(2.5) تطبيق عملي لا يبدو مثال المعدّل التراكمي السّابق مفيدًا كثيرًا، أليس كذلك؟ يمكننا جعله أكثر إفادة بتحديد مجال من 0 إلى 2 بحيث يكون هذا المجال هو مجال الإنذار كالتّالي: gpa_warning = 0.0..2.0 الآن لنطبّق ما تعلّمناه في درس العبارات الشرطيّة يمكننا إنشاء برنامج لاستقبال المعدّل التراكمي للطالب ومن ثمّ معالجة البيانات وطباعة معلومات عنها. افتح ملفًّا جديدًا واحفظه باسم gpa.rb أو أيّ اسم من اختيارك مع التأكّد من وجود rb. في نهاية اسم الملفّ. لنبدأ أوّلاً بطباعة التعليمات الخاصّة بالبرنامج، سنطلب من المستخدم إدخال المعدّل التراكمي الخاصّ به واستقبال المعدّل التراكمي كالتّالي: gpa_warning = 0.0...2.0 puts "Please Enter your GPA (0.0 to 4.0)" gpa_entry = gets.to_f في السّطر الأوّل قمنا بتعريف متغيّر باسم gpa_warning وقمنا بتعيين المجال 0 إلى 2 إليه، حيث هذا المجال هو مجال التنبيه لأيّ طالب حاصل على معدّل تراكمي بين 0 و2. بعد ذلك في السطر الثّاني طبعنا رسالة على الشّاشة تسأل المستخدم أن يدخل قيمة المعدّل التراكمي الخاصّ به بين 0 و 4. في السطر الثّالث أنشأنا متغيّر باسم gpa_entery وهو المتغيّر الذي ستقوم روبي بحفظ القيمة التي سيدخلها المستخدم إلى البرنامج به. أمر gets يتيح للمستخدم إدخال بيانات، دالّة to_f تستخدم لتحويل المُدخلات Inputs من سلاسل إلى أرقام عشريّة، حيث أنّ أيّ مدخل يتمّ اعتباره افتراضيًّا أنّه سلسلة (اسم الدّالّة نفسها هو اختصار لـ get string). قد تتساءل هنا، لماذا لم نضِف دالّة chomp إلى gets لحذف السطر الذي تضيفه روبي إلى المدخل عند الضغط على Enter؟ الإجابة هي أنّ السطر الزائد يتمّ حذفه بالفعل عند تحويل المُدخل إلى عدد عشري. الآن لدينا ما نحتاج للحصول على مُدخل من المستخدم. الجزء الثّاني من البرنامج هو أن نقوم بالتعامل مع هذه البيانات بحيث تقوم بطباعة شيء محدّد بناءً على ما يدخله المستخدم. if (gpa_entry < 0.0 || gpa_entry > 4.0) puts "False entery. Please enter a numberic value between 0 and 4." elsif (gpa_warning.include?(gpa_entry)) puts "Your GPA is too low! You need to do something about it." else puts "Congratulations! Your GPA is good. No warnings." end ماذا فعلنا هنا؟ أوّلاً، عبارة if الأولى تقوم بالتحقّق إذا كان ما أدخله المستخدم أصغر من 0 أو أكبر من 4 وهذه حالات غير متوقّعة في المعدّل التراكمي. إذا كان المُدخل يحقّق أحد الشرطين سيقوم البرنامج بإخبار المستخدم أنّ عليه إدخال رقم ما بين 0 و 4. عبارة elsif تتحقّق هل يحتوي المجال الموجود في المتغيّر gpa_warning على قيمة المتغيّر gpa_entery والذي هو القيمة التي أدخلها المستخدم. إذا تحقّق شرط العبارة فهذا يعني أن المعدّل التراكمي الذي تمّ إدخاله سيكون بين 0 و2 وفي هذه الحالّة سيقوم البرنامج بطباعة رسالة التنبيه للمستخدم. في حالة إذا كان المعدّل التراكمي غير ذلك، أي أنّه ليس أقل من 0 أو أكبر من 4. وأيضًا ليس بين 0 و2 فسيقوم البرنامج بإخبار المستخدم أنّ معدّله التراكمي جيّد وأنّه لا توجد أيّ تنبيهات له. الآن إذا قمت بتجربة البرنامج ستجده يعمل بشكلٍ جيّد ولكن ماذا يحدث عندما تُدخل سلسلة نصيّة؟ يقوم البرنامج بطباعة رسالة التنبيه بأنّ المعدّل التراكمي منخفض، أعلم أنّك لم تتوقّع ذلك. تقوم دالّة t_f بتحويل ما نستدعيها عليه إلى عدد عشري وإذا كان المُدخل هو نصّ عادي فتقوم بتحويل قيمته إلى 0.0 وهي القيمة الموجودة لدينا بالفعل في المجال. والآن حان دورك، هل يمكنك حلّ هذه المشكلة بحيث أنّه عند إدخال المستخدم أحرفًا وقيمًا ليست بالأرقام يطبع له البرنامج أنّ عليه إدخال أرقام؟ خاتمة تعرّفنا في هذا الدّرس والدّروس الماضية على أنواع المجموعات المختلفة في روبي مثل المصفوفات، جداول التّقطيع والمجالات. يمكنك قراءة توثيق روبي حول المجالات لمعرفة المزيد عنها. جرّب تحسين البرنامج الموجود بقسم التطبيق العملي بأن يقوم بطباعة بيانات أخرى حول المعدّل التراكمي بناءً على إدخال المستخدم وموقع هذا المعدّل بالنسبة للمجال المحدّد. إذا كنت تودّ إضافة معلومة عن المجالات أو واجهتك مشاكل أو لديك تساؤل حول الدّرس فلا تتردّد في السؤال بقسم التعليقات أدناه.
  17. سنتعرّف في هذا الدّرس على العبارات الشرطيّة وكيفيّة استخدامها لتنفيذ أوامر أو تجاهلها استنادًا إلى شرط معيّن. افتح ملفّ روبي جديد وسمّه logic.rb أو أيّ اسم آخر مع التأكّد من كتابة rb. في آخر اسم الملفّ واكتب الشيفرات البرمجيّة التّالية والتي تمثّل نسخة مجرّدة من لعبة إلقاء النّرد: number = rand(1..6) puts "You rolled #{number}" ماذا فعلنا هنا؟ أنشأنا متغيّرًا وحفظنا به قيمة عشوائيّة بين 1 و 6، ثمّ استعملنا هذا المتغيّر لطباعة القيمة العشوائيّة على الشّاشة باستخدام أمر puts مخبرين المستخدم أنّ ناتج إلقائه للنرد هو ذلك الرقم العشوائي النّاتج عن استخدام دالّة rand (التي سبق وأن اطّلعنا عليها في الدّرس السّابق). سنتعرّف الآن على بعض العبارات الشرطيّة التي يمكننا استخدامها لإخراج بعض المعلومات عن الرقم استنادًا إلى ما إذا كان الشّرط متحقّقًا أم لا. اكتب السطر التّالي أسفل الشيفرات البرمجيّة السّابقة: puts "You rolled the highest number possible" if number == 6 تلاحظ في العبارة الشرطيّة السّابقة أنّ الشرط هو أن يكون المتغيّر number يساوي 6. إذا تحقّق هذا الشرط سنقوم بإخراج رسالة "You rolled the highest number possible". الأمر المهم لدينا هنا هو عبارة if التي تأتي قبل الشّرط الذي يجب أن يكون صحيحًا من أجل طباعة السّلسلة، في حالتنا هذه كما ذكرنا فالشّرط هو أن يكون المتغيّر مساويًا لـ 6. لاحظ كيف استخدمنا علامتي == لاختبار المساواة بين الطرفين. يختلف هذا عمّا فعلنا في بداية الشيفرات حيث استخدمنا علامة مساواة واحدة لتعيين قيمة للمتغيّر. إذًا فنستخدم علامة يساوي واحدة لتعيين القيم وعلامتين من أجل اختبار إذا كان طرفين متساويين أم لا. يمكننا أيضًا استخدام بنية مشابهة لذلك لمعرفة إذا كان الشّرط غير متحقّق كالتّالي: puts "You didn't roll the lowest number possible" if number != 1 كما تلاحظ فالجملة مشابهة إلى حدٍّ كبير، نريد طباعة هذه السلسلة إذا كان المتغيّر number لا يساوي (!=) 1. سيتم طباعة تلك السلسلة فقط عن تحقّق هذا الشرط. في الحالتين السّابقتين لدينا الجملة الشّرطيّة موجودة بالنهاية والشيفرة البرمجيّة التي نريدها أن تنفّذ موجودة في البداية. يمكننا كتابة ذلك بترتيب مختلف بأن نضع الجملة الشّرطيّة في البداية كالتّالي: if number < 5 then puts "You rolled a number less than 5" end كما تلاحظ، نحن نخبر روبي إذا كان قيمة المتغيّر أقل من 5 يجب تنفيذ الأمر الموجود بعد الكلمة المفتاحيّة then. وبالنّهاية نضع end والتي نحتاج إليها لإخبار روبي بأنّ عبارة if قد انتهت. كتابة أكثر من شرط يمكننا أيضًا كتابة أكثر من شرط في أكثر من سطر. مثال على ذلك: if number.even? puts "You rolled and even number" else puts "You rolled an odd number" end السّطر الأوّل يتحقّق إذا كان المتغيّر number عددًا زوجيًّا باستخدام دالّة even، إذا كانت الإجابة true سيتمّ تنفيذ أمر puts الخاصّ بهذا الشرط. إذا كانت قيمة المتغيّر عددًا غير زوجي، فمن البديهي أنه سيكون فرديًّا. إذًا فإنّ else تعدّ بمثابة ما نريد تنفيذه إذا لم يتحقّق الشرط. لاحظ أنّه يجب وضع end مرّة واحدة في النهاية تمامًا وليس بعد if مرّة وبعد else مرّة. elsif يمكننا كتابة عدد أكبر من الجمل الشرطيّة أيضًا باستخدام elsif. if number == 1 then puts "You rolled one" elsif number == 2 then puts "You rolled two" elsif number == 3 then puts "You rolled three" else puts "You rolled a number bigger than three" end لدينا في هذا المثال ثلاثة جمل شرطيّة مختلفة متبوعة بجملة else والتي ستُنفّذ إذا لم يتحقّق أيّ شرط من الشّروط الثلاثة السّابقة لها. لنبدأ مع أول سطر، جملة if عاديّة تتحقّق إذا كانت قيمة المتغيّر number تساوي 1، إذا كان الجواب نعم فستنفّذ روبي الأمر الموجود بعد then وتستبعد باقي الجمل الشرطيّة. أمّا إذا لم تتحقّق الجملة الشرطيّة الأولى فتنتقل روبي إلى السطر التّالي والذي يحتوي على جملة شرطيّة أخرى والتي تختبرها روبي فقط إذا لم تتحقّق الجملة الشرطيّة السّابقة، لذلك فهي تسمّى elsif. تعمل تمامًا elsif عمل if، الفرق فقط هو أنّها لا تنفّذ في حالة تحقّق الجملة الشرطيّة السّابقة لها. السطر الثالث مشابه للسطر الثّاني. وبالنهاية لدينا else والتي لا تحتوي على شرط محدّد وإنّما تنفّذ في حال عدم تحقّق أي جملة شرطيّة من الثلاث. ثم end. عبارة Case يمكننا استخدام عدد لا نهائي من جمل if, elsif, else ولكن مع زيادى عدد الشروط المطلوبة قد يصبح الأمر متعبًا ومستهلكًا للوقت. لذلك فهناك عبارة أخرى يمكننا استخدامها وهي case. حاول تخمين ما تفعله case بالاطّلاع على الشيفرات التالية: case number when 4 then puts "You rolled four" when 5 then puts "You rolled five" when 6 then puts "You rolled six" else puts "You rolled a number less than four" end كما تلاحظ، فهي تعمل بشكل مشابه جدًّا للمثال السّابق. الفرق أنّنا نستخدم case بدلاً من if. لاحظ أنّنا نضع بالبداية المتغيّر number حيث أنّ كل الجمل الشّرطيّة الموجودة بـ case تشير إلى هذا المتغيّر. على سبيل المثال بالنّظر إلى السطر الثاني، فهو يشير أنّه عندما (when) تكون قيمة المتغيّر number تساوي 4 إذًا (then) ننفّذ أمر puts. وهكذا حتى الوصول إلى else ثم end في نهاية عبارة case واللذان يستخدمان تمامًا مثل استخدامهما في عبارة if. سلسلة عبارات if يمكننا أيضًا وضع جمل if كمتسلسلات كما ترى أدناه: if number == 2 or number == 3 or number == 5 puts "You rolled a prime number" end استخدمنا جملة if عاديّة ونخبر روبي بأنّه إذا كانت قيمة المتغيّر number تساوي 2، أو (or) تساوي 3، أو (or) تساوي 4 فكما قد تكون توقّعت سيتمّ تنفيذ الأمر الخاصّ بتلك العبارة. إذًا فنحن في الواقع نختبر ثلاث شروط مختلفة وإذا تحقّق شرط واحد منها فإنّ ناتج هذه العبارة الشّرطيّة سيصبح true. هناك طريقة أخرى لكتابة تلك الشيفرات. puts "You rolled a square number" if number == 1 || number == 4 يسمّى || برمز الأنبوب المزدوج (Double Pipe Symbol) والذي يمثّل كلمة or. لاحظ أيضًا كيف قمنا بعكس الجملة الشرطيّة وذلك بوضع الشرط في النهاية كما فعلنا سابقًا. هناك أيضًا عبارة and والتي تتحقّق إذا تحقّقت جميع الشروط الموجودة والتي مربوط بينها باستخدام and. if number.odd? and number >= 4 puts "You rolled five" end في الشيفرات البرمجيّة أعلاه نتحقّق إذا كانت قيمة المتغيّر هي عدد فردي و (and) أكبر من أو تساوي 4. هذا الرّمز >= يعني أنّ الطرف الأيسر من الجملة أكبر من أو يساوي الطرف الأيمن. بالرجوع مجدّدًا إلى الشيفرة البرمجيّة السّابقة فإنّه عند تحقّق الشرطين معًا (وليس أحدهما فقط) فسيتمّ تنفيذ الأمر puts الذي يلي العبارة الشرطيّة. مثل or، هناك أيضًا طريقة أخرى لكتابة and. if number.even? && number <= 3 puts "You rolled two" end الشيفرة أعلاه مشابهة تمامًا للسّابقة، نتحقّق إذا كانت قيمة المتغيّر عددًا زوجيًّا و (&&) أقل من أو تساوي 3. إذا تحقّق الشّرطيّن المربوط بينها باستخدام && فسيتمّ تنفيذ أمر puts الخاصّ بالعبارة الشرطيّة. رمز && يسمّى Double Ampersand Symbol. ختام تعرّفنا في هذا الدّرس على العبارات الشرطيّة في ruby وكيفيّة استخدامها. إذا كتبت الشيفرة البرمجيّة المستخدمة في هذا الدّرس في ملفّ روبي فعلاً فجرّب تنفيذها في سطر أوامر روبي التفاعليّ كما تعلّمنا في الدّروس السّابقة وقارن النتائج بالشيفرات الموجودة لمعرف وظيفة كل جزء منها.
  18. سنتعرّف في هذا الدّرس على كيفية التّعامل مع الأرقام في روبي. سنقوم في البداية بالتعامل مع العمليّات الحسابيّة من خلال سطر أوامر روبي التفاعليّ. ثمّ سنتعرّف بعد ذلك على الأعداد الصحيحة Integers والدوال الخاصّة بها. كما سنتعرف أيضًا على كيفية توليد أرقام عشوائيّة في روبي. الأرقام لنلق الآن نظرة على استخدام الأرقام في سطر أوامر روبي التفاعليّ. افتح الطرفيّة وابدأ جلسة IRB جديدة عن طريق كتابة أمر irb. يمكننا كتابة عمليّة جمع كالتالي: 3 + 5 علامة الجمع في الأمر السّابق هي عبارة عن دالّة خاصّة بالرقم 3 (الذي يعدّ كائنًا، كما ذكرنا سابقًا أن روبي تتعامل مع أي شيء على أنّه كائن له دوالّ خاصّة به). قد تتساءل كيف لعلامة + أن تكون دالّة وقد تعرّفنا أن استدعاء الدوال يتم عن طريق إضافة نقطة ثم اسم الدّالّة بعد الكائن. دعني أخبرك أنّه يمكن بالفعل تنفيذ الدّالّة + بهذه الطريقة. ولأنّنا نريد إضافة 5 إلى 3 فإنّنا نريد إضافة ما يسمّى بالمعطى Argument والذي يوضع بين قوسين بعد اسم الدّالّة. لذلك فيمكن إعادة كتابة الأمر السّابق هكذا: 3.+(5) إذا كتبت الأمرين ستحصل على نفس النّتائج. إذًا فرقم 3 هو الكائن الذي يستدعي الدّالّة، علامة الجمع هي اسم الدّالّة وبين القوسين نجد المعطى هو رقم 5. ولأنّه عند كتابة عمليّة الجمع بهذه الطريقة يبدو الأمر صعبًا بعض الشيء، فهذا الاستدعاء لا يبدو كعمليّة الجمع المألوفة لنا. لذلك فإنّ روبي تسمح لنا باستخدام الدّالّة بالصورة الأولى والتي تبدو أسهل في القراءة والاستخدام لإتمام عمليّات الجمع. يسمّى هذا بالتجميل اللّغوي Syntactic Sugar. لذلك فإن الطريقة الأولى لإتمام عمليّة الجمع هي الأكثر شيوعًا عن التنويت النقطي Dot Notation. لدى روبي الكثير من العمليّات الحسابيّة الأخرى. مثل الطرح: 10 – 4 الضرب: 4 * 8 القسمة: 9 / 2 إذا كنت تنفّذ الأوامر السّابقة في سطر أوامر روبي التفاعليّ ستلاحظ أنّ ناتج عمليّة قسمة 9 على 2 هو 4، وهذا ليس صحيحًا فحاصل قسمة 9 على 2 هو 4.5 وليس 4، أليس كذلك؟ حدث ذلك لأنّ 9 و2 هي أرقام صحيحة ممّا يعني أنّ الناتج سيكون أيضًا الجزء الصحيح فقط من خارج القسمة وهو في هذه الحالة 4 بدلاً من 4.5. للحصول على الناتج الصحيح لهذه العمليّة نحتاج إلى استخدام نوع آخر من كائنات الأرقام يسمّى بالعدد العشري Float. لاستخدام عدد عشري بدلاً من عدد صحيح كل ما علينا فعله هو على سبيل المثال كتابة 9.0 بدلاً من 9. جرّب إعادة كتابة عمليّة القسمة السّابقة باستخدام 9.0. ستجد أنّ روبي قد أعادت النّاتج 4.5. دالتا even وodd تستخدم روبي دالتين لمعرفة ما إذا كان العدد زوجيًّا أم فرديًّا. على سبيل المثال اكتب الأمر التّالي ولاحظ النّتيجة: 62.even? بعد الضغط على Enter ستطبع روبي على الشّاشة true ردًّا على سؤالنا لها هل العدد 62 عددًا زوجيًّا؟. يمكننا أيضًا استخدام دالّة odd للتحقّق ممّا إذا كان العدد 62 زوجيًّا أم فرديًّا هكذا: 62.odd? تحدّثنا في الدّرس السّابق عن دوال Bang وكيف أنّها تنتهي بعلامة تعجّب. دالّتي even و odd هما مثالين للدوال الشرطيّة وهي الدوال التي ترجع true أو false وغالبًا ستجد علامة استفهام بنهاية تلك الدوال والتي تجعل من السّهل تمييز هذا النّوع. دالة GCD يمكن لروبي تنفيذ بعض العمليّات الحسابيّة اللّطيفة أيضًا. على سبيل المثال يمكن إيجاد القاسم المشترك الأكبر لعددين (وهو أكبر عدد يقسم في نفس الوقت العددين معًا بدون أيّ باقي قسمة) باستخدام دالّة gcd. إذًا لو أردت معرفة القاسم المشترك الأكبر لكلٍّ من 12 و20 فيمكن كتابة هذا كالتّالي: 12.gcd(20) وستكون النتيجة 4 دالة LCM دالّة lcm مشابهة نوعًا ما لدالّة gcd وتقوم بإيجاد المضاعف المشترك الأصغر لعددين (وهو أصغر عدد موجب صحيح مضاعف لكلا هذين العددين، أي أنّه يمكن قسمة هذا المضاعف المشترك الأصغر على العددين بدون باقي قسمة). إذا أردت معرفة المضاعف المشترك الأصغر لكل من 15 و20 يمكن كتابة هذا كالتّالي: 15.lcm(20) وستكون النتيجة 60 دالة to_s آخر دالّة خاصّة بالأعداد الصحيحة التي سنتطرّق إليها هي دالّة to_s والتي هي اختصار لـ to string. يمكنك من اسمها تخمين وظيفتها، تقوم الدّالّة بتحويل عدد صحيح إلى سلسلة حرفيّة تحتوي على هذا الرقم. على سبيل المثال، الشيفرة البرمجيّة التالية تقوم بتحويل العدد الصحيح 7 إلى سلسلة "7": 7.to_s بعد الضغط على Enter سيتم إرجاع القيمة هكذا "7". نعرف أنّ القيمة نوعها سلسلة من علامات الاقتباس المحيطة بها. الآن يمكننا التّعامل مع هذا الرقم على أنّه سلسلة وتنفيذ دوال السّلاسل عليه. دالة to_i دالّة to_i مشابهة لدالّة to_s ولكنّها خاصّة بالسّلاسل، تقوم الدّالّة بتحويل السلسلة إلى عدد صحيح. لذلك فيمكننا كتابة "42" وتحويلها إلى عدد صحيح هكذا: "42".to_i سنلاحظ بعد تنفيذ الأمر إرجاع قيمة صحيحة عبارة عن 42 وستختفي علامات الاقتباس المحيطة بالعدد. بذلك يمكننا استخدام الدوال الخاصّة بالأعداد الصحيحة على 42، مثل الجمع والطّرح. عليك توخّي الحذر عند التّعامل مع هذه الدّالّة، حيث أنّه في حالة عدم احتواء السلسلة على أيّة أرقام فسوف تقوم بإرجاع قيمة 0. على سبيل المثال اكتب الأمر التّالي ولاحظ الناتج: "Hello World".to_i أمّا في حالة تنفيذ الدّالّة على سلسلة تبدأ بقيمة عدديّة فستقوم الدّالّة بتحويل وإرجاع الرقم الموجود في أوّل السلسلة فقط. مثال على ذلك: "2016 Jan".to_i دالة Rand يُعدّ توليد أرقام عشوائيّة أحد الأمور المفيدة في البرمجة وله استخدامات عديدة، يمكن فعل ذلك في ruby بسهولة عن طريق استخدام دالّة rand. باستخدام الدّالّة يمكننا توليد عدد عشري بين 0 و 1.0. كلّ ما عليك فعله هو كتابة rand في سطر أوامر روبي التفاعليّ والضّغط على Enter، جرّب كتابة الأمر أكثر من مرّة ولاحظ كيف تختلف القيمة في كلّ مرّة ولكنّها لا تزيد عن 1.0 ولا تقلّ عن 0. ولكن ماذا إذا أردت الحصول على أرقام عشوائيّة صحيحة؟ إذا أضفت عددًا صحيحًا للدّالّة كمُعطى فستقوم الدّالّة بتوليد عددًا عشوائيًّا صحيحًا بين 0 والعدد المعطى ولكن العدد المعطى لن يكون ضمن تلك الأعداد العشوائيّة. فمثلاً إذا أعطيت الدّالّة رقم 6 ستقوم بتوليد رقمًا عشوائيًّا بين 0 و 5: rand(6) ماذا لو أردت البدء من 1 بدلاً من 0؟ توفّر روبي طريقة لطيفة لفعل ذلك. كلّ ما علينا فعله هو إدخال مجال range معيّن من القيم كمُعطى للدّالّة فنكتبها هكذا: rand(1..6) الأمر السّابق يخبر روبي بأن تختار رقمًا عشوائيًّا بين 1 و 6، متضمّنة 1 و 6 أيضًا ضمن الأرقام العشوائيّة. يذكّرنا هذا بلعبة إلقاء النّرد، أليس كذلك؟ خاتمة تعرّفنا في هذا الدّرس على بعض المفاهيم المهمّة في روبي مثل الأرقام والدوال الخاصّة بها وكيفيّة تحويل رقم إلى سلسلة والعكس. هدف هذا الدّرس هو إعطاؤك لمحة سريعة عن العمليات التي يُمكن تنفيذها على الأرقام ومبدأ عمل ذلك. بطبيعة الحال لم نستعرض سوى عدد محدود من الدّوال. بإمكانك الاطّلاع على باقي الدّوال عبر زيارة التّوثيق الرّسمي للغة وخاصة صفحة الأرقام الصّحيحة.
  19. تعرّفنا في الدّرس السّابق على لغة روبي ووميزاتها. في هذا الدّرس سوف نستعرض بعض أنواع البيانات في Ruby، وسنتعرّف باستفاضة على نوع السلاسل. كما سنتعرّف على أحد أهمّ سمات أيّ لغة برمجة، ألا وهي المتغيّرات. سنستخدم في الجزء الأول من الدّرس سطر أوامر روبي التفاعليّ. افتح الطرفيّة وابدأ جلسة irb عن طريق كتابة أمر irb والضغط على زر Enter. القيم والأنواع القيمة هي أبسط شيء يتعامل معه البرنامج مثل حرف أو رقم. تعرّفنا في الدّرس السّابق على بعض تلك القيم. تختلف القيمة 2 عن القيمة "Hello World"، حيث أن الأولى هي عدد صحيح Integer أمّا الثّانية فهي من نوع سلسلة String، جاءت التسمية من كونها سلسلة من الأحرف. يمكنك التعرّف بسهولة على السلاسل من خلال علامات الاقتباس المحيطة بالقيمة. هناك نوع آخر يسمّى Float وهو أيّ رقم يحتوي على علامة عشريّة، مثلاً: 2.5، 13.6، 4.0. ملحوظة: قد تعتقد أن "12" و"1.5" هي أرقام وتتعامل معها في برنامجك على ذلك فتنفّذ عليها عمليّات حسابيّة وتكتشف بعد ذلك أنّ مفسّر روبي يظهر لك علّة برمجية Bug. سبب ذلك أنّها ربّما تكون أرقامًا بالنسبة لك ولكن المفسّر يعتبرها سلاسل نصيّة حيث أنّها محاطة بعلامات الاقتباس والمفسّر مُبَرْمَج على تصنيف أيّ شيء بين علامات اقتباس على أنّه سلسلة. تريد التأكد من ذلك بنفسك؟ جرّب جمع "12" + "1.5". أو بطريقة أخرى يمكنك استخدام الدّالة المدمجة is_a لسؤال روبي هل هذه القيمة نوعها Integer مثلاً وستردّ عليك روبي true أو false. فمثلاً إذا أردت التأكّد من نوع قيمة معيّنة مثل 12 اكتب الأمر التالي: 12.is_a?(Integer) الآن للعودة إلى ما إذا كان كل من "12" و"1.5” أرقامًا أم سلاسل، جرّب الأوامر التالية (تأكّد من كتابة أوّل حرف بشكل كبير Capital): "12".is_a?(Integer) "1.5".is_a?(Float) أحيانًا عند كتابة أعداد كبيرة قد تجد نفسك محتاجًا إلى إرفاق فواصل لجعل العدد قابلاً للقراءة أكثر، مثلاً 1,000,000. لو فعلت ذلك في روبي ستحصل على خطأ. ولكن يمكنك فعل ذلك مع الأمر puts. مثلاً يمكن كتابة: puts 1,000,000 ماذا كانت النتيجة؟ لم تتوقعها أليس كذلك؟ قامت روبي قامت بطباعة: 1 0 0 رقم 1 و 0 و 0 في أسطر مختلفة. حدث ذلك لأنّ روبي قد فسّرت هذا العدد على أنّه ثلاثة أرقام مستقلّة ومفصول بينها بفواصل. في روبي يمكن استخدام الأمر puts لطباعة أكثر من قيمة في أكثر من سطر عن طريق الفصل بين تلك القيم بفاصلة. فمثلاً لو أردت طباعة "Hello" في سطر وبعدها "World" في سطر باستخدام أمر روبي واحد يمكن استخدام الأمر التالي: puts "Hello", "World" Hello World السلاسل السلاسل في روبي هي مجموعات من الأحرف. يمكن إنشاء سلسلة نصيّة عن طريق كتابة أحرف ونصوص داخل علامتي اقتباس مزدوجة. مثلاً لكتابة سلسلة تحتوي على نصّ Hello Ruby اكتب الأمر التّالي: "Hello Ruby" يمكن استخدام علامة الاقتباس المفردة أيضًا لإنشاء سلسلة في روبي، لذلك فيمكن إنشاء السلسلة السّابقة كالتّالي: 'Hello Ruby' إذا أردت استخدام أحد نوعي علامات الاقتباس في داخل نصّك فعليك استخدام النّوع الآخر لإنشاء السلسلة فمثلا لو أردت إنشاء سلسلة نصيّة بها I'm learning Ruby، واستخدمت علامة الاقتباس المفردة لإنشاء السلسلة سيؤدّي ذلك إلى حدوث خطأ ولن يتمّ تنفيذ الأمر. لذلك فالطريقة الصحيحة لكتابة هذه السلسلة هو استخدام علامة الاقتباس المزدوجة: "I'm learning Ruby" مثال آخر: puts '"Never memorize something that you can look up." -Albert Einstein' خيار آخر لاستخدام علامات الاقتباس في السلاسل يسمّى هروب علامة الاقتباس escaping، يحدث هذا عن طريق وضع خط مائل عكسي Backslash قبل علامة الاقتباس والذي سيؤدّي إلى اعتبارها حرفًا عاديًّا بدلاً من تنفيذها كوسيلة لإنشاء سلسلة. يمكن إذًا كتابة السلسلتين السّابقتين كالتّالي: 'I\'m learning Ruby" "\"Never memorize something that you can look up.\" -Albert Einstein" ذكرنا في الدّرس الأوّل أنّ كل شيء في روبي هو كائن Object. يمكن للكائنات تنفيذ أفعال يُطلق عليها اسم دوال. هناك الكثير من الطرق لجعل كائن ينفّذ دالّة ولكن الطريقة الأكثر شيوعًا هي التنويت النُقطي Dot Notation وهو عبارة عن أن تكتب الكائن (في مثالنا هذا الكائن هو السلسلة "Hello Ruby") متبوع بنقطة ثم اسم الدّالة التي تريد استدعاءها. لنجرّب استدعاء الدّالة length والتي هي دالّة مدمجة في روبي تقوم بإرجاع طول السلسلة أو بمعنى آخر عدد الأحرف الموجودة في السلسلة: "Hello Ruby".length المتغيرات أحد أقوى السمات في أيّ لغة برمجة هي القدرة على التعامل مع المتغيّرات. المتغيّر ببساطة هو عنوان (أو "اسم") يشير إلى قيمة معيّنة. بمعنى آخر فالمتغيّر هو طريقة لحفظ الكائنات في الذّاكرة من أجل الاستخدام لاحقًا. يمكن في روبي تعيين متغيّر إلى كائن بسهولة باستخدام عامل التساوي (Equals Operator =): name = "Sara" في هذا المثال، اسم المتغيّر يشير إلى السلسلة "Sara". يعني هذا أنّه بإمكاننا استدعاء دوال مختلفة مباشرةً على اسم المتغيّر بدلاً من كتابة السلسلة في كلّ مرة أردنا استدعاء دالّة. الآن إذا أردت استدعاء الدّالة length لحساب طول السلسلة يمكن ببساطة كتابة ذلك كالتّالي: name.length لنجرّب الآن بعض دوّال روبي التي يمكن استخدامها مع السّلاسل في روبي (السّلاسل عبارة عن كائنات في روبي) على هذا المتغيّر. على سبيل المثال، دالّة reverse ستقوم بطباعة السلسلة معكوسة: name.reverse دوال upcase و downcase من الدوال الخاصّة بالسلاسل أيضًا دالّتا upcase و downcase. تقوم دالّة upcase بتحويل كل الأحرف الموجودة في السلسلة النصيّة إلى أحرف كبيرة Capital Letters. أمّا دالّة downcase فتحوّل كل الأحرف الموجودة في السلسلة إلى أحرف صغيرة Small Letters. جرّب استدعاء الدّالتين على المتغيّر name: name.upcase name.downcase هناك عدد كبير جدًا من الدوال الخاصّة بالسلاسل يمكنك التعرّف على جميعها عن طريق استخدام دالّة methods (نعم هذا هو اسمها) على المتغيّر الخاصّ بك، هكذا: name.methods عند تنفيذ الأمر ستقوم روبي بعرض جميع الدوال المختلفة التي يمكن لهذا النوع من الكائنات استخدامها. يمكنك أيضًا معرفة أكثر عن تلك الدوال عن طريق قراءة التوثيقات الخاصّة بها. دوال Bang ربّما لاحظت أن جميع الدوال التي استخدمناها حتّى الآن لم تغيّر شيئًا في السلسلة، فمثلاً إذا أردتُ معرفة القيمة الموجودة في المتغيّر name من جديد سأجد أن القيمة ما زالت كما عيّنتها من البداية (لمعرفة قيمة متغيّرك فقط اكتب اسم المتغيّر واضغط Enter أو استخدم puts إن أردت). لم تتغيّر القيمة وتصبح السلسلة معكوسة ولا جميع الحروف كبيرة أو صغيرة. لكن ماذا لو أردت فعلاً تغيير تلك القيمة؟ سنجرّب بعض الدوال التي تقوم بالفعل بتغيير قيمة السلسلة. أنشئ متغيّرًا باسم fruit واجعله يساوي السلسلة "apple". يمكن تغيير قيمة ذلك المتغيّر عن طريق استدعاء دوال تسمّى بدوال Bang، تنتهي تلك الدوال بعلامة التعجّب أو ما يسمّى برمز Bang. فمثلاً لو أردت تنفيذ الدالة reverse على السلسلة الخاصّة بك وحفظ القيمة الجديدة بدلاً من القيمة الأصليّة يمكن كتابة ذلك ببساطة هكذا: fruit.reverse! عندما نقوم بطباعة المتغيّر بعد ذلك نلاحظ تغيّر القيمة إلى elppa بدلاً من القيمة السّابقة apple. بهذا نكون قد استغنينا تمامًا عن القيمة الأصليّة واستبدلناها بالقيمة الحاليّة. معظم الدوال الخاصّة بالسلاسل لديها دالّة Bang مماثلة. على سبيل المثال هناك أيضًا دالّة Bang مقابلة لدالّة upcase العاديّة. إذًا فيمكننا كتابة: fruit.upcase! سيطبع هذا الأمر قيمة السلسلة بأحرف كبيرة وأيضًا من جديد سيتمّ استبدال القيمة السابقة بالقيمة الجديدة والتي هي ELPPA. إذا كانت هناك علامة تعجّب Bang في نهاية دالّة روبي فهذا غالبًا يعني كن حذرًا، يمكن لهذه الدالّة أن تكون خطرًا. في حالة الأمثلة السّابقة فهي تعني أنّ قيمة السلسلة ستتغيّر فيجب أن تكون حذرًا متى قمت باستخدام هذه الدوال. تسلسل الدوال قد يكون راودك تساؤل حول ماذا لو أردت تنفيذ العديد من الدوال على أحد المتغيّرات، هل يجب كتابة استدعاء الدالّة على المتغيّر لكل دالّة على حدة؟ لحسن الحظّ يمكن استدعاء أكثر من دالة في وقتٍ واحد على نفس المتغيّر. على سبيل المثال إذا عدنا من جديد إلى المتغيّر name وأردنا كتابته معكوسًا وبأحرف كبيرة، يمكن كتابة ذلك تسلسليًّا هكذا: name.reverse.upcase ولكن تذكّر دائمًا أنّه يتمّ تنفيذ الدوال بالترتيب من اليسار إلى اليمين. رغمّ أنّها لا تهمّ في حالة المثال أعلاه، فحتى إذا بدّلنا الترتيب ستكون النتيجة مشابهة. إلا أنّه يمكن أن يتغيّر النّاتج عن النّاتج الذي تتوقعه في حالات أخرى، لذلك يجب التحقّق من الترتيب عند استخدام هذه الطريقة في استدعاء الدوال. إدراج شيفرات برمجية في السلاسل يمكن بطريقة بسيطة جدًا إدراج شيفرات برمجيّة في سلاسل نصيّة وتنفيذ تلك الشيفرات. يمكن فعل ذلك عن طريق وضع الشيفرات البرمجيّة داخل حاضنات Curly Brackets مع إضافة رمز التلبيد Hash في البداية: "Hello my name is #{name}" يطبع هذا نصًا يحتوي على الجملة المكتوبة بالإضافة إلى النصّ الموجود في المتغيّر name الذي قمنا بإنشائه سابقًا. لذلك فالسلسلة السابقة ستصبح "Hello my name is Sara". ما حدث هو أنّه قد تمّ إدراج قيمة المتغيّر name بداخل السلسلة حيث أنّ علامة التلبيد مع الحاضنات تخبر روبي بأن الموجود بينها هو شيفرات يجب على روبي تنفيذها أولاً وليس طباعتها كما هي. هناك أمر مهمّ عليك تذكّره وهو أنّه عند استخدام شيفرات برمجيّة بداخل سلسلة فلا بد من استخدام علامات الاقتباس المزدوجة. إذا استخدمت علامات اقتباس مفردة فسيتمّ طباعة النصّ كما هو {Hello my name is \#{name. والخط المائل هذا يعني أنّه قد تمّ تخطي هذه القيمة وطُبعت كما هي، مثلما رأينا في السّابق مع 'I\'m'. تطبيق عملي الآن بعد التعرّف على المتغيّرات والسلاسل حان الوقت لكتابة برنامج حقيقي باستخدامهما. في هذا البرنامج سيتمّ طباعة سلسلة نصيّة تسأل المستخدم عن اسمه، ثمّ يقوم البرنامج بعد ذلك باستخدام الاسم الذي سيدخله المستخدم لتطبيق بعض الدوال التي تطرّقنا إليها. أوّلاً نحتاج لإنشاء ملفّ روبي، سمّه ما تريد ولكن لا تنس إضافة rb. إلى نهاية اسم الملفّ. لقد أسميتُ ملفّي greetings.rb. بدايةً سنحتاج إلى طباعة رسالة للمستخدم لسؤاله عن اسمه. لفعل ذلك سنستخدم الأمر puts: puts "Hello, what's your name?" بعد ذلك سنحتاج إلى تمكين المستخدم من كتابة اسمه. أمر gets أمر gets هو اختصار لـ get string والذي يقوم بإيقاف البرنامج وانتظار عمليّة إدخال من المستخدم قبل متابعة تنفيذ البرنامج. سيكون المُدخَل عبارة عن سلسلة كما هو واضح من اسم الأمر. إحدى دوال السلاسل دالّة تسمّى chomp يمكن تطبيقها على سلسلة لحذف الحرف الذي يتمّ إضافته افتراضيًّا للسلسلة بعد الضغط على Enter، حيث أنّه عند استقبال إدخال من المستخدم وبضغطه على زرّ Enter يتمّ تلقائيًّا إضافة حرف سطر جديد إلى نهاية السلسلة. إذًا لتمكين المستخدم من إدخال اسمه نحتاج إلى استخدام أمر gets للحصول على المدخل واستدعاء دالّة chomp لحذف الحرف الزائد وحفظ ناتج ذلك في متغيّر لاستخدامه في وقتٍ لاحق. نكتب ذلك في روبي كالآتي: name = gets.chomp وأخيرًا سنقوم بكتابة شيفرات برمجيّة تتعامل مع هذا المدخل وتطبع بعض المعلومات عنه، أضف ما يلي بعد الأمرين السّابقين في ملفّ روبي الخاصّ بك: puts "Hello #{name}. Your name's length is #{name.length}. That's not really long! Your name backwards is #{name.reverse.downcase.capitalize}" ليُصبح كامل الملف على النّحو التّالي: puts "Hello, what's your name?" name = gets.chomp puts "Hello #{name}. Your name's length is #{name.length}. That's not really long! Your name backwards is #{name.reverse.downcase.capitalize}" يقوم هذا الأمر بطباعة سلسلة نصيّة تحتوي على شيفرات برمجيّة تطبع اسم المستخدم، عدد الأحرف في اسم المستخدم واسم المستخدم معكوسًا. لاحظ في الجزء الثالث كيف أضفنا تسلسل دوال من ثلاث دوال، إحداها هي دالّة جديدة تسمّى capitalize. تقوم دالّة capitalize بتغيير الحرف الأوّل فقط من السلسلة وتحويله إلى حرف كبير على عكس دالة upcase التي تحوّل جميع أحرف السلسلة. احفظ الملفّ بعد كتابة الشيفرات البرمجيّة كاملة ثم قم بتنفيذه عن طريق فتح الطرفيّة والتوجّه إلى مسار المجلّد الذي قمت بحفظ الملفّ به ثم كتابة ruby متبوعًا باسم الملف (أي أنه في حالتي سأكتب الأمر ruby greetings.rb)، إذا واجهتك مشكلة في ذلك فراجع كيف قمنا بعمل ذلك في الدّرس الأوّل. خاتمة بهذا نكون قد انتهينا من الدّرس الثاني وتعرفنا على بعض المفاهيم الأساسيّة في روبي. جاء دورك للتطبيق وحدك، قارن ما يفعله البرنامج بالشيفرات البرمجيّة الخاصّة به للتعرّف أكتر على وظيفة كل جزء. طبّق أيضًا استخدام دوال أخرى لم نستخدمها في البرنامج. كذلك ربّما تريد تجربة كتابة اسمك ثنائيًّا أو ثلاثيًّا مع إضافة مسافة بين كل اسم، هل يتمّ حساب المسافة كحرف ضمن الاسم؟ ابحث في الدوال الخاصّة بالسلاسل عن سلسلة تعالج هذا الأمر وطبّقها.
  20. روبي هي لغة برمجة كائنيّة التوجّه بسيطة وقويّة في ذات الوقت، تم تطويرها في منتصف التسعينات بواسطة عالم الحاسوب Yukihiro Matsumoto والشهير باسم Matz. كان هدفه من تطوير اللغة جعل البرمجة أكثر متعةً وإنتاجيّة. تعمل اللُّغة على العديد من أنظمة التشغيل، مثل ويندوز، ماك والنسخ المختلفة من UNIX. مميزات لغة روبي مفتوحة المصدر ذات توجّهات عامّة (general purpose) كائنيّة التّوجّه ديناميكيّة ومفسّرة محمولة صيغة نظيفة مفتوحة المصدر كون اللّغة مفتوحة المصدر يعنى أنّه يوجد عدد غير منتهي من المطوّرين قائمين على اللّغة، وسرعة في معالجة الأخطاء. ذات توجهات عامة يعنى إمكانية استخدامها في أنواع مختلفة من البرامج، سواء في قواعد البيانات أو واجهات رسوميّة أو برامج علميّة أو الويب، إلى آخره. لغة كائنية التوجه الميزة هي أنّها الأسلوب الأفضل والآمن لتطوير البرمجيّات. دينامكية لن تكون مضطرًا أن تعلن عن نوع المتغير، بعكس لغات أخرى مثل Java. مفسرة بمعنى أنها تستخدم مفسّرًا وليس مترجمًا (المترجم: هو برنامج يقوم بتحويل الشيفرات البرمجيّة من لغة مثل C مثلاً إلى ملفّ تنفيذي. أمّا المفسر: هو برنامج يقوم بتنفيذ الشيفرات البرمجيّة سطرًا بعد سطر وهذا له مميزات وسلبيات. فمن المميزات هي المحموليّة على أكثر من نظام تشغيل وأكثر من بنية من العتاد. بعكس اللّغات المترجمة التي ستحتاج إلى إعادة ترجمة برنامجك كل مرّة لكل منصّة. من السلبيات، البطء وإمكانيّة الإطّلاع على الشيفرات البرمجيّة، إلّا أنّ هذا الأمر لا يعتبر مشكلة في عالم المصادر الحرة) المحمولية بمعنى أنّها مدعومة على العديد من النظم. وكذلك هي لغة بسيطة. Ruby on Rails لروبي قاعدة داعمين مخلصين في اليابان منذ بداية صدورها، إلا أنّ انطلاقتها الحقيقيّة كانت عند إطلاق David Heimmier Hansson لإطار العمل Ruby on Rails والذي يمكن اعتباره سبب شهرة روبي. يجعل إطار العمل Rails من إنشاء تطبيقات الويب عملاً سهلاً وممتعًا، وبديهيًّا فإنّ السبب وراء ذلك هو سهولة لغة روبي في الأساس. الكائنات Objects أحد أهم مبادئ روبي هي أنّ كل شيء في اللُّغة هو كائن. الكائن في البرمجة هو أي كيان له خصائصه المميّزة وأفعاله (المسمّاة دوال Methods). على سبيل المثال، اطّلع على الأسطر البرمجيّة التالية: "hello".reverse => "olleh" 6.even? => true [6,4,3,7].sort => [3,4,6,7] في السّطر الأول، كلمة hello هي كائن يمكن كتابته عكسيًّا بتطبيق الفعل/الدالّة reverse عليه. السّطر الثّاني يشير إلى إمكانيّة التحقّق ممّا إذا كان الرقم 6 (والذي هو كائن في روبي أيضًا) عددًا زوجيًّا أم لا. السّطر الثّالث يوضح أنّه يمكن ترتيب الأرقام تصاعديّا في قائمة باستخدام الدالة sort. تنصيب روبي نحتاج قبل البدء في استخدام روبي إلى تنصيبها أوّلاً. تختلف عمليّة التنصيب قليلاً باختلاف نظام التشغيل المستخدم، كذلك هناك العديد من الطرق لتنصيب روبي، اخترنا أبسطها. Windows إذا كنت تستخدم نظام التشغيل ويندوز فإنّ أفضل طريقة هي استخدام Ruby Installer، اضغط على زرّ Download وانتظر انتهاء التحميل، ثم افتح برنامج Ruby Installer واتّبع التعليمات، في أحد النوافذ سيظهر لك خيار Add Ruby executable to your path، أشّر عليه وتابع عمليّة التّنصيب مثلما تنصّب أي برنامج آخر على ويندوز. Mac إذا كنت تستخدم نظام التشغيل ماك فإنَّ روبي مثبَّتة عليه بشكل قياسي ولكن هناك احتمال كبير أنّ النسخة المثبَّتة ليست أحدث نسخة. لمعرفة نسخة روبي الموجودة لديك، افتح الطرفيّة (وذلك بالبحث عن Terminal وفتحه) واكتب الأمر التالي: ruby -v أبسط طريقة لتنصيب أحدث نسخة من روبي على نظام ماك هو استخدام مدير الحزم Homebrew. بعد تنصيب Homebrew على جهازك، اكتب الأمر التالي في الطرفيّة وسيقوم البرنامج بفعل كل شيءٍ لك: brew install ruby Linux إذا أردت إدارة نُسخ متعدِّدة من روبي على جهازك أو كنت تستخدم نظام لينكس فإنَّ الخيار الأمثل لديك هو استخدام مدير الإصدارات، هناك الكثير من هذه البرامج بما فيها (Ruby Version Manager (RVM. لكي تتمكّن من تنصيب روبي باستخدام RVM، افتح الطرفيّة واكتب الأمر التالي: \curl -L https://get.rvm.io | bash -s stable --ruby يمكنك بعد ذلك معرفة نسخ روبي المنصّبة على جهازك باستخدام أمر: rvm list بعد معرفة النسخ يمكنك تحديد النسخة التي تريدها أن تكون الافتراضيّة عن طريق كتابة أمر: rvm use [Ruby Version] –default حيث [Ruby Version] هو رقم النسخة، مثلا 2.0.0 أو غير ذلك. معرفة إصدار روبي على جهازك كما لاحظت وستلاحظ أيضًا مع تعمّقك في استخدام روبي أنّك ستحتاج في أغلب التنصيبات إلى استخدام الطرفيّة لتنفيذها. هذا الأمر شائع جدًا وستجد أنّك في كثيرٍ من الأحيان تستخدم الطرفيّة لتشغيل سكربتات وكتابة أوامر عندما تتعامل مع روبي. أتوقّع أنّك تعاملت مع الطرفيّة من قبل إذا كنت تستخدم أحد نظامي التشغيل ماك أو لينكس. ولكن على كلّ حال إذا لم تستخدم الطرفيّة من قبل فلا تقلق، ربّما تبدو صعبة في بادئ الأمر ولكن مع الوقت ستلاحظ مدى سهولتها وإنتاجيّتها. سنستخدم الآن الطرفيَّة للتحقُّق من أنّ عمليّة التنصيب تمّت بنجاح. اكتب السطر التالي: ruby -v إذا انتهى التنصيب بنجاح فسيظهر لك اسم نسخة روبي المنصّبة، كما هو ظاهر في الصورة أدناه، تخبرني الطرفيّة أنّ نسخة روبي الموجودة على حاسوبي هي ruby 2.2.3، وهي أحدث إصدار وقت كتابة هذا الدّرس. سطر أوامر روبي التفاعلي يوفّر سطر أوامر روبي التفاعلي (IRB) مجالاً لكتابة شيفرات روبي والحصول على نتائج لحظيًّة حيثُ يتمّ تنفيذ الأمر فور ضغطك على زرّ Enter. تأتي هذه الأداة مدمجة مع روبي، لذلك فلن تحتاج إلى عمليَّات تنصيب إضافيّة. سنجرّب الآن كتابة شيفرات برمجيّة بسيطة للتعرُّف على مدى سهولة اللُّغة في سطر أوامر روبي التفاعليّ والتي تعمل في الطرفيّة مباشرةً. للبدء، كل ما عليك فعله هو فتح الطرفيّة وكتابة أمر irb ثمّ الضغط على Enter. يؤدّي هذا إلى بدء جلسة IRB، أي أنّه الآن بإمكانك كتابة وتنفيذ أوامر روبي. لطباعة Hello World على الشاشة اكتب الأمر التالي: puts "Hello World" puts هو أمر في روبي وهو اختصار لـ put string والذي يطبع السلاسل النصيّة Strings. سلسلة "Hello World" هي كائن روبي يخزّن النصّ المكتوب بين علامتي التنصيص. لنجرّب استخدام الدالّة المذكورة سابقًا reverse على نص "Hello World". اكتب الأمر التالي: puts "Hello World".reverse ستلاحظ ظهور النصّ معكوسًا. النقطة الموجودة بعد السلسلة هي الطريقة التي نضيف بها الدوال إلى الكائنات في روبي. ربّما تريد أن تجرّب الدالّة على نصوص مختلفة أيضًا لفهم طريقة عملها أكثر. العمليات الحسابية يمكن لروبي التعامل مع الأرقام أيضًا فيمكننا إجراء عمليّات حسابيّة مختلفة. جرّب العمليّات الحسابيّة التالية أو عمليّات حسابيّة أخرى من اختيارك: 1 + 1 5 – 7 * 2 456549 * 45 + 23543 كما ذكرنا سابقًا فإنّ روبي تعتبر أن كلّ شيء هو كائن، فالأرقام في روبي إذًا هي كائنات لها خصائص ودوال. اكتب السطر التالي ولاحظ ما سيظهر لك: 2.even? ستجيب عليك روبي true، نعم اثنان هو رقم زوجي (even). لاحظ مدى سهولة لغة روبي. لو عرضت السطر السابق على شخص لا يعرف شيئًا على البرمجة، بنسبة كبيرة جدًا سيفهم المقصود منه. تسمّى القوائم الموجودة بين أقواس مربّعة بالمصفوفة Array. إليك مثال على مصفوفة تحتوي على بعض الأرقام: [2, 7, 4, 8] بديهيًّا فهذه المصفوفة هي كائن، وبالتّالي فهناك دوالّ خاصّة بها. لنجّرب دالّة sort. أضف .sort إلى نهاية المصفوفة: [2, 7, 4, 8].sort ماذا حدث بعد كتابة الأمر؟ تم ترتيب المصفوفة تصاعديًّا. لأنّ الدّالة sort وكما يوضّح اسمها تُرتّب المصفوفة التي تعمل معها ترتيبًا تصاعديًا. العبارات المنطقية مثال آخر على مدى سهولة ووضوح روبي، اكتب السطر التالي في جلسة IRB لديك ثم اضغط Enter: 4.times do لن يحدث شيء ولكن ستلاحظ ظهور علامة (*) بدلاً من علامة (<) بعد رقم السّطر. هذه العلامة توضّح أنّنا لا زلنا في مرحلة كتابة الشيفرات البرمجيّة، بمعنى آخر أنّ الشيفرات البرمجيّة لم تنته بعد. اكتب شيئًا مثل التّالي أو اطبع نصًّا آخر من اختيارك ثمّ اضغط Enter: puts "Ruby is so easy" نهايةً اكتب end في السطر الجديد واضغط Enter لإنهاء مرحلة الإدخال. ربّما يمكنك تخمين ماذا تفعل هذه الشيفرات البرمجيّة قبل حتّى تشغيلها. نحن ببساطة نخبر روبي أن تفعل أمرًا لعددٍ من المرّات. في هذا المثال، نطلب من اللُّغة طباعة النصّ الموجود بين علامتي التنصيص 4 مرّات. إذا لم تفهم ما المقصود تمامًا في الفقرة السّابقة فالمطلوب أن نكتب الأسطر التالية (سطرًا بعد الآخر): 4.times do puts "Ruby is so easy" end كتابة برنامج روبي الآن وبعد أن رأينا كيف تعمل روبي في سطر أوامر روبي التفاعليّ فقد حان الوقت لكتابة روبي وحفظها في ملفّ. افتح محرّر النصوص المفضّل لديك، لا تحتاج إلى برنامج معيّن لكتابة روبي، مجرّد محرّر نصوص بسيط يفي بالغرض. ولكن يفضّل اختيار محرّر نصوص يدعم خاصيّة تعليم الصيغة وتلوينها syntax highlighting. بعض الاقتراحات: Notepad++، Sublime Text. للبدء كل ما علينا فعله هو كتابة ما يلي في الملفّ: puts "Hello World" لاحظ أنّ هذه الشيفرة البرمجيّة مشابهة تمامًا لما كتبناه سابقًا في سطر أوامر روبي التفاعليّ، وبالتّالي فيجب أن نتوقّع نتائج مشابهة عند تشغيل البرنامج. لتشغيل البرنامج علينا أوّلاً حفظ الملفّ. أقترح عليك إنشاء مجلّد باسم learnRuby أو أيّ اسم آخر لحفظ ملفّات روبي التي سنعمل عليها في هذه السلسلة، احفظ الملف باسم hello.rb أو أيّ اسم آخر مع وجوب إضافة rb. في آخره حيث rb. هو الامتداد المستخدم لكلّ سكربتات روبي. بعد حفظ الملفّ، افتح الطرفيّة واذهب إلى المجلّد الذي قمت بحفظ الملفّ به عن طريق كتابة الأمر التالي: cd [file path] حيث file path هو مسار الملفّ. مثلاً إذا قمت بحفظ الملفّ في مجلّد learnRuby في C على ويندوز فالمسار سيكون C:\learnRuby وبالتّالي سيكون الأمر: cd C:\learnRuby بعد التوجّه إلى المجلّد الذي يحتوي على الملفّ نقوم بتشغيل البرنامج، كل ما علينا فعله هو كتابة: ruby hello.rb بعد تنفيذ الأمر ستلاحظ ظهور النصّ المكتوب، نفس العمليّة التي قمنا بها في سطر أوامر روبي التفاعليّ. الفرق الوحيد بين كتابة روبي في ملف وبين كتابتها في سطر أوامر روبي التفاعليّ هو أن سطر أوامر روبي التفاعليّ ينفّذ أمر روبي في كل مرّة نضغط Enter. أمّا كتابة شيفرات روبي في ملفّات يسمح لك بكتابة أكثر من سطر أوامر والتي ستُنفّذّ جميعًا عند تشغيل البرنامج. يمكننا التحقُّق من ذلك بالعودة إلى الملفّ المستخدم وإضافة أوامر أخرى. مثلاً، جرّب الأوامر التالية أسفل الأمر الموجود في الملفّ: puts 1 + 1 3.times do puts "Ruby" end احفظ الملف، وأعد تشغيله كما فعلت من قبل، ماذا تلاحظ؟ بمجرّد الضغط على Enter نحصل على نتيجة تنفيذ جميع الشيفرات البرمجيّة مرّة واحدة. التعليقات في روبي مع زيادة حجم الشيفرات البرمجيّة وتعقيدها، سيصبح من الصعوبة قراءتها وفهمها. لهذا السبب فمن المفيد إضافة ملاحظات إلى برنامجك لتوضيح وظيفة أجزاء البرنامج لك ولأي مبرمج آخر سيقرأ هذه الشيفرات فيما بعد. تسمّى تلك الملاحظات تعليقات. تبدأ التعليقات في روبي برمز التلبيد Hash (#) وكل ما يأتي بعد هذا الرمز يتمّ تجاهله ولا يُنفّذ. جرّب كتابة تعليق في ملفّ hello.rb وأعد تشغيل البرنامج. تحقّق هل حدث اختلاف في النتائج أم لا. مثال على تعليق: # هذا البرنامج يطبع حاصل ضرب الرقمين 3 * 4 puts 3 * 4 ختام تعرّفنا في هذا الدّرس على مدى بساطة روبي وقربها من الإنجليزيّة العاديّة، ما يميّزها عن لغات البرمجة الأخرى وكيف يمكن تنصيب وبدء البرمجة باستخدامها. كذلك تعرّفنا على بعض المفاهيم الخاصّة باللّغة والتي إن لم تفهمها فلا تقلق، ليس من المفترض أن تعرف كلّ هذا حيث أنّنا سنستعرض هذا كلّه باستفاضة في الدروس القادمة من هذه السلسلة. إذا استعصى عليك أمر أو واجهت مشكلة، لا تتردد في السؤال عنها في قسم التعليقات أدناه. *مصدر المعلومات: ويكيبيديا.