在主应用程序中扩展 Rails 3 引擎的控制器
我在我的应用程序中使用 Rails 引擎作为 gem。该引擎有 PostsController
和许多方法,我想在我的主应用程序中扩展控制器逻辑,例如添加一些方法。如果我只是在主应用程序中创建 PostsController
,则不会加载引擎的控制器。
Rails 引擎扩展功能 中提出了一个基于更改 ActiveSupport: 的解决方案: Dependency#require_or_load
这是执行此操作的唯一/正确方法吗?如果是的话,我应该把这段代码放在哪里?
EDIT1:
module ActiveSupport::Dependencies
alias_method :require_or_load_without_multiple, :require_or_load
def require_or_load(file_name, const_path = nil)
if file_name.starts_with?(RAILS_ROOT + '/app')
relative_name = file_name.gsub(RAILS_ROOT, '')
@engine_paths ||= Rails::Initializer.new(Rails.configuration).plugin_loader.engines.collect {|plugin| plugin.directory }
@engine_paths.each do |path|
engine_file = File.join(path, relative_name)
require_or_load_without_multiple(engine_file, const_path) if File.file?(engine_file)
end
end
require_or_load_without_multiple(file_name, const_path)
end
end
I am using a Rails engine as a gem in my app. The engine has PostsController
with a number of methods and I would like to extend the controller logic in my main app, e.g. to add some methods. If I just create PostsController
in the main app, then the engine's controller is not loaded.
There is a solution proposed in question Rails engines extending functionality based on altering ActiveSupport::Dependencies#require_or_load
Is it the only/correct way to do this? If yes, where do I put that piece of code?
EDIT1:
This is the code suggested by Andrius for Rails 2.x
module ActiveSupport::Dependencies
alias_method :require_or_load_without_multiple, :require_or_load
def require_or_load(file_name, const_path = nil)
if file_name.starts_with?(RAILS_ROOT + '/app')
relative_name = file_name.gsub(RAILS_ROOT, '')
@engine_paths ||= Rails::Initializer.new(Rails.configuration).plugin_loader.engines.collect {|plugin| plugin.directory }
@engine_paths.each do |path|
engine_file = File.join(path, relative_name)
require_or_load_without_multiple(engine_file, const_path) if File.file?(engine_file)
end
end
require_or_load_without_multiple(file_name, const_path)
end
end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
根据设计,Rails::Engine 中的类应该限定在引擎范围内。这样,他们就不会因意外破坏主应用程序或其他引擎加载的所有代码而引入奇怪的错误。 Monkeypatching ActiveSupport::Dependency 来全面混合引擎是一个非常糟糕的解决方法。
只需使用 Rails::Railtie 即可。它们具有相同的功能,但范围与引擎不同。您可以访问整个 Rails 应用程序堆栈(包括引擎)。这是一种更像外科手术的方法。
就文件布局而言,railties 看起来与引擎完全相同。
进一步阅读:使用 Railties 扩展 Rails 3
如果你还是很困惑,看看这个 git 项目,它有一个完整的实现: https://github.com/ jamezilla/bcms_pubcookie
By design, classes in a Rails::Engine are supposed to be scoped to the engine. That way they don't introduce strange bugs by accidentally stomping all over code loaded in the main app or by other engines. Monkeypatching ActiveSupport::Dependencies to mix engines across-the-board is a really bad workaround.
Just use a Rails::Railtie, instead. They have all the same functionality, but aren't scoped the same way as an engine. You have access to the entire rails app stack (including engines). It's a more surgical approach.
As far as file layout, railties look exactly like engines.
Further reading: Extending Rails 3 with Railties
And if you're still confused, take a look at this git project which has a full implementation: https://github.com/jamezilla/bcms_pubcookie
为什么不直接继承应用程序中引擎的控制器类(并将路由指向新的子控制器)?听起来概念上类似于扩展内置 Devise 控制器的方式。
Why not just inherit from the Engine's controller class in your application (and point your routes at the new child controllers)? Sounds conceptually similar to the way that you extend built-in Devise controllers.
方法 1
这是我在
require 'rails/all'
之后在application.rb
中的 Rails 3 应用程序中放置的内容(如果放置位置不好,请告诉我) )有一段时间这不起作用
,但这是由于类定义输入错误
class PostsController <; ActionController::Base
应该是class PostsController
ApplicationController
方法2
如果您不想对所有引擎控制器等执行此操作,您可以在主应用程序中的定义之前加载引擎的控制器
Method 1
Here is what I put in my Rails 3 app in
application.rb
afterrequire 'rails/all'
(let me know if it is a bad place to put it)For a while this didn't work raising
but that was due to a mistyped class definition
class PostsController < ActionController::Base
which should beclass PostsController < ApplicationController
Method 2
If you do not want to do this for all engine controllers etc., you can load the engine's controller before the definition in the main app
我根据上面 Andrius 和 Andrei 的代码创建了一个 gem。无需复制该代码,只需使用 mixable_engines gem。目前仅适用于 Rails 3。
https://github.com/asee/mixable_engines
https://rubygems.org/gems/mixable_engines
@Andrei 和 @Artrius:我已在许可证文件中注明了您的身份,如果您想要真实的,请告诉我姓名或其他信用。
I've created a gem based on the code from Andrius and Andrei above. Instead of copying around that code, just require the mixable_engines gem. Only works with rails 3 right now.
https://github.com/asee/mixable_engines
https://rubygems.org/gems/mixable_engines
@Andrei and @Artrius: I've credited you in the license file, let me know if you want your real name or some other credit.
如果您不希望补丁主动支持按照 Rails 引擎扩展功能中的建议更改加载顺序,您可以利用机架中间件进行身份验证。如果身份验证作为每个控制器操作的一部分完成,那么这种方法可能会节省大量代码和时间。
If you do not want patch active support to change the load order as suggested in Rails engines extending functionality, you can make use of a rack middleware for authentication. If authentication is done as part of every controller action, this approach might save you lot of code and time.
您可以使用 Ruby 的
send()
方法在创建引擎时将代码注入到控制器中...此方法使您无需在主应用程序中重新定义控制器继承。
You can use Ruby's
send()
method to inject your code into the controller at the time the engine is created...This method spares you from having to redefine the controller inheritance within your main app.
@cowboycoded 方法 2 与 require_dependency 和 config.reload_plugins 结合使用,在 Rails 3.2.2 / Ruby 1.9 上为我工作。
这是代码:https://stackoverflow.com/a/9790497/22237
@cowboycoded method 2 in conjunction with
require_dependency
andconfig.reload_plugins
worked for me on Rails 3.2.2 / Ruby 1.9.Here is the code: https://stackoverflow.com/a/9790497/22237