+3

Rails engine

Chào mọi người, bài viết này là tìm hiểu của mình về Rails Engine.

Rails engine

Mọi Rails developer đều quen với một Rails project (folder app với các subfolder controllers, models, ... , folder config chứa routes cho app, folder db chứa những migrations).

Nếu ta coi Rails project là một main app thì Rails Engine là những mini app được "nhúng" vào main app nhằm mục đích thêm những functionality riêng biệt cho main app.

main app cũng thực chất cũng là 1 Rails Engine, với class Rails::Application kế thừa class Rails::Engine, do đó cả main app và các Engine đều có chung một cấu trúc thư mục giống nhau.

Một số gem quen thuộc có sử dụng Rails engine: Devise, Spree.

Engine structure

Để nhúng một engine vào main app, ta dùng lệnh:

rails plugin new sherlock --mountable

Lệnh này làm 2 việc: tạo folder sherlock (cùng cấp với app, config, ... của main app) chứa mini engine folder, và thêm gem sherlock vào Gemfile của main app:

# Gemfile

gem 'sherlock', path: 'sherlock'

Và cấu trúc folder sherlock cơ bản sẽ giống với main app, trừ folder lib:

Trong quá trình khởi chạy main app, nó sẽ setup các gem được chỉ định trong Gemfile, vì đã thêm gem sherlockGemfile nên bundler sẽ đọc file sherlock.gemspec và require những file được chỉ định trong option spec.files:

# sherlock/sherlock.gemspec

...
spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]

như ta thấy ở trên, bundler sẽ require tất cả các file trong các folder app, config, dblib

folder lib

bắt đầu từ file sherlock.rb define module Sherlock và require tới sherlock/engine

# lib/sherlock.rb
require "sherlock/engine"

module Sherlock
  # Your code goes here...
end

# lib/sherlock/engine.rb
module Sherlock
  class Engine < ::Rails::Engine
    isolate_namespace Sherlock
  end
end

dòng

isolate_namespace Sherlock

chỉ định namespace của engine, mỗi engine đều được bọc bởi một module, thường là tên củaengineđó (ở đây là module Sherlock), và tất cả các class trong engine cũng đều bọc trong module này.

class Sherlock::Engine là nơi ta định nghĩa các config, setup cần thiết cho functions được nhúng. Ví dụ ở Spree:

# 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

initializer là một class method của Rails::Railtie hỗ trợ việc hook vào quá trình Rails init process nhằm mục đích thêm, sửa đổi config của cả Rails app. Ở đây params app chính là instance của Rails::Application, Spree khởi tạo config riêng của mình ở đây, từ đó config này (1 instance của class Spree::Core::Environment) có thể sử dụng ở mọi nơi trong main app:

Rails.application.config.spree

folder config

chứa routes.rb nơi define các endpoint của engine:

# sherlock/config/routes.rb

Sherlock::Engine.routes.draw do
    get :home, to: "home#index"
end

ta cần mount Sherlock::Engine vào config routes của main app để sử dụng:

# config/routes.rb

mount Sherlock::Engine => "/sherlock"

lúc này ở main app có thể truy cập endpoint: /sherlock/home.

folder app

các folder con trong app đều giống với main app, mọi class, views đều được module hoá (Sherlock):

các controller, helpers, models, ... được define ở đây và được sử dụng ở main app.

Devise là một ví dụ điển hình:

các controller, view, helper, mailer đều được define sẵn và sử dụng ở main app như cách ta thường làm với devise: mount routes, include devise trong model => hoàn thành phần authentication với đầy đủ routes, controllers, views, ...


Trên đây là tìm hiểu của mình về Rails::Engine, cảm ơn mọi người đã đọc bài viết 😀 .


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí