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

And*_*rei 15 rubygems ruby-on-rails rails-engines ruby-on-rails-3

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

Jam*_*s H 10

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

只需使用Rails :: Railtie.它们具有所有相同的功能,但其作用方式与引擎不同.您可以访问整个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
Run Code Online (Sandbox Code Playgroud)

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

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

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


Obi*_*bie 7

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

  • 我知道这个问题专门针对控制器,但是这个问题出现在引擎的/ app目录中.当引擎调用特定的模型名称时(例如:在UsersController中调用User),此解决方案对于模型等不起作用.您不能只创建User的子类,而无需修改引擎的代码来调用该子类名称.我对重写路由的想法也并不陌生,特别是当它们在引擎中定义时.IMO AS:Dependencies补丁是目前最好的方式...... (5认同)