قم ببناء غبي ، معيد البناء الذكي: كيفية تدليك المشاكل من روبي أون ريلز كود

نشرت: 2022-03-11
كان دانيال لويس مطور Ruby on Rails محترف لأكثر من 4 سنوات ، حيث عمل على حوالي عشرة تطبيقات ويب عالية الحركة ، والعديد منها من خلال Toptal.

في بعض الأحيان ، يقدم لنا العملاء طلبات مميزة لا نحبها حقًا. ليس الأمر أننا لا نحب عملائنا - فنحن نحبهم. لا يعني ذلك أننا لا نحب الميزة — فمعظم الميزات التي يطلبها العميل تتوافق تمامًا مع أهداف أعمالهم ودخلهم.

في بعض الأحيان ، لا نحب طلب الميزة لأن أسهل طريقة لحلها هي كتابة تعليمات برمجية سيئة ، وليس لدينا حل أنيق في مقدمة رؤوسنا. سيؤدي هذا إلى إرسال العديد منا من مطوري Rails إلى عمليات بحث غير مثمرة من خلال RubyToolbox و GitHub ومدونات المطورين و StackOverflow للبحث عن جوهرة أو مكون إضافي أو رمز مثال يجعلنا نشعر بتحسن تجاه أنفسنا.

في بعض الأحيان ، لا نحب طلب الميزة لأن أسهل طريقة لحلها هي كتابة تعليمات برمجية سيئة.
سقسقة

Refactor روبي أون ريلز كود

حسنًا ، أنا هنا لأخبرك: لا بأس في كتابة تعليمات برمجية سيئة. في بعض الأحيان ، يكون من الأسهل إعادة بناء كود ريلز السيئ إلى كود جميل أكثر من حل غير مدروس جيدًا يتم تنفيذه في ظل أزمة زمنية.

هذه هي عملية إعادة هيكلة ريلز التي أحب أن أتبعها عند معالجة المشاكل من حلول ضماداتي الرهيبة:

عملية إعادة هيكلة Ruby on Rails هي العملية التي أتبعها لمعالجة مشاكل ريلز.

لمنظور بديل ، إليك سجل التزام Git لميزة تم إعادة بنائها خطوة بخطوة:

يوضح هذا السجل إعادة بناء ريلز التي تم إجراؤها خطوة بخطوة.

وإليك مقالة أخرى مثيرة للاهتمام حول إعادة بناء ديون ضخمة من زميل في شبكة Toptal.

دعونا نرى كيف يتم ذلك.

المناظر

الخطوة 1. ابدأ في "المشاهدات"

لنفترض أننا بدأنا تذكرة لميزة جديدة. يخبرنا العميل: "يجب أن يكون الزائرون قادرين على عرض قائمة بالمشاريع النشطة على صفحة الترحيب."

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

للبدء ، افترض أن لدينا نموذجًا يسمى Project بسمة منطقية تسمى active . نريد الحصول على قائمة بجميع Projects التي يكون active فيها مساويًا لـ true ، حتى نتمكن من استخدام Project.where(active: true) ، ونقوم بالتكرار فوقه مع each كتلة.

 app/views/pages/welcome.haml: %ul.projects - Project.where(active: true).each do |project| %li.project= link_to project_path(project), project.name

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

  1. يمكنك الحصول على هذا التغيير عند بدء التشغيل في أقل من 15 دقيقة.
  2. إذا تركت هذه الكتلة ، فمن السهل تخزينها مؤقتًا.
  3. يعد إصلاح مشكلة ريلز هذه أمرًا بسيطًا (يمكن إعطاؤه إلى مطور مبتدئ).

الخطوة 2. الجزئيات

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

 app/views/pages/welcome.haml: = render :partial => 'shared/projects_list' app/views/shared/projects_list.haml: %ul.projects - Project.where(active: true).each do |project| %li.project= link_to project_path(project), project.name

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

من الواضح أن إصلاح شيء غبي أسهل دائمًا من إصلاح تجريد عربات التي تجرها الدواب سيئة التنفيذ.
سقسقة

الخطوة 3. المساعدون

تعد المساعدون في ريلز طريقة لإنشاء DSL (لغة خاصة بالمجال) لقسم من طرق العرض الخاصة بك. يجب عليك إعادة كتابة التعليمات البرمجية الخاصة بك باستخدام content_tag بدلاً من haml أو HTML ، ولكن يمكنك الاستفادة من السماح لك بمعالجة هياكل البيانات دون الحاجة إلى جعل المطورين الآخرين يراقبونك لمدة 15 سطرًا من رمز العرض غير المطبوع.

