定制自己的 Engine
Engine = Ruby gem + Rails MVC stack elements.
一,创建自己的 Engine
可用命令 rails plugin new
创建自己的 Engine.
常用可选参数 --full
或 --mountable
两者之间的区别,可以参考【Engine full vs mountable】章节。
拆分一下,步骤大概如下:
1) 继承于 Rails::Engine,一般把它们放在 lib/ 目录下。
# lib/my_engine.rb
module MyEngine
class Engine < Rails::Engine
# ... ...
end
end
2) 在 config/application.rb (或 Gemfile) 里加载本文件。
Engine 相关的 model、controller 和 helper 会被加载到 app/ 里,route 会被加载到 config/routes.rb, locale 会被加载到 config/locales, tasks 会被加载到 lib/tasks.
# config/application.rb
require 'my_engine/engine'
# 或者
# Gemfile
gem 'my_engine', path: "/path/to/my_engine"
3) 在 routes.rb 里 mount MyEngine::Engine
Rails.application.routes.draw do
mount MyEngine::Engine => "/engine"
end
二,编写 my_engine/engine.rb 文件内容
每个 Engine 都会有自己的 engine.rb 文件。里面有自己的 Engine 类,它继承于 ::Rails::Engine
1) 常用方法之 config、initializer
在这文件里,你可以使用 config, initializer 等方法。这点和定制 Railtie 类似,但不同点是:当前 Engine 的配置和初始化,作用域仅限于当前 Engine.
class MyEngine < Rails::Engine
# 添加新的、额外的目录到 autoload_paths 里
config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
initializer "my_engine.add_middleware" do |app|
app.middleware.use MyEngine::Middleware
end
end
config 是个方法,但同时它也是 Configuration 的实例对象,所以你可以使用 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
2) 常用方法之 isolate_namespace
默认 Engine 和应用是在一个环境里的,这意味着应用所有 helper 和命名路由都可以在 Engine 里使用。
你可以使用 isolate_namespace
更改此项默认配置,将 Engine 和应用隔离出来。使用举例:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
end
end
此时 MyEngine
和应用是隔离了的,假设 MyEngine 有代码:
module MyEngine
class FooController < ActionController::Base
end
end
此时 FooController 仅能使用 Engine
里提供的 helper,以及 MyEngine::Engine.routes
提供的 url helper.
另外一个改变就是 Engine 里的路由不必再使用 namespace,举例:
MyEngine::Engine.routes.draw do
resources :articles
end
resources :articles 自动对应着 MyEngine::ArticlesController
. 并且不必使用长长的 url helper,例如 my_engine_articles_path
可以直接使用 articles_path
不受 isolate_namespace 影响的就是对于 model 的调用,仍然使用 engine_name 做为前缀。例如以下例子的 MyEngine::Article
polymorphic_url(MyEngine::Article.new) # => "articles_path"
form_for(MyEngine::Article.new) do
text_field :title
# => <input type="text" name="article[title]" id="article_title" />
end
另一个改变是对表名的更改。默认使用 engine_name (在这里是 "my_engine")做为表前缀,也就是说 MyEngine::Article 对应的表名应该是 my_engine_articles
3) 常用方法之 paths
Engine 默认都有自己的文件、目录结构,如果你没有定制,那么就使用默认的:
"app", eager_load: true, glob: "*"
"app/assets", glob: "*"
"app/controllers", eager_load: true
"app/helpers", eager_load: true
"app/models", eager_load: true
"app/mailers", eager_load: true
"app/views"
"app/controllers/concerns", eager_load: true
"app/models/concerns", eager_load: true
"lib", load_path: true
"lib/assets", glob: "*"
"lib/tasks", glob: "**/*.rake"
"config"
"config/environments", glob: "#{Rails.env}.rb"
"config/initializers", glob: "**/*.rb"
"config/locales", glob: "*.{rb,yml}"
"config/routes.rb"
"db"
"db/migrate"
"db/seeds.rb"
"vendor", load_path: true
"vendor/assets", glob: "*"
paths
通过它,可以更改默认的文件、目录结构。
举例,你想把 controller 文件放到 lib/ 目录下:
class MyEngine < Rails::Engine
paths["app/controllers"] = "lib/controllers"
end
再或者,controller 在 app/ 和 lib/ 下都可接受:
class MyEngine < Rails::Engine
paths["app/controllers"] << "lib/controllers"
end
Application 在 Engine 之上,它又有自己的配置和初始化。它配置了 app/ 下的文件、目录会被自动加载,所以像 app/services 会被自动加载。
4) 常用方法之 endpoint
Engine 内容也可以是一个 Rack Application. 当你的代码本身是 Rack Application,而又想使用 Engine 的特性时,可以这么做:
1) 在自己定义的 Engine 里,使用 endpoint
:
module MyEngine
class Engine < Rails::Engine
# Engine 的内容就是 MyRackApplication
endpoint MyRackApplication
end
end
2) 和平常一样,在 route 里 mount
你的 Engine:
Rails.application.routes.draw do
mount MyEngine::Engine => "/engine"
end
5) 常用方法之 middleware
Engine 内容也可以是一个 Middleware. 当你的代码本身是 Middleware,而又想使用 Engine 的特性时,可以这么做:
module MyEngine
class Engine < Rails::Engine
# Engine 的内容就是 SomeMiddleware
middleware.use SomeMiddleware
end
end
6) 常用方法之 engine_name
用几个场景可能会用到 engine name:
routes: 当你使用 mount(MyEngine::Engine => '/my_engine')
rake task: 当你使用 my_engine:install:migrations
Engine name 默认根据类名而来,如 MyEngine::Engine
对应有 my_engine_engine
. 你可以使用 engine_name
进行自定义:
module MyEngine
class Engine < Rails::Engine
engine_name "my_engine"
end
end
三,编写 Engine 内容
做了上述两步后,就是编写内容。该做什么事,做什么事;该完成什么功能,完成什么功能。
四,在 main_app 引入定制的 Engine
也就是:
在 config/application.rb (或 Gemfile) 里加载本文件。 在 routes.rb 里 mount MyEngine::Engine
最后更新于
这有帮助吗?