كيفية إزالة الميراث جدول واحد من متراصة القضبان الخاصة بك

الميراث سهل - حتى تضطر إلى التعامل مع الديون والضرائب الفنية.

عندما ظهرت قاعدة بيانات Learn الرئيسية إلى حيز الوجود قبل خمس سنوات ، كانت الوراثة المفردة للطاولة (STI) ذات شعبية كبيرة. بدأ فريق Flatiron Labs في ذلك الوقت في استخدامه - لاستخدامه في كل شيء بدءًا من التقييمات والمناهج وحتى أحداث تغذية النشاط والمحتوى ضمن نظامنا المتطور لإدارة التعلم. وكان ذلك رائعا - لقد أنجز العمل. سمحت للمدرسين بتقديم المناهج الدراسية ، وتتبع تقدم الطالب ، وخلق تجربة مستخدم جذابة.

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

لقد عشنا في تلك المساحة لفترة من الوقت ، ونمنح هذه الشفرة رصيفًا واسعًا ، ونصححه فقط عند الضرورة. ثم حان الوقت ل refactor.

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

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

حول الميراث المنفرد (STI)

باختصار ، يتيح لك الوراثة المنفردة في Rails تخزين أنواع متعددة من الفئات في نفس الجدول. في السجل النشط ، يتم تخزين اسم الفئة كنوع في الجدول. على سبيل المثال ، قد يكون لديك Lab و Readme و Project جميعها مباشرة في جدول المحتويات:

مختبر الصف <المحتوى ؛ النهاية
الطبقة التمهيدي النهاية
مشروع فئة <المحتوى ؛ النهاية

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

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

create_table "محتوى" ، فرض:: تتالي تفعل | t |
  t.integer "curriculum_id" ،
  t. string "type" ،
  t.text "markdown_format" ،
  t. string "title" ،
  t.integer "track_id" ،
  t.integer "github_repository_id"
النهاية

تحديد نطاق العمل

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

درس صف <منهج
  has_many: المحتويات ، -> {order (ordinal:: asc)}
  has_one: content، foreign_key:: curriculum_id
  has_many: readmes، foreign_key:: curriculum_id
  has_one: lab، foreign_key:: curriculum_id
  has_one: readme، foreign_key:: curriculum_id
  has_many: assign_repos ، من خلال: محتويات
النهاية

مشوش؟ كان كذلك I. وكان ذلك مجرد نموذج واحد للكثيرين الذين كان علي تغييره.

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

تصميم جديد

كان المفهوم الذي كان يحاول المحتوى تمثيله هو وسيط بين GithubRepository والدرس.

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

لذلك توجد مستودعات GitHub على طرفي الدروس: الإصدار المتعارف عليه والإصدار المنشور.

فئة المحتوى 
class AssignedRepo 

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

لذا ، ما قررنا القيام به هو استبدال المحتوى بمفهوم جديد يسمى CanonicalMaterial ، ومنح AssignedRepo إشارة مباشرة إلى الدرس المرتبط به بدلاً من الانتقال إلى المحتوى.

قديم إلى مخطط النظام الجديد ، حيث تشير الخطوط المنقطة الحمراء إلى المسارات المحددة للإهمال

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

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

استراتيجيات لإعادة البناء والاستبدال STI

النموذج الجديد

أولاً ، أنشأنا جدولًا جديدًا يُطلق عليه المواد الأساسية وأعدنا النموذج والجمعيات الجديدة.

الفئة CanonicalMaterial 

لقد أضفنا أيضًا مفتاحًا أجنبيًا لـ canonical_material_id إلى جدول المناهج الدراسية ، بحيث يمكن أن يحتفظ الدرس بالإشارة إليه.

إلى جدول التخصيص ، أضفنا عمودًا للدرس.

يكتب المزدوج

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

فمثلا:

lesson.build_content (
  'repo_name' => repo.name ،
  'github_repository_id' => repo_id ،
  'markdown_format' => repo.readme
)

lesson.canonical_material = repo.canonical_material
lesson.save

هذا سمح لنا بوضع الأساس لإزالة المحتوى في النهاية.

ردمها

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

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

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

إستبدال

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

ما الذي تبحث عنه

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

