Rails 3.1: Engine vs. Mountable App

Can someone help me understand the differences between a Rails Engine and a Mountable app? In Rails 3.1, you can create either one with the "rails new plugin ___" command.

rails plugin new forum --full        # Engine
rails plugin new forum --mountable   # Mountable App

When would you want to use one versus the other? I know you can package an Engine as a gem, for one. Is that not the case for Mountable Apps? What other differences are there?

30742 次浏览

The difference, I believe, is that a mountable app's are isolated from the host app, so they can't share classes - models, helper etc. This is because a Mountable app is a Rack endpoint (i.e a Rack app in its own right).

免责声明: 和大多数人一样,我只是刚开始玩 Rails 3.1。

我对这种差异的理解是,引擎就像插件一样,可以向现有应用程序添加功能。而可挂载的应用程序本质上是一个应用程序,可以独立存在。

因此,如果你想能够运行它本身或在另一个应用程序,你会做一个挂载应用程序。如果您希望它成为现有应用程序的附加组件,但不希望它自己运行,那么您可以将它变成一个引擎。

我注意到以下几点:

全速前进

对于完整的引擎,父应用程序从引擎继承路由。不需要在 parent_app/config/routes.rb中指定任何内容。在 Gemfile 指定 gem 就足以让父应用程序继承模型、路由等。发动机路线指定为:

# my_engine/config/routes.rb
Rails.application.routes.draw do
# whatever
end

没有模型、控制器等的名称空间 accessible to the parent application.

Mountable Engine

默认情况下,引擎的名称空间是独立的:

# my_engine/lib/my_engine/engine.rb
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
end
end

使用可挂载引擎,路由是有名称空间的,父应用程序可以将这个功能捆绑在一个路由下:

# my_engine/config/routes.rb
MyEngine::Engine.routes.draw do
#whatever
end


# parent_app/config/routes.rb
ParentApp::Application.routes.draw do
mount MyEngine::Engine => "/engine", :as => "namespaced"
end

模型、控制器等与父应用程序相隔离——尽管辅助程序可以很容易地共享。

这些是我发现的主要差异。也许还有其他人?我已经通过 here询问过了,但是还没有得到答复。

我的印象是,由于完整的引擎不会将自己与父应用程序隔离开来,因此最好将其用作与父应用程序相邻的绿色软体。我相信名字冲突可能会发生。

当您希望避免名称冲突并将引擎绑定到父应用程序中的一个特定路由下时,可以使用可挂载引擎。例如,我正在构建我的第一个为客户服务设计的引擎。父应用程序可以将其功能捆绑在一个路由下,比如:

mount Cornerstone::Engine => "/cornerstone", :as => "help"

如果我的假设有误,请让我知道,我会修正这个反应。我写了一篇关于 给你的小文章,干杯!

我也想知道,所以就来了这里。在我看来,早期的答案基本上涵盖了这个问题,但我认为以下内容也可能有所帮助:

# generate plugins (NOTE: using same name each time to minimize differences)
# -----------------------------------------------------------------------------


$ rails plugin new test-plugin -T
$ mv test-plugin{,.01}


$ rails plugin new test-plugin -T --mountable
$ mv test-plugin{,.02}


$ rails plugin new test-plugin -T --full
$ mv test-plugin{,.03}


$ rails plugin new test-plugin -T --full --mountable
$ mv test-plugin{,.04}








# compare "stock" (01) with "mountable" (02)
# -----------------------------------------------------------------------------


$ diff -r test-plugin.01 test-plugin.02


Only in test-plugin.02: app
Only in test-plugin.02: config
Only in test-plugin.02/lib/test-plugin: engine.rb
diff -r test-plugin.01/lib/test-plugin.rb test-plugin.02/lib/test-plugin.rb
0a1,2
> require "test-plugin/engine"
>
Only in test-plugin.02: script
diff -r test-plugin.01/test-plugin.gemspec test-plugin.02/test-plugin.gemspec
18a19
>   # s.add_dependency "jquery-rails"








# compare "stock" (01) with "full" (03)
# -----------------------------------------------------------------------------


$ diff -r test-plugin.01 test-plugin.03
Only in test-plugin.03: app
Only in test-plugin.03: config
Only in test-plugin.03/lib/test-plugin: engine.rb
diff -r test-plugin.01/lib/test-plugin.rb test-plugin.03/lib/test-plugin.rb
0a1,2
> require "test-plugin/engine"
>
Only in test-plugin.03: script
diff -r test-plugin.01/test-plugin.gemspec test-plugin.03/test-plugin.gemspec
18a19
>   # s.add_dependency "jquery-rails"








# compare "mountable" (02) with "full" (03)
# -----------------------------------------------------------------------------


$ diff -r test-plugin.02 test-plugin.03


