Rails 引擎扩展功能

发布于 2024-09-03 15:01:46 字数 156 浏览 2 评论 0原文

我有一个定义一些模型和控制器的引擎。我希望能够扩展应用程序中某些模型/控制器的功能(例如添加方法),而不会丢失引擎的原始模型/控制器功能。我到处都读到,你只需要在应用程序中定义具有相同名称的控制器,Rails 会自动合并它们,但是它对我不起作用,引擎中的控制器被简单地忽略(我认为它甚至没有加载)。

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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

糖果控 2024-09-10 15:01:46
require MyEngine::Engine.root.join('app', 'models', 'my_engine', 'my_model')

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

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

before the model class definition in your application.

等风来 2024-09-10 15:01:46

您可以将这些行添加到 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

然后您可以在主应用程序(使用引擎的应用程序)中从引擎请求必要的文件。这很好,因为您可以维护 Rails Engines 的默认功能,并且还拥有一个简单的工具来使用正常的 ruby​​ 继承,而无需修补。

前任:

#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"

You can add these lines to you engine's module file in the lib root directory:

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.

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"
久伴你 2024-09-10 15:01:46

如果其他人将来某个时候遇到同样的问题,这就是我编写的解决问题的代码:

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

如果文件路径以“app”开头,这将在从应用程序请求之前自动从引擎请求文件。

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'.

贪了杯 2024-09-10 15:01:46

确实如此。将使用最先找到的控制器。

因此,要使其正常工作,您可能有两个选择:

  • 创建控制器的本地副本,并修改所需的方法。
  • 如果您可以控制插件,则可以创建一个包含代码和 包含两个控制器中的代码,仅覆盖您的方法本地控制器。
    据我所知,由于没有多重继承,所以这是唯一的方法。

希望这有帮助。

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.

若相惜即相离 2024-09-10 15:01:46

您可以更改引擎的加载顺序,以避免每个型号的要求。

在 config/application.rb 中添加以下行:

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

这将确保 MyEngine 中的模型在 MyApp 之前加载

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:

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

紫南 2024-09-10 15:01:46

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

I've never used Engines before but can't you define a new controller that inherits from the controller provided by the engine

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文