Rails::Engine
允許您包裝特定的 Rails 應用程式或功能子集,並與其他應用程式或在較大的封裝應用程式中分享。每個 Rails::Application
僅是一個引擎,允許簡單的功能和應用程式分享。
任何 Rails::Engine
也是一個 Rails::Railtie
,因此相同的 method(例如 rake_tasks 和 generators)和 railties 中可用的設定選項也可以用於引擎中。
建立一個 Engine
如果您希望一個 gem 以引擎的方式運作,您必須在您的外掛程式 lib
資料夾中的某個地方為它指定一個 Engine
(類似於我們指定 Railtie
的方式)
# lib/my_engine.rb
module MyEngine
class Engine < Rails::Engine
end
end
然後確保這個檔案載入到您的 config/application.rb
(或您的 Gemfile
)的最上方,它會自動載入 app
內部的模型、控制器和 helper,載入 config/routes.rb
的路由,載入 config/locales/*/
的語言環境,並載入 lib/tasks/*/
的工作。
設定
與 railties 類似,引擎可以存取一個包含所有 railties 和應用程式共用設定的設定物件。此外,每個引擎都可以存取設定為該引擎範圍的 autoload_paths
、eager_load_paths
和 autoload_once_paths
設定。
class MyEngine < Rails::Engine
# Add a load path for this specific Engine
config.autoload_paths << File.expand_path("lib/some/path", __dir__)
initializer "my_engine.add_middleware" do |app|
app.middleware.use MyEngine::Middleware
end
end
產生器
您可以使用 config.generators
方法為引擎設定產生器
class MyEngine < Rails::Engine
config.generators do |g|
g.orm :active_record
g.template_engine :erb
g.test_framework :test_unit
end
end
您也可以使用 config.app_generators
為應用程式設定產生器
class MyEngine < Rails::Engine
# note that you can also pass block to app_generators in the same way you
# can pass it to generators method
config.app_generators.orm :datamapper
end
路徑
應用程式和引擎具有彈性的路徑設定,表示您不需要將您的控制器放在 app/controllers
,而可以放在您覺得方便的任何地方。
例如,假設您想將您的控制器放在 lib/controllers
。您可以將其設定為一個選項
class MyEngine < Rails::Engine
paths["app/controllers"] = "lib/controllers"
end
您也可以從 app/controllers
和 lib/controllers
兩者載入您的控制器
class MyEngine < Rails::Engine
paths["app/controllers"] << "lib/controllers"
end
引擎中可用的路徑為
class MyEngine < Rails::Engine
paths["app"] # => ["app"]
paths["app/controllers"] # => ["app/controllers"]
paths["app/helpers"] # => ["app/helpers"]
paths["app/models"] # => ["app/models"]
paths["app/views"] # => ["app/views"]
paths["lib"] # => ["lib"]
paths["lib/tasks"] # => ["lib/tasks"]
paths["config"] # => ["config"]
paths["config/initializers"] # => ["config/initializers"]
paths["config/locales"] # => ["config/locales"]
paths["config/routes.rb"] # => ["config/routes.rb"]
end
Application
類別會新增幾個路徑到此組。且如同您的 Application
,app
下的所有資料夾會自動新增到載入路徑。例如,如果您有一個 app/services
資料夾,它會預設新增。
端點
引擎也可以是 Rack
應用程式。如果您有一個 Rack
應用程式,且您想要提供一些 Engine
的功能,這會很有用。
要執行此操作,請使用 ::endpoint
方法
module MyEngine
class Engine < Rails::Engine
endpoint MyRackApplication
end
end
現在您可以在應用程式的路由中掛載您的引擎
Rails.application.routes.draw do
mount MyEngine::Engine => "/engine"
end
中間件堆疊
由於引擎現在可以是 Rack
端點,因此它也可以有一個中間件堆疊。用法與 Application
中完全相同
module MyEngine
class Engine < Rails::Engine
middleware.use SomeMiddleware
end
end
路由
如果您未指定端點,則路由會用作預設端點。您可以像使用應用程式的路由一樣使用它們
# ENGINE/config/routes.rb
MyEngine::Engine.routes.draw do
get "/" => "posts#index"
end
掛載優先順序
請注意,現在您的應用程式中可以有多個路由器,最好避免透過許多路由器傳遞要求。考慮以下情況
Rails.application.routes.draw do
mount MyEngine::Engine => "/blog"
get "/blog/omg" => "main#omg"
end
MyEngine
掛載在 /blog
,而 /blog/omg
指向應用程式的控制器。在這種情況下,對 /blog/omg
的要求會透過 MyEngine
,如果 Engine
的路由中沒有此類路由,則會傳送至 main#omg
。交換它們會好得多
Rails.application.routes.draw do
get "/blog/omg" => "main#omg"
mount MyEngine::Engine => "/blog"
end
現在,Engine
僅會取得未由 Application
處理的要求。
Engine
名稱
有些地方會使用引擎的名稱
-
路由:當您使用
mount(MyEngine::Engine => '/my_engine')
掛載Engine
時,它會用作預設:as
選項 -
安裝遷移的 rake 任務
my_engine:install:migrations
Engine
名稱預設會根據類別名稱設定。對於 MyEngine::Engine
,它會是 my_engine_engine
。您可以使用 engine_name
方法手動變更它
module MyEngine
class Engine < Rails::Engine
engine_name "my_engine"
end
end
孤立的 Engine
通常,當您在引擎內建立控制器、輔助程式和模型時,它們會被視為在應用程式本身內建立的。這表示應用程式中的所有輔助程式和命名路由也會提供給引擎的控制器。
但是,有時您想要將引擎與應用程式隔離,特別是如果您的引擎有自己的路由器。要執行此操作,您只需呼叫 ::isolate_namespace
。此方法需要您傳遞一個模組,您所有的控制器、輔助程式和模型都應該嵌套到其中
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
end
end
對於這樣的引擎,MyEngine
模組內的所有內容都會與應用程式隔離。
考慮這個控制器
module MyEngine
class FooController < ActionController::Base
end
end
如果 MyEngine
引擎被標記為孤立,FooController
只能存取 MyEngine
的輔助程式,以及 MyEngine::Engine.routes
的 url_helpers
。
孤立引擎中變更的下一件事是路由的行為。一般來說,當您命名空間您的控制器時,您也需要命名空間相關的路由。使用孤立引擎時,引擎的命名空間會自動套用,因此您不需要在您的路由中明確指定它。
MyEngine::Engine.routes.draw do
resources :articles
end
如果 MyEngine
是孤立的,上面的路由將指向 MyEngine::ArticlesController
。您也不需要使用較長的 URL 輔助程式,例如 my_engine_articles_path
。相反地,您應該只使用 articles_path
,就像您在主應用程式中所做的一樣。
為了讓此行為與框架的其他部分保持一致,孤立引擎也會對 ActiveModel::Naming
產生影響。在一般的 Rails 應用程式中,當您使用命名空間模型,例如 Namespace::Article
時,ActiveModel::Naming
會產生具有前綴「namespace」的名稱。在孤立引擎中,前綴會在 URL 輔助程式和表單欄位中省略,以方便使用。
polymorphic_url(MyEngine::Article.new)
# => "articles_path" # not "my_engine_articles_path"
form_for(MyEngine::Article.new) do
text_field :title # => <input type="text" name="article[title]" id="article_title" />
end
此外,孤立引擎會根據其命名空間設定自己的名稱,因此 MyEngine::Engine.engine_name
會傳回「my_engine」。它也會將 MyEngine.table_name_prefix
設定為「my_engine_」,表示例如 MyEngine::Article
預設會使用 my_engine_articles
資料庫表格。
在 Engine
外部使用 Engine 的路由
由於您現在可以在應用程式的路由內掛載引擎,因此您無法在 Application
內部直接存取 Engine
的 url_helpers
。當您在應用程式的路由中掛載引擎時,會建立一個特殊輔助程式讓您能夠這麼做。考慮以下情況
# config/routes.rb
Rails.application.routes.draw do
mount MyEngine::Engine => "/my_engine", as: "my_engine"
get "/foo" => "foo#index"
end
現在,您可以在您的應用程式內部使用 my_engine
輔助程式
class FooController < ApplicationController
def index
my_engine.root_url # => /my_engine/
end
end
還有一個 main_app
輔助程式,讓您可以在 Engine 內部存取應用程式的路由
module MyEngine
class BarController
def index
main_app.foo_path # => /foo
end
end
end
請注意,提供給掛載的 :as
選項預設採用 engine_name
,因此大多數時候您都可以省略它。
最後,如果你想使用 polymorphic_url
產生一個引擎路由的 URL,你也需要傳遞引擎輔助程式。假設你想要建立一個指向引擎路由之一的表單。你所需要做的就是將輔助程式傳遞為 URL 屬性的陣列中的第一個元素
form_for([my_engine, @user])
這段程式碼將使用 my_engine.user_path(@user)
來產生適當的路由。
孤立的引擎輔助程式
有時你可能想要孤立一個引擎,但使用為它定義的輔助程式。如果你只想共用幾個特定的輔助程式,你可以將它們新增到 ApplicationController 中的應用程式輔助程式
class ApplicationController < ActionController::Base
helper MyEngine::SharedEngineHelper
end
如果你想要包含所有引擎的輔助程式,你可以使用引擎實例上的輔助程式方法
class ApplicationController < ActionController::Base
helper MyEngine::Engine.helpers
end
它將包含引擎目錄中的所有輔助程式。請注意,這不包括使用 helper_method 或其他類似方法在控制器中定義的輔助程式,只有在輔助程式目錄中定義的輔助程式才會包含在內。
遷移和種子資料
引擎可以有自己的遷移。遷移的預設路徑與應用程式中的路徑完全相同:db/migrate
要在應用程式中使用引擎的遷移,你可以使用以下 rake 任務,它會將遷移複製到應用程式的目錄中
$ rake ENGINE_NAME:install:migrations
請注意,如果應用程式中已經存在名稱相同的遷移,則可能會略過某些遷移。在這種情況下,你必須決定是要保留該遷移,還是重新命名應用程式中的遷移並重新執行複製遷移。
如果你的引擎有遷移,你可能還想要在 db/seeds.rb
檔案中準備資料庫的資料。你可以使用 load_seed
方法載入該資料,例如
MyEngine::Engine.load_seed
載入優先順序
為了變更引擎的優先順序,你可以在主應用程式中使用 config.railties_order
。它會影響載入檢視、輔助程式、資產和所有其他與引擎或應用程式相關的檔案的優先順序。
# load Blog::Engine with highest priority, followed by application and other railties
config.railties_order = [Blog::Engine, :main_app, :all]
- A
- C
- E
- F
- H
- I
- L
- N
- R
屬性
[RW] | called_from | |
[RW] | isolated | |
[RW] | isolated? |
類別公開方法
endpoint(endpoint = nil) 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 379 def endpoint(endpoint = nil) @endpoint ||= nil @endpoint = endpoint if endpoint @endpoint end
find(path) 連結
尋找具有給定路徑的引擎。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 423 def find(path) expanded_path = File.expand_path path Rails::Engine.subclasses.each do |klass| engine = klass.instance return engine if File.expand_path(engine.root) == expanded_path end nil end
find_root(from) 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 375 def find_root(from) find_root_with_flag "lib", from end
inherited(base) 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 361 def inherited(base) unless base.abstract_railtie? Rails::Railtie::Configuration.eager_load_namespaces << base base.called_from = begin call_stack = caller_locations.map { |l| l.absolute_path || l.path } File.dirname(call_stack.detect { |p| !p.match?(%r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack]) }) end end super end
isolate_namespace(mod) 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 385 def isolate_namespace(mod) engine_name(generate_railtie_name(mod.name)) routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) } self.isolated = true unless mod.respond_to?(:railtie_namespace) name, railtie = engine_name, self mod.singleton_class.instance_eval do define_method(:railtie_namespace) { railtie } unless mod.respond_to?(:table_name_prefix) define_method(:table_name_prefix) { "#{name}_" } ActiveSupport.on_load(:active_record) do mod.singleton_class.redefine_method(:table_name_prefix) do "#{ActiveRecord::Base.table_name_prefix}#{name}_" end end end unless mod.respond_to?(:use_relative_model_naming?) class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__ end unless mod.respond_to?(:railtie_helpers_paths) define_method(:railtie_helpers_paths) { railtie.helpers_paths } end unless mod.respond_to?(:railtie_routes_url_helpers) define_method(:railtie_routes_url_helpers) { |include_path_helpers = true| railtie.routes.url_helpers(include_path_helpers) } end end end end
new() 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 439 def initialize @_all_autoload_paths = nil @_all_load_paths = nil @app = nil @config = nil @env_config = nil @helpers = nil @routes = nil @app_build_lock = Mutex.new super end
實例公開方法
app() 連結
傳回這個引擎的底層 Rack
應用程式。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 517 def app @app || @app_build_lock.synchronize { @app ||= begin stack = default_middleware_stack config.middleware = build_middleware.merge_into(stack) config.middleware.build(endpoint) end } end
call(env) 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 534 def call(env) req = build_request env app.call req.env end
config() 連結
定義引擎的組態物件。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 553 def config @config ||= Engine::Configuration.new(self.class.find_root(self.class.called_from)) end
eager_load!() 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 491 def eager_load! # Already done by Zeitwerk::Loader.eager_load_all. By now, we leave the # method as a no-op for backwards compatibility. end
endpoint() 連結
傳回這個引擎的端點。如果沒有註冊,預設為 ActionDispatch::Routing::RouteSet。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 529 def endpoint self.class.endpoint || routes end
env_config() 連結
定義在每次呼叫時新增的額外 Rack
env 組態。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 540 def env_config @env_config ||= {} end
helpers() 連結
傳回一個包含引擎中定義的所有輔助函式的模組。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 501 def helpers @helpers ||= begin helpers = Module.new AbstractController::Helpers.helper_modules_from_paths(helpers_paths).each do |mod| helpers.include(mod) end helpers end end
helpers_paths() 連結
傳回所有已註冊的 helpers 路徑。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 512 def helpers_paths paths["app/helpers"].existent end
load_console(app = self) 連結
載入主控台並呼叫已註冊的掛勾。查看 Rails::Railtie.console
以取得更多資訊。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 453 def load_console(app = self) require "rails/console/app" require "rails/console/helpers" run_console_blocks(app) self end
load_generators(app = self) 連結
載入 Rails 產生器並呼叫已註冊的掛勾。查看 Rails::Railtie.generators
以取得更多資訊。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 477 def load_generators(app = self) require "rails/generators" run_generators_blocks(app) Rails::Generators.configure!(app.config.generators) self end
load_runner(app = self) 連結
載入 Rails 執行器並呼叫已註冊的掛勾。查看 Rails::Railtie.runner
以取得更多資訊。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 462 def load_runner(app = self) run_runner_blocks(app) self end
load_seed() 連結
從 db/seeds.rb 檔案載入資料。它可用於載入引擎的種子,例如:
Blog::Engine.load_seed
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 561 def load_seed seed_file = paths["db/seeds.rb"].existent.first run_callbacks(:load_seed) { load(seed_file) } if seed_file end
load_server(app = self) 連結
呼叫伺服器已註冊的掛勾。查看 Rails::Railtie.server
以取得更多資訊。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 486 def load_server(app = self) run_server_blocks(app) self end
load_tasks(app = self) 連結
載入 Rake 和 railties 任務,並呼叫已註冊的掛勾。查看 Rails::Railtie.rake_tasks
以取得更多資訊。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 469 def load_tasks(app = self) require "rake" run_tasks_blocks(app) self end
railties() 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 496 def railties @railties ||= Railties.new end
routes(&block) 連結
定義此引擎的路由。如果將區塊傳遞至路由,則會附加至引擎。
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 546 def routes(&block) @routes ||= ActionDispatch::Routing::RouteSet.new_with_config(config) @routes.append(&block) if block_given? @routes end
實例私有方法
load_config_initializer(initializer) 連結
來源:顯示 | 在 GitHub 上
# File railties/lib/rails/engine.rb, line 688 def load_config_initializer(initializer) # :doc: ActiveSupport::Notifications.instrument("load_config_initializer.railties", initializer: initializer) do load(initializer) end end