คำแนะนำเกี่ยวกับ Rails Engines in the Wild: ตัวอย่างการใช้งาน Rails Engine ในโลกแห่งความเป็นจริง
เผยแพร่แล้ว: 2022-03-11เหตุใด Rails Engines จึงไม่ถูกใช้บ่อยขึ้น ฉันไม่รู้คำตอบ แต่ฉันคิดว่าลักษณะทั่วไปของ "ทุกอย่างคือกลไก" ได้ซ่อนขอบเขตปัญหาที่พวกเขาสามารถช่วยแก้ไขได้
เอกสารคู่มือ Rails Guide ที่ยอดเยี่ยมสำหรับการเริ่มต้นใช้งาน Rails Engines อ้างอิงสี่ตัวอย่างยอดนิยมของการใช้งาน Rails Engine: Forem, Devise, Spree และ RefineryCMS นี่เป็นกรณีการใช้งานจริงที่ยอดเยี่ยมสำหรับ Engine แต่ละเครื่องใช้วิธีการที่แตกต่างกันในการผสานรวมกับแอปพลิเคชัน Rails
การตรวจสอบส่วนต่างๆ ของการกำหนดค่าและประกอบอัญมณีเหล่านี้จะช่วยให้นักพัฒนา Ruby on Rails ขั้นสูงมีความรู้อันมีค่าเกี่ยวกับรูปแบบหรือเทคนิคใดบ้างที่ได้มีการทดลองและทดสอบในธรรมชาติ ดังนั้นเมื่อถึงเวลา คุณจะมีตัวเลือกเพิ่มเติมในการประเมิน
ฉันคาดหวังให้คุณมีความคุ้นเคยคร่าวๆ เกี่ยวกับวิธีการทำงานของ Engine ดังนั้นหากคุณรู้สึกว่ามีบางอย่างที่ไม่ค่อยลงตัว โปรดอ่าน Rails Guide ที่ยอดเยี่ยมที่สุดใน การเริ่มต้นใช้งาน Engines
เพื่อไม่ให้เป็นการเสียเวลา มาผจญภัยในโลกแห่งตัวอย่างเครื่องยนต์ Rails กันเถอะ!
โฟร์ม
เอ็นจิ้นสำหรับ Rails ที่มุ่งหวังที่จะเป็นระบบฟอรัมเล็กๆ ที่ดีที่สุดเท่าที่เคยมีมา
อัญมณีนี้เป็นไปตามทิศทางของ Rails Guide on Engines จนถึงจดหมาย นี่เป็นตัวอย่างขนาดใหญ่ และการอ่านพื้นที่เก็บข้อมูลจะทำให้คุณทราบว่าคุณสามารถขยายการตั้งค่าพื้นฐานได้ไกลแค่ไหน
เป็นอัญมณีเครื่องยนต์เดียวที่ใช้เทคนิคสองสามอย่างเพื่อรวมเข้ากับแอปพลิเคชันหลัก
module ::Forem class Engine < Rails::Engine isolate_namespace Forem # ... config.to_prepare do Decorators.register! Engine.root, Rails.root end # ... end end ส่วนที่น่าสนใจของที่นี่คือ Decorators.register! วิธีการเรียน เปิดเผยโดยอัญมณีมัณฑนากร มันห่อหุ้มไฟล์การโหลดที่จะไม่รวมอยู่ในกระบวนการโหลดอัตโนมัติของ Rails คุณอาจจำได้ว่าการใช้คำสั่งที่ชัดเจนว่า 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 ที่ถูกตั้งค่าในไฟล์ initializer:
Forem.user_class = "User" คุณทำสิ่งนี้ให้สำเร็จโดยใช้ mattr_accessor แต่ทั้งหมดนั้นอยู่ใน Rails Guide ดังนั้นฉันจะไม่ทำซ้ำที่นี่ ด้วยสิ่งนี้ 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? # ...ซึ่งกลับกลายเป็นว่าค่อนข้างเยอะ! ฉันได้ตัดส่วนส่วนใหญ่ออกแล้ว แต่ยังเหลือในคำจำกัดความการเชื่อมโยงรวมถึงวิธีการอินสแตนซ์เพื่อแสดงประเภทของบรรทัดที่คุณสามารถหาได้ในนั้น
การมองดูทั้งไฟล์อาจแสดงให้คุณเห็นว่าการพอร์ตส่วนหนึ่งที่จัดการได้ของแอปพลิเคชันของคุณเพื่อนำกลับมาใช้ใหม่กับ Engine อาจเป็นอย่างไร
การตกแต่งเป็นชื่อของเกมในการใช้งาน Engine เริ่มต้น ในฐานะผู้ใช้อัญมณี คุณสามารถแทนที่โมเดล ดู และควบคุมโดยการสร้างเวอร์ชันของคลาสของคุณเองโดยใช้พาธไฟล์และรูปแบบการตั้งชื่อไฟล์ที่วางไว้ในมัณฑนากร gem README มีค่าใช้จ่ายที่เกี่ยวข้องกับวิธีการนี้ โดยเฉพาะอย่างยิ่งเมื่อ Engine ได้รับการอัปเกรดเวอร์ชันหลัก – การบำรุงรักษาเพื่อให้การตกแต่งของคุณทำงานได้อย่างรวดเร็ว ฉันไม่ได้อ้างถึง Forem ที่นี่ ฉันเชื่อว่าพวกเขาแน่วแน่ในการรักษาฟังก์ชันการทำงานหลักที่แน่นแฟ้น แต่อย่าลืมสิ่งนี้หากคุณสร้าง Engine และตัดสินใจที่จะยกเครื่อง
มาทบทวนกันอีกครั้ง: นี่คือรูปแบบการออกแบบกลไกของ Rails เริ่มต้นโดยอาศัยผู้ใช้ปลายทางที่ตกแต่งมุมมอง ตัวควบคุม และรุ่น ควบคู่ไปกับการกำหนดค่าการตั้งค่าพื้นฐานผ่านไฟล์การเริ่มต้น วิธีนี้ใช้ได้ผลดีกับการทำงานที่เน้นและเกี่ยวข้องกันมาก
ประดิษฐ์
โซลูชันการรับรองความถูกต้องที่ยืดหยุ่นสำหรับ Rails
คุณจะพบว่า Engine นั้นคล้ายกับแอปพลิเคชั่น Rails มาก โดยมีไดเร็กทอรี views , controllers และ models ประดิษฐ์เป็นตัวอย่างที่ดีของการห่อหุ้มแอปพลิเคชันและเปิดเผยจุดรวมที่สะดวก มาดูกันว่ามันทำงานอย่างไร
คุณจะจำโค้ดเหล่านี้ได้หากคุณเป็นนักพัฒนา Rails มานานกว่าสองสามสัปดาห์:
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 ภายในขอบเขต
การมีจุดรวมประเภทนี้มีความยืดหยุ่นสูง คุณสามารถเพิ่มหรือลบพารามิเตอร์เหล่านี้เพื่อเปลี่ยนระดับของฟังก์ชันที่คุณต้องการให้ Engine ดำเนินการได้ นอกจากนี้ยังหมายความว่าคุณไม่จำเป็นต้องฮาร์ดโค้ดโมเดลที่คุณต้องการใช้ภายในไฟล์ initializer ตามที่แนะนำโดย Rails Guide on Engines กล่าวอีกนัยหนึ่งสิ่งนี้ไม่จำเป็น:
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::
Devise::ModelsDevise::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วุ้ย.
หากคุณเคยใช้ Rails' Concerns มาก่อนและประสบกับความสามารถในการนำกลับมาใช้ใหม่ได้ คุณสามารถชื่นชมข้อดีของแนวทางนี้ได้ กล่าวโดยย่อ การแยกฟังก์ชันในลักษณะนี้ทำให้การทดสอบง่ายขึ้นโดยแยกจากโมเดล ActiveRecord และมีค่าใช้จ่ายต่ำกว่ารูปแบบเริ่มต้นที่ใช้โดย Forem เมื่อพูดถึงการขยายฟังก์ชันการทำงาน
รูปแบบนี้ประกอบด้วยการแบ่งการทำงานของคุณออกเป็นข้อกังวลของ Rails และเปิดเผยจุดกำหนดค่าเพื่อรวมหรือแยกสิ่งเหล่านี้ภายในขอบเขตที่กำหนด Engine ที่สร้างขึ้นในลักษณะนี้สะดวกสำหรับผู้ใช้ปลายทาง ซึ่งเป็นปัจจัยที่ส่งผลต่อความสำเร็จและความนิยมของ Devise และตอนนี้คุณก็รู้วิธีการทำเช่นกัน!
สนุกสนาน
โซลูชันอีคอมเมิร์ซโอเพ่นซอร์สที่สมบูรณ์สำหรับ Ruby on Rails
Spree พยายามอย่างมากที่จะนำแอปพลิเคชันเสาหินของพวกเขามาอยู่ภายใต้การควบคุมโดยเปลี่ยนไปใช้ Engines การออกแบบสถาปัตยกรรมที่พวกเขาใช้อยู่ในขณะนี้คืออัญมณี "สนุกสนาน" ที่มีอัญมณีเครื่องยนต์จำนวนมาก
เครื่องมือเหล่านี้สร้างพาร์ติชันในลักษณะการทำงานที่คุณอาจเคยเห็นภายในแอปพลิเคชันแบบเสาหินหรือกระจายไปทั่วแอปพลิเคชัน:
- spree_api (RESTful API)
- spree_frontend (ส่วนประกอบที่ผู้ใช้เผชิญ)
- spree_backend (พื้นที่ผู้ดูแลระบบ)
- spree_cmd (เครื่องมือบรรทัดคำสั่ง)
- spree_core (รุ่น & Mailers ส่วนประกอบพื้นฐานของ Spree ที่ไม่สามารถทำงานได้หากไม่มี)
- spree_sample (ข้อมูลตัวอย่าง)
อัญมณีที่ล้อมรอบสิ่งเหล่านี้เข้าด้วยกันทำให้นักพัฒนามีทางเลือกในระดับการทำงานที่ต้องการ ตัวอย่างเช่น คุณสามารถเรียกใช้ spree_core Engine และใส่อินเทอร์เฟซของคุณเองได้
Spree gem หลักต้องการเครื่องมือเหล่านี้:
# 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 และเป็นเบ็ดที่ให้โอกาสคุณในการเพิ่มหรือลบขั้นตอนออกจากการเริ่มต้นของเฟรมเวิร์ก Rails Spree อาศัยเบ็ดนี้อย่างหนักในการกำหนดค่าสภาพแวดล้อมที่ซับซ้อนสำหรับเครื่องยนต์ทั้งหมด

จากตัวอย่างด้านบนขณะใช้งานจริง คุณจะสามารถเข้าถึงการตั้งค่าของคุณได้โดยการเข้าถึงค่าคงที่ Rails ระดับบนสุด:
Rails.application.config.spreeด้วยคำแนะนำรูปแบบการออกแบบเครื่องยนต์ Rails ด้านบนนี้ เราสามารถเรียกได้ว่าเป็นวันเดียว แต่ Spree มีโค้ดที่น่าทึ่งมากมาย ดังนั้น มาดูวิธีที่พวกเขาใช้การเริ่มต้นเพื่อแชร์การกำหนดค่าระหว่าง Engines และแอปพลิเคชัน Rails หลัก
Spree มีระบบการกำหนดลักษณะที่ซับซ้อนซึ่งโหลดโดยเพิ่มขั้นตอนลงในกระบวนการเริ่มต้น:
# api/lib/spree/api/engine.rb initializer "spree.environment", :before => :load_config_initializers do |app| app.config.spree = Spree::Core::Environment.new end ที่นี่ เรากำลังแนบอินสแตนซ์ Spree::Core::Environment เข้ากับ app.config.spree ภายในแอปพลิเคชัน rails คุณจะสามารถเข้าถึงสิ่งนี้ผ่าน 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 ไม่ว่าจะเป็นค่าขนส่ง โปรโมชัน การปรับราคาผลิตภัณฑ์ ดังนั้นการมีกลไกในการแลกเปลี่ยนในลักษณะนี้จะเพิ่มความสามารถในการขยายของเครื่องยนต์
หนึ่งในหลายวิธีที่คุณสามารถแทนที่การตั้งค่าเริ่มต้นสำหรับการตั้งค่าเหล่านี้ได้อยู่ภายในตัวเริ่มต้นในแอปพลิเคชัน Rails หลัก:
# config/initializergs/spree.rb Spree::Config do |config| config.admin_interface_logo = 'company_logo.png' endหากคุณได้อ่าน RailsGuide on Engines โดยพิจารณาถึงรูปแบบการออกแบบหรือสร้าง Engine ด้วยตัวเอง คุณจะรู้ว่าการเปิดเผย setter ในไฟล์ initializer นั้นเป็นเรื่องเล็กน้อย ดังนั้นคุณอาจสงสัยว่าทำไมต้องยุ่งยากกับระบบการตั้งค่าและการกำหนดค่าตามความชอบ? โปรดจำไว้ว่า ระบบกำหนดลักษณะจะแก้ปัญหาโดเมนสำหรับ Spree การเข้าร่วมกระบวนการเริ่มต้นและการเข้าถึงเฟรมเวิร์กของ Rails สามารถช่วยให้คุณตอบสนองความต้องการของคุณในรูปแบบที่บำรุงรักษาได้
รูปแบบการออกแบบเอ็นจิ้นนี้เน้นที่การใช้เฟรมเวิร์ก Rails เป็นค่าคงที่ระหว่างชิ้นส่วนที่เคลื่อนไหวจำนวนมากเพื่อจัดเก็บการตั้งค่าที่ไม่ (โดยทั่วไป) เปลี่ยนแปลงขณะรันไทม์ แต่จะเปลี่ยนแปลงระหว่างการติดตั้งแอปพลิเคชัน
หากคุณเคยพยายามไวท์เลเบลแอปพลิเคชัน Rails คุณอาจคุ้นเคยกับสถานการณ์การกำหนดค่าตามความชอบนี้ และรู้สึกถึงความเจ็บปวดของตาราง "การตั้งค่า" ฐานข้อมูลที่ซับซ้อนภายในกระบวนการตั้งค่าที่ยาวนานสำหรับแอปพลิเคชันใหม่แต่ละรายการ ตอนนี้คุณก็รู้ว่ามีเส้นทางที่แตกต่างออกไปและนั่นก็เยี่ยมไปเลย - ไฮไฟว์!
โรงกลั่นCMS
ระบบจัดการเนื้อหาโอเพ่นซอร์สสำหรับ Rails
อนุสัญญาเกี่ยวกับการกำหนดค่าใคร? Rails Engines อาจดูเหมือนเป็นแบบฝึกหัดในการกำหนดค่าในบางครั้ง แต่ RefineryCMS จำความมหัศจรรย์ของ Rails บางอย่างได้ นี่คือเนื้อหาทั้งหมดของไดเรกทอรี 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 มันมีอัญมณีสำหรับการเย็บที่ล้อมรอบ แต่ใช้เพียงสองตะเข็บ และรวบรวมคอลเลกชั่นของ Engines เข้าด้วยกันเพื่อมอบฟังก์ชันการทำงานครบชุด
ส่วนขยายยังถูกสร้างขึ้นโดยผู้ใช้ Engine เพื่อสร้างการผสมผสานคุณลักษณะ CMS ของตนเองสำหรับบล็อก ข่าวสาร พอร์ตโฟลิโอ คำรับรอง การสอบถาม ฯลฯ (เป็นรายการยาว) ทั้งหมดนี้เกี่ยวข้องกับ RefineryCMS หลัก
การออกแบบนี้อาจได้รับความสนใจจากคุณสำหรับแนวทางแบบแยกส่วน และโรงกลั่นเป็นตัวอย่างที่ดีของรูปแบบการออกแบบ Rails นี้ “มันทำงานยังไง?” ฉันได้ยินคุณถาม
เอ็นจิ้น 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 เก็บรายการ procs ที่จะเรียกใช้ในภายหลัง กระบวนการรวมโรงกลั่นขยายแอปพลิเคชัน Rails ที่โหลดอยู่ในปัจจุบันด้วยตัวควบคุมและผู้ช่วยของ Refinery นี่คือหนึ่งในการดำเนินการ:
# 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 มาก่อน นี่เป็นวิธีการแบบเป็นโปรแกรม
การดูส่วนที่เหลือของไฟล์ Authentication Engine นั้นจะช่วยให้เรารวบรวมส่วนประกอบหลักอื่นๆ สองสามอย่าง:
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 เพียงเพิ่มชื่อโมดูล ไปยังรายการที่จัดเก็บไว้ในคลาส accessor
นี่คือสิ่งที่น่าตกใจ: คลาส Refinery::Authentication นั้นเป็นสิ่งที่ห่อหุ้ม Devise ด้วยการปรับแต่งบางอย่าง มันเลยเป็นเต่าลงไปจนสุด!
ส่วนขยายและปลั๊กอินเป็นแนวคิดที่ Refinery พัฒนาขึ้นเพื่อรองรับระบบนิเวศที่สมบูรณ์ของแอปและเครื่องมือ mini-rails - คิดว่า rake generate refinery:engine รูปแบบการออกแบบที่นี่แตกต่างจาก Spree โดยกำหนด API เพิ่มเติมรอบ Rails Engine เพื่อช่วยในการจัดการองค์ประกอบ
สำนวน “The Rails Way” เป็นหัวใจสำคัญของ Refinery ซึ่งปรากฏอยู่ในแอปมินิเรลมากขึ้นเรื่อยๆ แต่จากภายนอก คุณจะไม่รู้เลย การออกแบบขอบเขตที่ระดับองค์ประกอบของแอปพลิเคชันมีความสำคัญ ซึ่งอาจมากกว่านั้น มากกว่าการสร้าง API ใหม่ทั้งหมดสำหรับคลาสและโมดูลของคุณที่ใช้ในแอปพลิเคชัน Rails ของคุณ
การห่อโค้ดที่คุณไม่มีการควบคุมโดยตรงนั้นเป็นรูปแบบทั่วไป ซึ่งเป็นการมองการณ์ไกลในการลดเวลาการบำรุงรักษาเมื่อรหัสนั้นเปลี่ยนไป การจำกัดจำนวนตำแหน่งที่คุณต้องทำการแก้ไขเพื่อรองรับการอัปเกรด การใช้เทคนิคนี้ควบคู่ไปกับฟังก์ชันการแบ่งพาร์ติชั่นจะสร้างแพลตฟอร์มที่ยืดหยุ่นสำหรับการจัดองค์ประกอบภาพ และนี่คือตัวอย่างในโลกแห่งความเป็นจริงที่อยู่ใต้จมูกของคุณ - ต้องรักโอเพ่นซอร์ส!
บทสรุป
เราได้เห็นแนวทางสี่วิธีในการออกแบบรูปแบบกลไกของ Rails โดยการวิเคราะห์อัญมณียอดนิยมที่ใช้กับแอปพลิเคชันในโลกแห่งความเป็นจริง ควรอ่านผ่านคลังข้อมูลเพื่อเรียนรู้จากประสบการณ์มากมายที่นำไปใช้และทำซ้ำแล้วซ้ำเล่า ยืนบนไหล่ของยักษ์
ในคู่มือ Rails นี้ เราได้เน้นที่รูปแบบการออกแบบและเทคนิคในการรวม Rails Engines และแอปพลิเคชัน Rails ของผู้ใช้ปลายทาง เพื่อให้คุณสามารถเพิ่มความรู้เกี่ยวกับสิ่งเหล่านี้ลงในแถบเครื่องมือ Rails ของคุณ
ฉันหวังว่าคุณจะได้เรียนรู้มากเท่าที่ฉันจากการทบทวนโค้ดนี้และรู้สึกได้รับแรงบันดาลใจที่จะให้โอกาส Rails Engines เมื่อพวกเขาเหมาะสมกับใบเรียกเก็บเงิน ขอบคุณมากสำหรับผู้ดูแลและผู้มีส่วนร่วมในอัญมณีที่เราตรวจสอบ คนงานเยอะ!