إذا كنت سأستخدم مساعدين هنا ، فمن المحتمل أن أعيد تصنيع بطاقة li .

 app/views/shared/projects_list.haml: %ul.projects - Project.where(active: true).each do |project| = project_list_item(project) app/helpers/projects_helper.rb: def project_list_item(project) content_tag(:li, :class => 'project') do link_to project_path(project), project.name end end

المتحكمات

الخطوة 4. انقلها إلى وحدات التحكم

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

 app/views/shared/projects_list.haml: %ul.projects - @projects_list.each do |project| = project_list_item(project) app/controllers/pages_controller.rb: def welcome @projects = Project.where(active: true) end

الخطوة 5. مرشحات التحكم

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

 app/controllers/pages_controller.rb: before_filter :projects_list def welcome end def projects_list @projects = Project.where(active:true) end

الخطوة 6. وحدة تحكم التطبيق

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

 app/controllers/pages_controller.rb: def welcome end app/views/layouts/application.haml: %ul.projects - projects_list.each do |project| = project_list_item(project) app/controllers/application_controller.rb: before_filter :projects_list def projects_list @projects = Project.where(active: true) end

نماذج

الخطوة 7. إعادة هيكلة النموذج

كما يقول شعار MVC: نموذج الدهون ، أدوات التحكم النحيفة ، والمشاهد البكم . يُتوقع منا إعادة تشكيل كل ما في وسعنا في النموذج ، وصحيح أن معظم الوظائف المعقدة ستصبح في النهاية ارتباطات نموذجية وطرق نموذجية. يجب علينا دائمًا تجنب القيام بتنسيق / عرض الأشياء في النموذج ، ولكن تحويل البيانات إلى أنواع أخرى من البيانات مسموح به. في هذه الحالة ، فإن أفضل شيء يمكن إعادة تشكيله في النموذج هو عبارة where(active: true) ، والتي يمكننا تحويلها إلى نطاق. يعد استخدام النطاق أمرًا ذا قيمة ليس فقط لأنه يجعل المكالمة تبدو أجمل ، ولكن إذا قررنا في أي وقت إضافة سمة مثل delete أو outdated ، فيمكننا تعديل هذا النطاق بدلاً من تعقب كل عبارات where .

 app/controllers/application_controller.rb: before_filter :projects_list def projects_list @projects = Project.active end app/models/project.rb: scope :active, where(active: true)

الخطوة 8. نموذج المرشحات

ليس لدينا استخدام خاص before_save أو after_save filters في هذه الحالة ، ولكن الخطوة التالية التي أتخذها عادةً هي نقل الوظائف من أساليب التحكم والطراز إلى عوامل تصفية النموذج.

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

 app/models/project.rb: before_save :deactivate_if_over_num_views def deactivate_if_over_num_views if num_views > 50 self.active = false fi end

ملاحظة: يجب تجنب استدعاء self.save في مرشح النموذج ، لأن هذا يتسبب في أحداث حفظ متكررة ، ويجب أن تكون طبقة معالجة قاعدة البيانات في تطبيقك هي وحدة التحكم على أي حال.

الخطوة 9. المكتبات

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

من الجيد تخزين ملفات المكتبة في الدليل lib / ، ولكن مع نموها ، يمكنك نقلها إلى RubyGem حقيقي! الميزة الرئيسية لنقل التعليمات البرمجية الخاصة بك إلى مكتبة هي أنه يمكنك اختبار المكتبة بشكل منفصل عن النموذج الخاص بك.

على أي حال ، في حالة قائمة المشروع ، يمكننا تبرير نقل scope :active من نموذج Project إلى ملف مكتبة ، وإعادته إلى روبي:

 app/models/project.rb: class Project < ActiveRecord::Base include Activeable before_filter :deactivate_if_over_num_views end lib/activeable.rb: module Activeable def self.included(k) k.scope :active, k.where(active: true) end def deactivate_if_over_num_views if num_views > 50 self.active = false end end end

ملاحظة: يتم استدعاء الأسلوب self.included عند تحميل فئة نموذج ريلز وتمرر في نطاق الفئة كمتغير k .

خاتمة

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

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

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

ذات صلة: اقتطاع الطابع الزمني: حكاية ActiveRecord روبي على القضبان