+3

Giới thiệu về Rails Engine

1.Giới thiệu Rails Engine

Hiện nay ngôn ngữ lập trình nói chung và Ruby on Rails nói riêng đang có những bước phát triển chóng mặt, hàng loạt những công nghệ mới ra đời và các ứng dụng chuyên biệt hóa đòi hỏi lập trình viên phải biết chắt lọc và tái sử dụng rất nhiều . Rails Engine ra đời cũng nhằm giải quyết vấn đề đó.

Rails Engine thực chất cũng là một ứng dụng nhỏ cung cấp cho parrent app một vài chức năng nhất định nào đó. Các bạn có thể hình dung nếu chúng ta coi các gem là những viên gạch để xây lên một ngôi nhà( ứng dụng ) thì Rails Engine sẽ là một vài viên gạch đã được ghép vào với nhau sẵn rồi. khi xây phần nào đó thì chúng ta chỉ việc mang nó ra và xài thôi, nếu có chỗ nó thừa ra hoặc hụt thì chúng ta phải gọt bớt hoặc thêm vào sao cho phù hợp với ứng dụng của chúng ta. Trên thực tế thì việc cắt gọt đó cũng không hề đơn giản. Vì thế nếu muốn tái sử dụng Engine thì chúng ta phải xác định rất rõ khi xây dựng một số ứng dụng có những điểm tương đồng nào, phần nào là phần có thể tách riêng ra thành một Rails Engine.

Với rất nhiều người thì việc xây dựng một ứng dụng sử dụng một vài Rails Engine là chuyện bình thường nhưng nếu một ứng dụng có số lượng Engine lên đến 10-20 Engine thì việc quản lí code là khá phức tạp đòi hỏi phải có kiến thức rất vững và một tâm lí chày cối khi làm việc với đống Engine đó vì số lượng file và thư mục các bạn cần quản lí có thể tăng gấp 50-100 lần so với một rails app bình thường. Việc tìm kiếm các file trong quá trình code sẽ gặp thêm những khó khăn.

Có 2 loại Rails Engine là full engine và mountable engine, chúng ta có thể tạo ra bằng lệnh sau:

rails plugin new zoo --mountable  # create mountable engine
rails plugin new zoo --full       # create full engine

Lệnh trên sẽ tạo ra rails engine có tên là zoo .Ngoài ra còn có nhiều các tùy chọn khi khởi tạo một Rails Engine các bạn có thể xem chi tiết bằng lệnh:

rails plugin --help

Sự khác biệt của 2 loại này được mô tả trong bảng sau:

Tiêu chí Full Engine Mountable Engine
Chạy Được tích hợp vào trong parrent app Giống như một ứng dụng chạy song song với parrent app
Chia sẻ Chia sẻ models, views, controllers, helpers, và routes với parent app Không chia sẻ models, views, controllers, helpers, và routes với parent app
layout, js, css Dùng chung với parent app Sử dụng của riêng mình
mount Không cần Mount vào parrent app Cần được "mount" vào parent app thông qua config/routes.rb
Thực tế sử dụng ít được dùng được dùng rất nhiều

=> Phần lớn engine được dùng với mục đích tách rời các component để dễ maintain và tái sử dụng nên mountable engine là kiểu engine chủ yếu được sử dụng. Trong những phần tiếp sau đây tôi sẽ chỉ nói về Mountable Engine.

2. Cấu trúc của Rails Engine

Trước tiên chúng ta sẽ tạo ra một mountable Engine có tên là "zoo" bằng lệnh sau:

rails plugin new zoo --dummy-path=spec/dummy --skip-test-unit --mountable
  • Ở lệnh trên thì tùy chọn --dummy-path=spec/dummy sẽ tạo ra cho chúng ta một rails test app độc lập dùng để viết test cho engine mà tôi sẽ nói tiếp ở các phần phía sau đây.

  • --skip-test-unit là tùy chọn viết test theo kiểu unit test.

  • --mountable chính là tùy chọn để tạo ra mountable engine.

Sau khi gõ lệnh trên sẽ sinh ra một số file như sau:

create new mountable engine.png

nhìn vào cấu trúc được sinh ra chúng ta nhận thấy trong tất cả các các thư mục layout, js, css đều nằm trong thư mục cha là "zoo", bạn có thể nhìn thấy rõ hơn qua cấu trúc thư mục app của mountable engine dưới đây.

rails engine folder.png

Tương tự như vậy thì các file trong view, controller, i18n, serrierlizer, test cũng sẽ nằm trong một thư mục cha là zoo.

Còn trong thư mục zoo/spec/ sẽ là thư mục dummy. Thực chất dummy chính là một rails application hoàn chỉnh được tạo ra bằng tùy chọn --dummy-path=spec/dummy của chúng ta ở trên dùng để viết test cho mountable engine zoo này.

spec dummy test.png

=> Như vậy chúng ta có thể thấy cấu trúc của một Mountable Engine gần giống như một Rails application thông thường. Nhưng có một số file chúng ta cần chú ý sau đây.

  • config/routes.rb dành cho việc thiết lập các routes trong engine
  • zoo.gemspec giống như gemspec của một gem, thay thế cho Gemfile
  • lib/zoo/engine.rb nơi chưa các config liên quan đến engine

Vì engine được đóng gói như một gem nên để sử dụng engine với Rails app, chúng ta khai báo engine trong Gemfile của app như sau:

gem "zoo", path: "zoo"

Chúng ta lần lượt xem xét các file trên.

config/routes.rb

Zoo::Engine.routes.draw do
  # routes definitions
end

routes của engine được wrap trong Foo::Engine.routes thay vì Rails.application.routes. Tuy nhiên về cách khai báo routes thì không có gì khác biệt.

Chúng ta có thể mount một engine vào parent app trong config/routes.rb của parent app:

Rails.application.routes.draw do
  mount Zoo::Engine, at: "foo"
end

Khi đó thông qua địa chỉ localhost:3000/zoo trong development chúng ta có thể truy cập vào giao diện của engine zoo.

zoo.gemspec

$:.push File.expand_path("../lib", __FILE__)

# Maintain your gem's version:
require "zoo/version"

# Describe your gem and declare its dependencies:
Gem::Specification.new do |s|
  s.name        = "zoo"
  s.version     = Zoo::VERSION
  s.authors     = ["HoangVanTrinh92"]
  s.email       = ["hoang.van.trinh@framgia.com"]
  s.homepage    = "TODO"
  s.summary     = "TODO: Summary of Zoo."
  s.description = "TODO: Description of Zoo."
  s.license     = "MIT"

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

  s.add_dependency "rails", "~> 5.0.0", ">= 5.0.0.1"

  s.add_development_dependency "sqlite3"
end

Ở đây chúng ta sẽ add thêm các dependency chính là các gem mà chúng ta sẽ sử dụng.

lib/zoo/engine.rb

module Zoo
  class Engine < ::Rails::Engine
    isolate_namespace Zoo
  end
end

Khai báo isolate_namespace Zoo có nghĩa là mọi class trong models, controllers, helpers và cả routes sẽ được gói trong namespace Zoo. Điều này đảm bảo rằng Zoo engine sẽ chạy độc lập và không có các xung khắc về tên hay code với parent app.

Ví dụ: parent app và engine đều có thể khai báo class Lion, tuy nhiên của Zoo engine sẽ là Zoo::Lion với table name là zoo_lion.

3. Model và Migration

3.1 Model

Tạo một ActiveRecord model trong engine hoàn toàn giống như trong Rails app nhưng cần phải cd Zoo tới thư mục chứa engine.

rails generate model Item name:string price:integer

Lệnh trên sẽ tạo ra một model Item được gói trong module Zoo

#zoo/app/models/zoo/item.rb

module Zoo
  class Item < ApplicationRecord
  end
end

Và tạo ra một migration file như sau

#zoo/db/migrate/2016112712345_create_zoo_items.rb

class CreateZooItems < ActiveRecord::Migration[5.0]
  def change
    create_table :zoo_items do |t|
      t.string :name
      t.integer :price

      t.timestamps
    end
  end
end

Để parrent app có thể sử dụng được migration này chúng ta cần coppy migration này sang parrent app cd parrent_app

rake zoo:install:migrations

Lệnh trên cũng tạo ra một file migration trong parrent app giống như trong Zoo engine như sau

#parrent_app/db/migrate/2016112712665_create_zoo_items.rb

#This migration comes from now_common (originally 2016112712345)
class CreateZooItems < ActiveRecord::Migration[5.0]
  def change
    create_table :zoo_items do |t|
      t.string :name
      t.integer :price

      t.timestamps
    end
  end
end

Như vậy là file migration của chúng ta đã được lưu ở hai nơi. Và như vậy thì mỗi khi chúng ta thêm migration vào engine thì chúng ta lại phải coppy ra parrent app.

4. Lời kết và tài liệu tham khảo

=> Như vậy là tôi đã giới thiệu xong với các bạn về Rails engine và cấu trúc sơ lược của nó. Chi tiết hơn tôi sẽ chia sẻ với các bạn ở những bài viết tiếp theo.

  • Tài liệu tham khảo.

http://guides.rubyonrails.org/engines.html

Chúc các bạn có thành công trong việc sử dụng Rails Engine!


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í