دليل لمحركات السكك الحديدية في البرية: أمثلة من العالم الحقيقي لمحركات السكك الحديدية أثناء العمل
نشرت: 2022-03-11لماذا لا يتم استخدام محركات السكك الحديدية في كثير من الأحيان؟ لا أعرف الإجابة ، لكني أعتقد أن تعميم "كل شيء هو محرك" قد أخفى مجالات المشكلة التي يمكنهم المساعدة في حلها.
تشير وثائق دليل القضبان الرائعة لبدء استخدام محركات ريلز إلى أربعة أمثلة شائعة لتطبيقات محرك ريلز: Forem و Devise و Spree و RefineryCMS. هذه حالات استخدام رائعة في العالم الحقيقي لمحركات كل منها يستخدم أسلوبًا مختلفًا للتكامل مع تطبيق ريلز.
إن فحص أجزاء من كيفية تكوين هذه الأحجار الكريمة وتكوينها سيمنح مطوري Ruby on Rails المتقدمين معرفة قيمة بالأنماط أو التقنيات التي يتم تجربتها واختبارها في البرية ، لذلك عندما يحين الوقت ، يمكنك الحصول على بعض الخيارات الإضافية للتقييم على أساسها.
أتوقع أن يكون لديك إلمام سريع بكيفية عمل المحرك ، لذلك إذا شعرت أن شيئًا ما لا يضيف شيئًا ، فيرجى الاطلاع على أفضل دليل للسكك الحديدية لبدء استخدام المحركات .
بدون مزيد من اللغط ، دعنا نغامر في عالم البرية لأمثلة محركات ريلز!
فورم
محرك لـ Rails يهدف إلى أن يكون أفضل نظام منتدى صغير على الإطلاق
تتبع هذه الأحجار الكريمة اتجاه دليل السكك الحديدية على المحركات حرفياً. إنه مثال كبير وسيمنحك الاطلاع على مستودع التخزين الخاص به فكرة عن المدى الذي يمكنك من خلاله توسيع الإعداد الأساسي.
إنها جوهرة ذات محرك واحد تستخدم طريقتين للتكامل مع التطبيق الرئيسي.
module ::Forem class Engine < Rails::Engine isolate_namespace Forem # ... config.to_prepare do Decorators.register! Engine.root, Rails.root end # ... end end
الجزء المثير هنا هو Decorators.register!
طريقة الفصل ، المكشوفة بواسطة جوهرة الديكور. إنه يغلف ملفات التحميل التي لن يتم تضمينها في عملية التحميل التلقائي لريلز. قد تتذكر require
استخدام عبارات الطلب الصريحة يفسد إعادة التحميل التلقائي في وضع التطوير ، لذلك يعد هذا المنقذ! سيكون من الواضح استخدام المثال من الدليل لتوضيح ما يحدث:
config.to_prepare do Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c| require_dependency(c) end end
يحدث معظم السحر في تكوين Forem في تعريف الوحدة النمطية الرئيسية العليا لـ Forem
. يعتمد هذا الملف على متغير user_class
يتم تعيينه في ملف مُهيئ:
Forem.user_class = "User"
يمكنك تحقيق ذلك باستخدام mattr_accessor
لكن كل ذلك في دليل ريلز لذا لن أكرر ذلك هنا. مع وضع هذا في مكانه ، يقوم Forem بعد ذلك بتزيين فئة المستخدم بكل ما يحتاجه لتشغيل تطبيقه:
module Forem class << self def decorate_user_class! Forem.user_class.class_eval do extend Forem::Autocomplete include Forem::DefaultPermissions has_many :forem_posts, :class_name => "Forem::Post", :foreign_key => "user_id" # ... def forem_moderate_posts? Forem.moderate_first_post && !forem_approved_to_post? end alias_method :forem_needs_moderation?, :forem_moderate_posts? # ...
الذي تبين أنه كثير جدا! لقد قمت بقص الأغلبية لكنني تركت تعريف ارتباط بالإضافة إلى طريقة مثيل لتظهر لك نوع الخطوط التي يمكنك العثور عليها هناك.
قد يظهر لك إلقاء نظرة خاطفة على الملف بالكامل إلى أي مدى يمكن إدارة جزء من تطبيقك لإعادة استخدامه إلى محرك.
التزيين هو اسم اللعبة في استخدام المحرك الافتراضي. بصفتك مستخدمًا نهائيًا للجوهرة ، يمكنك تجاوز النموذج والعرض ووحدات التحكم عن طريق إنشاء إصداراتك الخاصة من الفئات باستخدام مسار الملف واصطلاحات تسمية الملفات الموضحة في جوهرة المصمم README. على الرغم من ذلك ، هناك تكلفة مرتبطة بهذا النهج ، خاصةً عندما يحصل المحرك على ترقية إصدار رئيسية - يمكن أن تخرج صيانة الحفاظ على عمل الديكورات الخاصة بك عن السيطرة بسرعة. أنا لا أقتبس من Forem هنا ، أعتقد أنهم ثابتون في الحفاظ على وظائف أساسية متماسكة ، لكن ضع ذلك في الاعتبار إذا قمت بإنشاء محرك وقررت إجراء إصلاح شامل.
دعنا نلخص هذا: هذا هو نمط تصميم محرك ريلز الافتراضي الذي يعتمد على المستخدمين النهائيين الذين يقومون بتزيين العروض ووحدات التحكم والنماذج ، إلى جانب تكوين الإعدادات الأساسية عبر ملفات التهيئة. هذا يعمل بشكل جيد للوظائف المركزة وذات الصلة.
ابتكر
حل مصادقة مرن لـ Rails
ستجد محركًا مشابهًا جدًا لتطبيق ريلز ، مع views
controllers
وأدلة models
. يعد Devise مثالاً جيدًا على تغليف أحد التطبيقات وكشف نقطة تكامل ملائمة. دعونا نفحص كيف يعمل هذا بالضبط.
سوف تتعرف على هذه الأسطر من التعليمات البرمجية إذا كنت مطورًا لريلز لأكثر من بضعة أسابيع:
class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable end
تمثل كل معلمة تم تمريرها إلى طريقة devise
وحدة نمطية داخل Devise Engine. هناك عشرة من هذه الوحدات مجتمعة ترث من ActiveSupport::Concern
المألوف. تعمل هذه على توسيع فئة User
الخاصة بك عن طريق استدعاء طريقة devise
في نطاقها.
إن وجود هذا النوع من نقاط التكامل مرن للغاية ، يمكنك إضافة أو إزالة أي من هذه المعلمات لتغيير مستوى الوظيفة التي تطلب من المحرك أداءها. هذا يعني أيضًا أنك لست بحاجة إلى الترميز الثابت للنموذج الذي ترغب في استخدامه داخل ملف مُهيئ ، كما هو مقترح في دليل ريلز للمحركات. بمعنى آخر ، هذا ليس ضروريًا:
Devise.user_model = 'User'
يعني هذا التجريد أيضًا أنه يمكنك تطبيق هذا على أكثر من نموذج مستخدم واحد داخل نفس التطبيق ( admin
user
على سبيل المثال) ، في حين أن نهج ملف التكوين قد يتركك مقيدًا بنموذج واحد مع المصادقة. هذه ليست أكبر نقطة بيع ، لكنها توضح طريقة مختلفة لحل المشكلة.
يقوم Devise بتوسيع ActiveRecord::Base
النمطية الخاصة التي تتضمن تعريف طريقة devise
:
# lib/devise/orm/active_record.rb ActiveRecord::Base.extend Devise::Models
أي فئة موروثة من ActiveRecord::Base
سيكون لديها الآن وصول إلى طرق الفئات المحددة في Devise::Models
:
#lib/devise/models.rb module Devise module Models # ... def devise(*modules) selected_modules = modules.map(&:to_sym).uniq # ... selected_modules.each do |m| mod = Devise::Models.const_get(m.to_s.classify) if mod.const_defined?("ClassMethods") class_mod = mod.const_get("ClassMethods") extend class_mod # ... end include mod end end # ... end end
(لقد أزلت الكثير من التعليمات البرمجية ( # ...
) لتسليط الضوء على الأجزاء المهمة.)
بإعادة صياغة الكود ، لكل اسم وحدة تم تمريره إلى طريقة devise
، فإننا:
- تحميل الوحدة التي حددناها والتي تعيش ضمن
Devise::Models
Devise::Models.const_get(m.to_s.classify
) - تمديد فئة
User
مع الوحدة النمطيةClassMethods
إذا كانت تحتوي على واحدة - قم بتضمين الوحدة النمطية المحددة (
include mod
) لإضافة أساليب المثيل الخاصة بها إلى الفئة التي تستدعي طريقةdevise
(User
)
إذا كنت ترغب في إنشاء وحدة يمكن تحميلها بهذه الطريقة ، فستحتاج إلى التأكد من أنها تتبع واجهة ActiveSupport::Concern
المعتادة ، ولكن مساحة الاسم تحت Devise:Models
لأن هذا هو المكان الذي نتطلع فيه لاسترداد الثابت:
module Devise module Models module Authenticatable extend ActiveSupport::Concern included do # ... end module ClassMethods # ... end end end end
تفو.
إذا كنت قد استخدمت اهتمامات ريلز من قبل واختبرت قابلية إعادة الاستخدام التي توفرها ، فيمكنك حينئذٍ تقدير التفاصيل الدقيقة لهذا النهج. باختصار ، يؤدي فصل الوظائف بهذه الطريقة إلى تسهيل الاختبار من خلال الاستخلاص من نموذج ActiveRecord
، وله مقدار حمل أقل من النمط الافتراضي المستخدم من قبل Forem عندما يتعلق الأمر بتوسيع الوظيفة.
يتكون هذا النمط من تقسيم وظائفك إلى مخاوف ريلز وكشف نقطة تكوين لتضمينها أو استبعادها ضمن نطاق معين. يعتبر المحرك الذي تم تشكيله بهذه الطريقة مناسبًا للمستخدم النهائي - وهو عامل مساهم في نجاح وشعبية Devise. والآن أنت تعرف كيف تفعل ذلك أيضًا!
مرح
حل كامل للتجارة الإلكترونية مفتوح المصدر لـ Ruby on Rails
بذلت شركة Spree جهدًا هائلاً للتحكم في تطبيقها المترابط مع الانتقال إلى استخدام المحركات. إن التصميم الهندسي الذي يستخدمونه الآن هو جوهرة "مرح" تحتوي على العديد من أحجار المحرك.
تنشئ هذه المحركات أقسامًا في سلوك قد تكون معتادًا على رؤيته داخل تطبيق مترابط أو منتشرة عبر التطبيقات:
- spree_api (واجهة برمجة تطبيقات مريحة)
- spree_frontend (المكونات التي تواجه المستخدم)
- spree_backend (منطقة الإدارة)
- spree_cmd (أدوات سطر الأوامر)
- spree_core (الطرز والمراسلات ، المكونات الأساسية لـ Spree التي لا يمكن تشغيلها بدونها)
- spree_sample (بيانات نموذجية)
تقوم الأحجار الكريمة الشاملة بتجميعها معًا مما يترك للمطور خيارًا في مستوى الوظائف المطلوب. على سبيل المثال ، يمكنك تشغيل محرك spree_core
ولف الواجهة الخاصة بك حوله.
تتطلب جوهرة Spree الرئيسية هذه المحركات:
# lib/spree.rb require 'spree_core' require 'spree_api' require 'spree_backend' require 'spree_frontend'
يحتاج كل محرك بعد ذلك إلى تخصيص engine_name
ومسار root
(يشير الأخير عادةً إلى جوهرة المستوى الأعلى) وتهيئة أنفسهم عن طريق التثبيت في عملية التهيئة:
# api/lib/spree/api/engine.rb require 'rails/engine' module Spree module Api class Engine < Rails::Engine isolate_namespace Spree engine_name 'spree_api' def self.root @root ||= Pathname.new(File.expand_path('../../../../', __FILE__)) end initializer "spree.environment", :before => :load_config_initializers do |app| app.config.spree = Spree::Core::Environment.new end # ... end end end
قد تتعرف أو لا تتعرف على طريقة التهيئة هذه: إنها جزء من Railtie
وهي أداة ربط تمنحك الفرصة لإضافة أو إزالة خطوات من تهيئة إطار عمل ريلز. تعتمد Spree على هذا الخطاف بشكل كبير لتهيئة بيئتها المعقدة لجميع محركاتها.

باستخدام المثال أعلاه في وقت التشغيل ، ستتمكن من الوصول إلى الإعدادات الخاصة بك عن طريق الوصول إلى المستوى الأعلى من Rails
الثابت:
Rails.application.config.spree
باستخدام دليل نمط تصميم محرك ريلز أعلاه ، يمكننا تسميته يوميًا ، ولكن Spree لديها الكثير من الأكواد المدهشة ، لذلك دعونا نتعمق في كيفية الاستفادة من التهيئة لمشاركة التكوين بين المحركات وتطبيق ريلز الرئيسي.
Spree لديها نظام تفضيلات معقد يتم تحميله عن طريق إضافة خطوة إلى عملية التهيئة:
# api/lib/spree/api/engine.rb initializer "spree.environment", :before => :load_config_initializers do |app| app.config.spree = Spree::Core::Environment.new end
هنا ، نعلق على app.config.spree
Spree::Core::Environment
جديد. داخل تطبيق القضبان ، ستتمكن من الوصول إلى هذا عبر Rails.application.config.spree
من أي مكان - النماذج ، وحدات التحكم ، طرق العرض.
بالانتقال إلى الأسفل ، تبدو فئة Spree::Core::Environment
التي أنشأناها كما يلي:
module Spree module Core class Environment attr_accessor :preferences def initialize @preferences = Spree::AppConfiguration.new end end end end
يعرض :preferences
الذي تم تعيينه على مثيل جديد من فئة Spree::AppConfiguration
، والذي بدوره يستخدم طريقة preference
المحددة في فئة Preferences::Configuration
لتعيين الخيارات مع الإعدادات الافتراضية لتكوين التطبيق العام:
module Spree class AppConfiguration < Preferences::Configuration # Alphabetized to more easily lookup particular preferences preference :address_requires_state, :boolean, default: true # should state/state_name be required preference :admin_interface_logo, :string, default: 'logo/spree_50.png' preference :admin_products_per_page, :integer, default: 10 preference :allow_checkout_on_gateway_error, :boolean, default: false # ... end end
لن أعرض ملف Preferences::Configuration
لأنه سيستغرق بعض الشرح ولكنه في الأساس عبارة عن سكر نحوي للحصول على التفضيلات وتعيينها. (في الحقيقة ، يعد هذا تبسيطًا مفرطًا لوظائفه ، حيث سيحفظ نظام التفضيلات قيمًا أخرى غير الافتراضية للتفضيلات الحالية أو الجديدة في قاعدة البيانات ، لأي فئة ActiveRecord
مع :preference
- لكنك لست بحاجة إلى ذلك إعلم أن.)
فيما يلي أحد هذه الخيارات قيد التنفيذ:
module Spree class Calculator < Spree::Base def self.calculators Rails.application.config.spree.calculators end # ... end end
تتحكم الآلات الحاسبة في جميع أنواع الأشياء في Spree - تكاليف الشحن ، والعروض الترويجية ، وتعديلات أسعار المنتج - لذا فإن وجود آلية لمبادلتها بهذه الطريقة يزيد من قابلية تمدد المحرك.
إحدى الطرق العديدة التي يمكنك من خلالها تجاوز الإعدادات الافتراضية لهذه التفضيلات هي داخل المُهيئ في تطبيق ريلز الرئيسي:
# config/initializergs/spree.rb Spree::Config do |config| config.admin_interface_logo = 'company_logo.png' end
إذا كنت قد قرأت من خلال RailsGuide on Engines ، أو نظرت في أنماط تصميمها أو أنشأت محركًا بنفسك ، فستعرف أنه من التافه كشف أداة ضبط في ملف تهيئة ليستخدمها شخص ما. لذلك قد تتساءل ، لماذا كل هذا العناء مع نظام الإعداد والتفضيل؟ تذكر أن نظام التفضيلات يحل مشكلة المجال لـ Spree. يمكن أن يساعدك الارتباط بعملية التهيئة والوصول إلى إطار عمل ريلز على تلبية متطلباتك بطريقة قابلة للصيانة.
يركز نمط تصميم المحرك هذا على استخدام إطار عمل ريلز باعتباره الثابت بين أجزائه المتحركة العديدة لتخزين الإعدادات التي لا تتغير (بشكل عام) في وقت التشغيل ، ولكنها تتغير بين عمليات تثبيت التطبيق.
إذا كنت قد حاولت من قبل وضع علامة بيضاء على تطبيق ريلز ، فقد تكون معتادًا على سيناريو التفضيلات هذا ، وقد شعرت بألم جداول "إعدادات" قاعدة البيانات المعقدة خلال عملية إعداد طويلة لكل تطبيق جديد. أنت تعرف الآن أن هناك مسارًا مختلفًا متاحًا وهذا رائع - ارتفاع خمسة!
مصفاة CMS
نظام إدارة محتوى مفتوح المصدر لـ Rails
اتفاقية على أي شخص التكوين؟ يمكن أن تبدو محركات ريلز بالتأكيد أشبه بتمرين في التكوين في بعض الأحيان ، لكن RefineryCMS يتذكر بعضًا من سحر ريلز. هذه هي المحتويات الكاملة لدليل lib
الخاص بها:
# lib/refinerycms.rb require 'refinery/all' # lib/refinery/all.rb %w(core authentication dashboard images resources pages).each do |extension| require "refinerycms-#{extension}" end
رائع. إذا كنت لا تستطيع معرفة ذلك ، فإن فريق المصفاة يعرف حقًا ما يفعلونه. إنهم يتدحرجون مع مفهوم extension
الذي هو في جوهره محرك آخر. مثل Spree ، فإنه يحتوي على جوهرة خياطة شاملة ، ولكنه يستخدم فقط غرزتين ، ويجمع مجموعة من المحركات لتقديم مجموعة كاملة من الوظائف.
يتم أيضًا إنشاء الإضافات من قبل مستخدمي المحرك ، لإنشاء مزيج خاص بهم من ميزات CMS للتدوين والأخبار والمحافظ والشهادات والاستفسارات وما إلى ذلك (إنها قائمة طويلة) ، وكلها مرتبطة بـ RefineryCMS الأساسية.
قد يجذب هذا التصميم انتباهك لنهجه المعياري ، والمصفاة هي مثال رائع على نمط تصميم ريلز هذا. "كيف يعمل؟" أسمع أنك تسأل.
يرسم المحرك core
بعض الخطافات لاستخدامها في المحركات الأخرى:
# core/lib/refinery/engine.rb module Refinery module Engine def after_inclusion(&block) if block && block.respond_to?(:call) after_inclusion_procs << block else raise 'Anything added to be called after_inclusion must be callable (respond to #call).' end end def before_inclusion(&block) if block && block.respond_to?(:call) before_inclusion_procs << block else raise 'Anything added to be called before_inclusion must be callable (respond to #call).' end end private def after_inclusion_procs @@after_inclusion_procs ||= [] end def before_inclusion_procs @@before_inclusion_procs ||= [] end end end
كما ترون ، before_inclusion
قبل after_inclusion
مجرد تخزين لقائمة العمليات التي سيتم تشغيلها لاحقًا. تعمل عملية تضمين المصفاة على توسيع تطبيقات ريلز المحملة حاليًا باستخدام أدوات التحكم في المصفاة ومساعديها. هنا واحد في العمل:
# authentication/lib/refinery/authentication/engine.rb before_inclusion do [Refinery::AdminController, ::ApplicationController].each do |c| Refinery.include_once(c, Refinery::AuthenticatedSystem) end end
أنا متأكد من أنك قد وضعت طرق المصادقة في ApplicationController
و AdminController
من قبل ، فهذه طريقة برمجية للقيام بذلك.
سيساعدنا النظر إلى بقية ملف محرك المصادقة هذا في جمع بعض المكونات الأساسية الأخرى:
module Refinery module Authentication class Engine < ::Rails::Engine extend Refinery::Engine isolate_namespace Refinery engine_name :refinery_authentication config.autoload_paths += %W( #{config.root}/lib ) initializer "register refinery_user plugin" do Refinery::Plugin.register do |plugin| plugin.pathname = root plugin.name = 'refinery_users' plugin.menu_match = %r{refinery/users$} plugin.url = proc { Refinery::Core::Engine.routes.url_helpers.admin_users_path } end end end config.after_initialize do Refinery.register_extension(Refinery::Authentication) end # ... end end
تحت الغطاء ، تستخدم ملحقات المصفاة نظام Plugin
. ستبدو خطوة initializer
مألوفة من تحليل كود Spree ، هنا فقط تلبية متطلبات طرق register
لإضافتها إلى قائمة Refinery::Plugins
التي يتتبعها الامتداد core
، ويضيف Refinery.register_extension
اسم الوحدة فقط إلى قائمة مخزنة في موصل فئة.
إليكم صادمًا: فئة Refinery::Authentication
عبارة عن غلاف حول Devise ، مع بعض التخصيص. لذلك فهي السلاحف على طول الطريق!
الإضافات والمكونات الإضافية عبارة عن مفاهيم طورتها شركة Refinery لدعم نظامها البيئي الثري لتطبيقات وأدوات السكك الحديدية الصغيرة - فكر في rake generate refinery:engine
. يختلف نمط التصميم هنا عن Spree عن طريق فرض واجهة برمجة تطبيقات إضافية حول محرك القضبان للمساعدة في إدارة تكوينها.
يعتبر مصطلح "The Rails Way" من صميم المصفاة ، وهو موجود أكثر من أي وقت مضى في تطبيقات min-rails الخاصة بهم ، ولكن من الخارج لن تعرف ذلك. يعد تصميم الحدود على مستوى تكوين التطبيق أمرًا مهمًا ، وربما أكثر من ذلك ، من إنشاء واجهة برمجة تطبيقات نظيفة للفئات والوحدات النمطية المستخدمة في تطبيقات ريلز الخاصة بك.
يعد رمز التغليف الذي لا يمكنك التحكم فيه بشكل مباشر نمطًا شائعًا ، فهو يعد تبصرًا في تقليل وقت الصيانة عندما يتغير هذا الرمز ، ويحد من عدد الأماكن التي ستحتاج إلى إجراء تعديلات لدعم الترقيات. يؤدي تطبيق هذه التقنية إلى جانب وظيفة التقسيم إلى إنشاء منصة مرنة للتكوين ، وهنا مثال من العالم الحقيقي يجلس تحت أنفك تمامًا - يجب أن تحب المصدر المفتوح!
خاتمة
لقد رأينا أربع طرق لتصميم أنماط محرك ريلز من خلال تحليل الأحجار الكريمة الشائعة المستخدمة في تطبيقات العالم الحقيقي. يجدر القراءة من خلال مستودعاتهم للتعلم من ثروة من الخبرات التي تم تطبيقها بالفعل وتكرارها. الوقوف على أكتاف العمالقة.
في دليل ريلز هذا ، ركزنا على أنماط وتقنيات التصميم لدمج محركات ريلز وتطبيقات ريلز الخاصة بالمستخدمين النهائيين ، بحيث يمكنك إضافة المعرفة بها إلى حزام أدوات ريلز الخاص بك.
آمل أن تكون قد تعلمت قدر ما تعلمت من مراجعة هذا الكود وتشعر بالإلهام لمنح Rails Engines فرصة عندما تتناسب مع الفاتورة. شكراً جزيلاً للقائمين على الصيانة والمساهمين في الأحجار الكريمة التي استعرضناها. الناس بعمل رائع!