简体   繁体   English

Rails引擎扩展功能

[英]Rails engines extending functionality

I have an engine which defines some models and controllers. 我有一个定义一些模型和控制器的引擎。 I want to be able to extend functionality of some models/controllers in my application (eg. adding methods) without loosing the original model/controller functionality from engine. 我希望能够在我的应用程序中扩展某些模型/控制器的功能(例如添加方法),而不会从引擎中丢失原始模型/控制器功能。 Everywhere I read that you simply need to define controller with the same name in your application and Rails will automatically merge them, however it doesn't work for me and controller in engine is simply ignored (I don't think it's even loaded). 在我读到的任何地方,您只需要在应用程序中定义具有相同名称的控制器,Rails将自动合并它们,但它对我不起作用并且引擎中的控制器被简单地忽略(我不认为它甚至被加载)。

require MyEngine::Engine.root.join('app', 'models', 'my_engine', 'my_model')

在应用程序中的模型类定义之前。

You can add these lines to you engine's module file in the lib root directory: 您可以将这些行添加到lib根目录中的引擎模块文件中:

def self.root
  File.expand_path(File.dirname(File.dirname(__FILE__)))
end

def self.models_dir
  "#{root}/app/models"
end

def self.controllers_dir
  "#{root}/app/controllers"
end

Then you have the ability in the main application (the app making use of the engine) to require the necessary files from the engine. 然后,您可以在主应用程序(使用引擎的应用程序)中从引擎中获取必要的文件。 This is nice because you maintain Rails Engines default functionality and also have an easy tool for making use of normal ruby inheritance, without the need for patching. 这很好,因为你维护了Rails Engines的默认功能,并且还有一个简单的工具来使用普通的ruby继承,而不需要修补。

EX: EX:

#ENGINE Model -

class User < ActiveRecord::Base
  def testing_engine
    puts "Engine Method"  
  end
end

#MAIN APP Model -

require "#{MyEngine.models_dir}/user"
class User
  def testing_main_app
    puts "Main App Method"  
  end
end

#From the Main apps console

user = User.new

puts user.testing_engine #=>  "Engine Method"

puts user.tesing_main_app #=> "Main App Method"

Just if anyone else runs into same issue some time in the future, this is the code I wrote that fixed my problem: 如果其他人在将来的某个时间遇到同样的问题,这就是我编写的修复我的问题的代码:

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

This will automatically require files from engine before requiring from application if file path starts with 'app'. 如果文件路径以“app”开头,这将自动要求应用程序中的文件。

You can change the load order of the engine to avoid the require on each of your models. 您可以更改引擎的加载顺序,以避免每个模型的要求。

In config/application.rb add this line: 在config / application.rb中添加以下行:

module MyApp
  class Application
    config.railties_order = [MyEngine::Engine, :main_app, :all]
  end
end

This will ensure that the models from MyEngine are loaded before MyApp 这将确保在MyApp之前加载MyEngine中的模型

That is true. 那是真实的。 The controller that is found first will be used. 将使用首先找到的控制器。

So to make it work you might have two options: 因此,要使其工作,您可能有两个选择:

  • create a local copy of the controller, and modify the method you need 创建控制器的本地副本,并修改所需的方法
  • if you have control over the plugin, you could create a Module containing the code and include the code in both controllers, only overriding the method in your local controller. 如果你可以控制插件,你可以创建一个包含代码的模块,并在两个控制器中包含代码,只覆盖本地控制器中的方法。 According to me, since there is no multiple inheritance, that is the only way. 据我所知,由于没有多重继承,这是唯一的方法。

Hope this helps. 希望这可以帮助。

我之前从未使用过Engines,但是你不能定义一个继承自引擎提供的控制器的新控制器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM