العديد من الإضافات (plugins) تتضمن تعريفات كثيرة للفئات (classes) داخل plugin.rb، أو تستخدم require_relative لتحميل ملفات روبي (ruby). هذا يعمل، ولكنه يأتي مع بعض السلبيات:
- لا يوجد إعادة تحميل تلقائية للتغييرات أثناء التطوير. أي تغييرات تتطلب إعادة تشغيل كاملة للخادم.
- ترتيب استدعاءات
requireالصحيح يمكن أن يكون مؤلماً. - إذا تم استدعاؤها خارج كتلة
after_initialize، فقد لا تكون الفئات/الوحدات التي يتم تحميلها تلقائياً متاحة للآخرين.
هناك حل! يمكن للإضافات الاعتماد على نظام التحميل التلقائي القياسي في Rails. بالنسبة للإضافات الجديدة، كل ما تحتاجه محدد في هيكل الإضافة (plugin-skeleton). يصف هذا الموضوع كيفية تكييف إضافة موجودة وتوسيع التكوين.
1. تعريف وحدة (module) ومحرك (Rails::Engine) لإضافتك
في plugin.rb، قم بتعريف وحدة لإضافتك باسم فريد PLUGIN_NAME، وأضف سطر require_relative لتحميل ملف المحرك الذي سنقوم بإنشائه الآن.
# name: my-plugin-name
# ...
module ::MyPluginModule
PLUGIN_NAME = "my-plugin-name"
end
require_relative "lib/my_plugin_module/engine"
الآن قم بإنشاء {plugin}/lib/my_plugin_module/engine.rb:
module ::MyPluginModule
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace MyPluginModule
end
end
أمور مهمة يجب ملاحظتها:
-
في
plugin.rb، يجب تضمين::قبل اسم الوحدة لتعريفها في مساحة الاسم الجذرية (وإلا، سيتم تعريفها تحتPlugin::Instance). -
يجب أن يكون
require_relative "lib/.../engine"في الجذر الخاص بملفplugin.rb، وليس داخل كتلةafter_initialize. -
وضع المحرك في ملفه الخاص تحت
lib/أمر مهم. تعريفه مباشرة في ملفplugin.rbلن يعمل. (يستخدم Rails وجود دليلlib/لتحديد جذر المحرك). -
يجب أن يتضمن مسار الملف اسم الوحدة، وفقاً لـ قواعد Zeitwerk.
-
يُستخدم
engine_nameكبادئة لمهام الـ rake وأي مسارات (routes) محددة بواسطة المحرك (
وثائق rails). -
يساعد
isolate_namespaceفي منع تسرب الأشياء بين النواة والإضافة (
وثائق Rails).
2. تعريف ملفات روبي في هيكل الدليل الصحيح
سيقوم المحرك الآن بالتحميل التلقائي لجميع الملفات الموجودة في {plugin}/app/{type}/*. على سبيل المثال، يمكننا تعريف وحدة تحكم (controller):
{plugin}/app/controllers/my_plugin_module/examples_controller.rb
module ::MyPluginModule
class ExamplesController < ::ApplicationController
requires_plugin PLUGIN_NAME
def index
render json: { hello: "world" }
end
end
end
سيتم الآن تحميل هذا تلقائياً كلما حاول أي شيء في Rails الوصول إلى ::MyPluginModule::MyController. لاختبار الأشياء، حاول الوصول إلى تلك الفئة من وحدة تحكم rails.
لكي يعمل التحميل التلقائي بشكل صحيح، يجب أن تتطابق مسارات الملفات مع التسلسل الهرمي الكامل للوحدة/الفئة وفقاً للقواعد المحددة بواسطة Zeitwerk.
3. تعريف المسارات (routes) على محرك الإضافة
أنشئ ملف {plugin}/config/routes.rb
MyPluginModule::Engine.routes.draw do
get "/examples" => "examples#index"
# define routes here
end
Discourse::Application.routes.draw do
mount ::MyPluginModule::Engine, at: "my-plugin"
end
سيتم تحميل هذا الملف تلقائياً بواسطة المحرك، وستدخل التغييرات حيز التنفيذ دون الحاجة لإعادة تشغيل الخادم. في هذه الحالة، سيكون إجراء وحدة التحكم متاحاً عند /my-plugin/examples.json.
4. إضافة المزيد من المسارات التي يتم تحميلها تلقائياً
قد ترغب أحياناً في إدخال أدلة إضافية لملفات روبي التي يمكن تحميلها تلقائياً. المثال الأكثر شيوعاً هو دليل lib/ في الإضافة.
قم بتعديل تعريف المحرك الخاص بك لإلحاق lib/ بمسارات التحميل التلقائي للمحرك:
class Engine < ::Rails::Engine
engine_name PLUGIN_NAME
isolate_namespace MyPluginModule
config.autoload_paths << File.join(config.root, "lib")
end
الآن يمكنك تعريف وحدة مكتبة مثل
{plugin}/lib/my_plugin_module/some_lib_module.rb
module ::MyPluginModule::SomeLibModule
end
والآن أي إشارات إلى ::MyPluginModule::SomeLibModule ستقوم بتحميل الوحدة تلقائياً من هذا الملف.
5. الربح!
سيتم الآن تحميل جميع هذه الملفات تلقائياً دون أي استدعاءات require متعمدة. سيتم التقاط التغييرات تلقائياً بواسطة rails وإعادة تحميلها في مكانها دون إعادة تشغيل الخادم.
يتم التحكم في إصدار هذه الوثيقة - اقترح تغييرات على github.