为什么当 cache_classes = false 时,Rails 引擎初始化程序中的包含会出现故障?

发布于 2024-11-04 15:05:55 字数 953 浏览 1 评论 0原文

我有一个引擎,它在其初始化程序中扩展另一个引擎的类,如下所示:

module MyApp
    class Engine < ::Rails::Engine
        initializer 'extend Product' do
            AnotherApp::Product.send :include, MyApp::ProductExtender
        end
    end
end

ProductExtender 模块在包含 AnotherApp::Product 时调用其上的一些方法,例如,

module ProductExtender
    def self.included( model )
        model.send :include, MethodsToCall
    end

    module MethodsToCall
        def self.included( m )
            m.has_many :variations
        end
    end
end

这适用于测试和生产环境,但是当 config.cache_classes = false 时,当我尝试调用 ProductExtender 定义的内容(例如 @product.variations)时,它会向我抛出 NoMethodError

不用说,看到我所有的测试都通过了,然后却因为开发中的错误而受到重创,真是令人不寒而栗。当我设置 cache_classes = true 时,这种情况不会发生,但这让我想知道我是否在做一些不应该做的事情。

我的问题是双重的:为什么会发生这种情况,是否有更好的方法我应该实现在另一个应用程序的对象上扩展/调用方法的功能?

谢谢大家!

I have an Engine which is extending another Engine's classes in its initializers like so:

module MyApp
    class Engine < ::Rails::Engine
        initializer 'extend Product' do
            AnotherApp::Product.send :include, MyApp::ProductExtender
        end
    end
end

The ProductExtendermodule calls some methods on the AnotherApp::Product when it is included, e.g.

module ProductExtender
    def self.included( model )
        model.send :include, MethodsToCall
    end

    module MethodsToCall
        def self.included( m )
            m.has_many :variations
        end
    end
end

This works in test and production environments, but when config.cache_classes = false, it throws a NoMethodError at me when I try to call something defined by the ProductExtender, like @product.variations.

Needless to say, it is chilling to see all my tests pass and then get slammed with an error in development. It doesn't happen when I set cache_classes = true, but it makes me wonder if I'm doing something I shouldn't be.

My question is twofold: Why is this happening, and is there a better way I should be achieving this functionality of extending/calling methods on another application's object?

Thanks all!

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

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

发布评论

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

评论(2

海的爱人是光 2024-11-11 15:05:55

我设法使用 to_prepare 块而不是初始化器解决了这个问题。 to_prepare 块在生产中和开发中的每个请求之前执行一次,因此似乎满足我们的需求。

当我研究 Rails::Engine 时,这一点并不明显,因为它继承自 Rails::Railtie::Configuration

因此,我不会使用问题中的代码,而是:

module MyApp
    class Engine < ::Rails::Engine
        config.to_prepare do
            AnotherApp::Product.send :include, MyApp::ProductExtender
        end
    end
end

I managed to solve this problem using a to_prepare block instead of the initializer. The to_prepare block executes once in production and before each request in development, so seems to meet our needs.

It wasn't obvious when I was researching Rails::Engine since it is inherited from Rails::Railtie::Configuration.

So instead of the code in the question, I would have:

module MyApp
    class Engine < ::Rails::Engine
        config.to_prepare do
            AnotherApp::Product.send :include, MyApp::ProductExtender
        end
    end
end
2024-11-11 15:05:55

cache_classes 实际上有一个误导性的名称:不涉及缓存。如果将此选项设置为 false,rails 会显式卸载您的应用程序代码并在需要时重新加载。这使得您在开发过程中所做的更改无需重新启动(服务器)进程即可生效。

在您的情况下,AnotherApp::Product 会被重新加载,ProductExtender 也是如此,但初始化程序在重新加载后不会再次触发,因此 AnotherApp::Product 不会“扩展”。

我非常了解这个问题,并最终使用 cache_classes = true 运行我的开发环境,并偶尔重新启动我的服务器。我在引擎/插件上没有太多开发要做,所以这是最简单的方法。

cache_classes has actually a misleading name: There is no caching involved. If you set this option to false, rails explicitly unloads your application code and reloads it when needed. This enables for changes you make in development to have an effect without having to restart the (server) process.

In your case, AnotherApp::Product is reloaded, as is ProductExtender, but the initializer is not fired again, after the reload, so AnotherApp::Product is not 'extended'.

I know this problem very well and ended up running my development environment with cache_classes = true and occasionally restart my server. I had not so much development to do on engines/plugins, so this was the easiest way.

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