عند إزالة STI ، هذه هي الأشياء التي يجب عليك البحث عنها:

  • الأشكال الفردية والمعدّة للنموذج ، بما في ذلك جميع فئاته الفرعية وطرقه وأساليب المنفعة والجمعيات والاستعلامات.
  • استعلامات SQL الثابت
  • التحكم
  • Serializers
  • الآراء

على سبيل المثال ، بالنسبة للمحتوى ، فهذا يعني البحث عن:

  • : المحتوى - للجمعيات والاستفسارات
  • : محتويات - للجمعيات والاستفسارات
  • .joins (: المحتويات) - لاستعلامات الانضمام ، والتي يجب أن يتم اكتشافها بواسطة البحث السابق
  • .includes (: المحتويات) - للجمعيات حريصة التحميل من الدرجة الثانية ، والتي ينبغي أيضا أن يتم البحث عنها من قبل البحث السابق
  • المحتوى: - للاستعلامات المتداخلة
  • المحتويات: - مرة أخرى ، المزيد من الاستعلامات المتداخلة
  • content_id — للاستعلامات مباشرة بواسطة المعرف
  • .المحتوى - طريقة المكالمات
  • .contents - استدعاءات طريقة التجميع
  • .build_content - طريقة الأداة المساعدة المضافة بواسطة has_one وتنتمي إلى اقتران
  • .create_content - طريقة الأداة المساعدة المضافة بواسطة has_one وتنتمي إلى اقتران
  • .content_ids - طريقة الأداة المساعدة التي أضافتها جمعية has_many
  • المحتوى - اسم الفصل نفسه
  • محتويات - سلسلة عادي لأي مراجع hardcoded أو استعلامات SQL

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

كيفية استبدال التطبيق فعليًا بعد العثور على جميع المتصلين

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

  1. استبدل سلوك الطريقة في التعريف أو قم بتغيير الطريقة في موقع الاتصال
  2. اكتب طرقًا جديدة واتصل بها خلف علامة المعالم في موقع الاتصال
  3. كسر التبعيات على الجمعيات مع الأساليب
  4. رفع الأخطاء وراء علامة الميزة إذا كنت غير متأكد من طريقة ما
  5. تبديل في الكائنات التي لها نفس الواجهة

فيما يلي أمثلة لكل استراتيجية.

1A. استبدال سلوك الأسلوب أو الاستعلام

بعض البدائل واضحة ومباشرة. يمكنك وضع علامة الميزة في مكانها ليقول "استدعاء هذا الرمز بدلاً من هذا الرمز الآخر عندما تكون هذه العلامة قيد التشغيل."

لذلك بدلاً من الاستعلام استنادًا إلى المحتويات ، فإننا هنا نستفسر استنادًا إلى المواد الأساسية.

1B. تغيير الطريقة في موقع الاتصال

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

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

2. اكتب طرقًا جديدة واتصل بها خلف علامة ميزة في موقع الاتصال

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

3. كسر التبعيات على الجمعيات مع الأساليب

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

4. رفع الأخطاء وراء علامة الميزة إذا كنت غير متأكد من طريقة ما

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

5. قم بتبديل الكائنات التي لها نفس الواجهة

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

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

اختبار واختبار يدوي

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

طرح ، اذهب لايف ، وتنظيف

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

كخطوة أخيرة ، والتي لا تزال قادمة ، يتعين علينا نسخ البيانات احتياطيًا وإسقاط جداولنا غير المستخدمة.

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

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

أيضا ، نحن نوظف! انضم لفريقنا نحن رائعون ، أعدكم.

الموارد والقراءة الإضافية

  • القضبان دليل الميراث
  • كيف ومتى تستخدم الوراثة المفردة في القضبان بواسطة يوجين وانغ (Flatiron Grad!)
  • إعادة بيع دينا القضبان التطبيقات من وراثة جدول واحد
  • وراثة جدول واحد مقابل الجمعيات متعددة الأشكال في القضبان
  • وراثة جدول واحد باستخدام القضبان 5.02

لمعرفة المزيد عن مدرسة Flatiron ، تفضل بزيارة موقع الويب ، وتابعنا على Facebook و Twitter ، وقم بزيارتنا في الأحداث القادمة القريبة منك.

مدرسة Flatiron هي عضو فخور في عائلة WeWork. تحقق من شقيقتنا التكنولوجيا بلوق WeWork التكنولوجيا وصنع Meetup.