在主应用程序中扩展Rails 3引擎的控制器

我在我的应用程序中使用Rails引擎作为gem。 引擎有PostsController ,有很多方法,我想在我的主应用程序中扩展控制器逻辑,例如添加一些方法。 如果我只是在主应用程序中创建PostsController ,则不会加载引擎的控制器。

有一个解决方案提出了Rails引擎,它基于改变ActiveSupport::Dependencies#require_or_load来扩展function

这是唯一/正确的方法吗? 如果是,我在哪里放这段代码?

EDIT1:

这是Andrius为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 

为什么不直接从应用程序中的Engine控制器类inheritance(并将路由指向新的子控制器)? 声音在概念上类似于扩展内置Devise控制器的方式。

按照设计,Rails :: Engine中的类应该作为引擎的范围。 这样他们就不会通过意外地重复主应用程序或其他引擎中加载的所有代码来引入奇怪的错误。 Monkeypatching ActiveSupport ::依赖于将引擎混合在一起的依赖性是一个非常糟糕的解决方法。

只需使用Rails :: Railtie。 它们具有所有相同的function,但其作用方式与引擎不同。 您可以访问整个rails应用程序堆栈(包括引擎)。 这是一种更外科的方法。

 module MyModule module SomeModelExtensions # Called when this module is included on the given class. def self.included(base) base.send(:include, InstanceMethods) base.extend(ClassMethods) end module ClassMethods def some_new_class_method # do stuff... end end module InstanceMethods def some_new_instance_method # do stuff... end end end module SomeControllerExtensions def self.included(base) base.send(:include, InstanceMethods) base.alias_method_chain :new, :my_module end module InstanceMethods # override the 'new' method def new_with_my_module # do stuff end end end class Railtie < ::Rails::Railtie # The block you pass to this method will run for every request in # development mode, but only once in production. config.to_prepare do SomeModel.send(:include, MyModule::SomeModelExtensions) SomeController.send(:include, MyModule::SomeControllerExtensions) end end end 

就文件布局而言,铁路看起来与发动机完全一样。

进一步阅读: 使用Railties扩展Rails 3

如果你仍然感到困惑,请看看这个具有完整实现的git项目: https : //github.com/jamezilla/bcms_pubcookie

方法1

这是我在application.rb放入我的Rails 3应用application.rbrequire 'rails/all' (让我知道它是不是放置它的地方)

 require 'active_support/dependencies' 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.to_s + '/app') relative_name = file_name.gsub(Rails.root.to_s, '') #@engine_paths ||= Rails::Application.railties.engines.collect{|engine| engine.config.root.to_s } #EDIT: above line gives deprecation notice in Rails 3 (although it works in Rails 2), causing error in test env. Change to: @engine_paths ||= YourAppName::Application.railties.engines.collect{|engine| engine.config.root.to_s } @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 

有一段时间,这不起作用

 TypeError in PostsController#index superclass mismatch for class PostsController 

但那是因为错误的类定义class PostsController < ActionController::Base应该是class PostsController < ApplicationController

方法2

如果您不想对所有引擎控制器等执行此操作,则可以在主应用程序中定义之前加载引擎的控制器

 require PostsEngine::Engine.config.root + 'app' + 'controllers' + 'posts_controller' class PostsController < ApplicationController # extended methods end 

我根据上面Andrius和Andrei的代码创建了一个gem。 而不是复制该代码,只需要mixable_engines gem。 仅适用于rails 3。

https://github.com/asee/mixable_engines

https://rubygems.org/gems/mixable_engines

@Andrei和@Artrius:我已经在许可证文件中记下了您,如果您想要真实姓名或其他信用,请告诉我。

如果您不希望补丁活动支持按照Rails引擎扩展function中的建议更改加载顺序,则可以使用机架中间件进行身份validation。 如果身份validation是作为每个控制器操作的一部分完成的,则此方法可能会节省大量代码和时间。

您可以使用Ruby的send()方法在创建引擎时将代码注入控制器…

 # lib/cool_engine/engine.rb module CoolEngine class Engine < ::Rails::Engine isolate_namespace CoolEngine initializer "cool_engine.load_helpers" do |app| # You can inject magic into all your controllers... ActionController::Base.send :include, CoolEngine::ActionControllerExtensions ActionController::Base.send :include, CoolEngine::FooBar # ...or add special sauce to models... ActiveRecord::Base.send :include, CoolEngine::ActiveRecordExtensions ActiveRecord::Base.send :include, CoolEngine::MoreStuff # ...even provide a base set of helpers ApplicationHelper.send :include, CoolEngine::Helpers end end end 

此方法使您不必重新定义主应用程序中的控制器inheritance。

@cowboycoded方法2与require_dependencyconfig.reload_plugins一起在Rails 3.2.2 / Ruby 1.9上为我工作。

这是代码: https : //stackoverflow.com/a/9790497/22237