我试图理解为什么ActionController::API
的祖先在它的类被加载后,当一个模块被显式地添加到它的层次结构中时,它不会被更新。
我有以下铁路:
module MyModule
class Railtie < Rails::Railtie
initializer "mymodule.initialize" do |app|
# Force Rails to load view templates even in API mode
if Rails::VERSION::MAJOR >= 5
puts ActionController::API.ancestors.include?(ActionView::Rendering)
module ::ActionController
module ApiRendering
include ActionView::Rendering
end
end
puts ActionController::API.ancestors.include?(ActionView::Rendering)
end
end
end
end
其显示:
false
false
如果我注解了第一个puts
(这触发了ActionController::API
的加载),第二个puts
的输出会改变:
true
在普通Ruby中,这种更新工作得很好,所以Rails必须做一些我不熟悉的事情。
module A; end
module B; end
class API
include A
end
module A
include B
end
API.ancestors.include?(B) # returns true
上下文:scout_apm
在需要时会在jbuilder
之前触发此问题,因此我设法重现了一个最小场景。
1条答案
按热度按时间6psbrbz91#
此功能适用于ruby
3.0
或更高版本(源代码)。如果您在2.7
版本上运行示例代码,您将获得false
作为API.ancestors.include?(B)
的结果。然而,有一种情况,即使是新的ruby版本,它也无法工作,那就是当这些模块都是
ActiveSupport::Concern
时,这对于ActionView::Rendering
和ActionController::ApiRendering
来说是正确的。ActiveSupport::Concern
改变了include
的标准行为,并称之为“依赖管理”。当您将一个Concern
包含在另一个Concern
中时,它将其存储为依赖项,而不是实际包含它。稍后,当您在其他地方包含外部Concern
时,首先包含这些依赖项(以前未包含的模块),然后包含实际的模块。这种行为是通过重新定义append_features
方法来实现的。如果您稍后包含另一个
Concern
,它会附加到依赖项中,但不会包含到现有的后代中。