Only in test-plugin.03/app/assets/javascripts/test-plugin: .gitkeep
Only in test-plugin.02/app/assets/javascripts/test-plugin: application.js
Only in test-plugin.03/app/assets/stylesheets/test-plugin: .gitkeep
Only in test-plugin.02/app/assets/stylesheets/test-plugin: application.css
Only in test-plugin.03/app/controllers: .gitkeep
Only in test-plugin.02/app/controllers: test-plugin
Only in test-plugin.03/app/helpers: .gitkeep
Only in test-plugin.02/app/helpers: test-plugin
Only in test-plugin.03/app/mailers: .gitkeep
Only in test-plugin.03/app/models: .gitkeep
Only in test-plugin.03/app/views: .gitkeep
Only in test-plugin.02/app/views: layouts
diff -r test-plugin.02/config/routes.rb test-plugin.03/config/routes.rb
1c1
< TestPlugin::Engine.routes.draw do
---
> Rails.application.routes.draw do
diff -r test-plugin.02/lib/test-plugin/engine.rb test-plugin.03/lib/test-plugin/engine.rb
3d2
<     isolate_namespace TestPlugin








# compare "mountable" (02) with "full & mountable" (04)
# -----------------------------------------------------------------------------


$ diff -r test-plugin.02 test-plugin.04


<no difference>








# compare "full" (03) with "full & mountable" (04)
# -----------------------------------------------------------------------------


$ diff -r test-plugin.03 test-plugin.04


Only in test-plugin.03/app/assets/javascripts/test-plugin: .gitkeep
Only in test-plugin.04/app/assets/javascripts/test-plugin: application.js
Only in test-plugin.03/app/assets/stylesheets/test-plugin: .gitkeep
Only in test-plugin.04/app/assets/stylesheets/test-plugin: application.css
Only in test-plugin.03/app/controllers: .gitkeep
Only in test-plugin.04/app/controllers: test-plugin
Only in test-plugin.03/app/helpers: .gitkeep
Only in test-plugin.04/app/helpers: test-plugin
Only in test-plugin.03/app/mailers: .gitkeep
Only in test-plugin.03/app/models: .gitkeep
Only in test-plugin.03/app/views: .gitkeep
Only in test-plugin.04/app/views: layouts
diff -r test-plugin.03/config/routes.rb test-plugin.04/config/routes.rb
1c1
< Rails.application.routes.draw do
---
> TestPlugin::Engine.routes.draw do
diff -r test-plugin.03/lib/test-plugin/engine.rb test-plugin.04/lib/test-plugin/engine.rb
2a3
>     isolate_namespace TestPlugin

(对我来说)特别感兴趣的是

rails plugin new test-plugin -T --mountable

还有

rails plugin new test-plugin -T --full --mountable

这两个选项都将生成 engine。不同之处在于,--mountable将在一个独立的名称空间中创建引擎,而 --full将创建一个共享主应用程序名称空间的引擎。

这些差异将以三种方式表现出来:

1)引擎类文件将调用 isolate_namespace:

Lib/my _ full _ engine/engine.rb:

module MyFullEngine
class Engine < Rails::Engine
end
end

Lib/my _ mount _ engine/engine.rb:

module MyMountableEngine
class Engine < Rails::Engine
isolate_namespace MyMountableEngine # --mountable option inserted this line
end
end

2)引擎的 config/routes.rb文件将使用名称空间:

全引擎:

Rails.application.routes.draw do
end

固定发动机:

MyMountableEngine::Engine.routes.draw do
end

3)控制器、助手、视图和资产的文件结构将使用名称空间:

创建 app/controller/我的 _ mount _ engine/application _ controller. rb
create app/helpers/我的 _ mount _ engine/application_helper.rb
创建应用程序/邮件发送者创建应用程序/模型
创建 app/views/layouts/my_mountable_engine/application.html. erb
创建 app/asset/images/< strong > my _ mount _ engine
创建应用程序/资产/样式表/我的 _ mount _ engine/application.css
创建 app/asset/javascript/我的 _ mount _ engine/application.js
create config/routes.rb create lib/my_mountable_engine.rb
创建 lib/asks/my _ mount _ engine. rake
创建 lib/my _ mount _ engine/version. rb
创建 lib/my _ mount _ engine/engine.rb


解释

--full选项的用例似乎非常有限。就我个人而言,我想不出任何好的理由来解释为什么要在不隔离名称空间的情况下将代码分离到一个引擎中——它实际上只会给你提供两个紧密耦合的应用程序,它们共享相同的文件结构,以及由此带来的所有冲突和代码泄漏。

我所看到的每一篇文档都演示了 --mountable选项,实际上当前的 边缘导轨强烈鼓励您包含 isolate namespace-这与使用 --mountable而不是 --full是一样的。

最后还有术语混淆: 不幸的是,rails plugin -h显示了以下描述:

[—— full ] # 为测试生成一个带有捆绑的 Rails 应用程序的 轨道发动机
[—— mount ] # 生成可挂载的独立应用程序

这给人的印象是,您使用 --full创建一个“引擎”,使用 --mountable创建另一个称为“可挂载应用程序”的东西,而实际上它们都是引擎——一个名称空间,一个不是。这必然会导致混乱,因为寻求创建引擎的用户可能会认为 --full是更相关的选项。

结论

  • rails plugin new something --full = Engine in your app's namespace. (Why would you?)
  • rails plugin new something --mountable = 拥有自己名称空间的引擎

References