在使用Rails STI(单表继承)时,我定义了一个名为Poi(兴趣点)的模型。
我们的应用程序要求必须在Admin :: Categories视图中创建Poi的子类(如餐厅,俱乐部等)(其中它有一个class_name
字符串输入字段),以便管理员应该能够随时创建新的子类,而无需程序员使用空(无用)子类打开新的ruby文件并重新部署应用程序。
同时,如果将来我们想为Poi的子类指定不同的行为(实例/类方法),我们可以创建该ruby文件,但这应该是一个选项而不是必需的。对于具有该子类的不同字段的不同表单也是如此:我们只需要在该子类中设置一个partial_name_for_form
实例方法,该方法返回具有部分名称的字符串,并且视图将相应地呈现它。如果未找到,则呈现默认的Poi视图。
如果你尝试使用'类型'来设置新的Poi对象,则Rails会引发错误。与Poi的子类不匹配的属性(所以子类必须先前定义),我们提出了以下解决方案,用于基于class_name动态创建Poi子类:
模型类别中的after_create
挂钩,使用此代码立即定义新类:Object.const_set(category.class_name, Class.new(Poi))
模型Poi文件中的require_dependency
调用(因为它在自动加载路径中)要求子类的ruby文件,我们最终创建了硬编码的子类(仅当文件存在时) :
Category.all.each do |category|
require_dependency category.class_name.underscore if File.exist (File.join("app","models","pois","#{category.class_name.underscore}.rb"))
end
即便是所有这些复杂性使得我们在开始时使用STI几乎感到后悔,它仍然正常 - 在开发中。
但是在生产环境中,在创建提供if Object.const_defined? category.class_name
的新类别之后,类没有被定义,因为当尝试使用该子类创建新的Poi时正在引发错误class_name
。
我在生产环境的Rails控制台中确认了after_create挂钩正在工作,因为正在那里定义类。我的猜测是,因为我们使用Unicorn,这个bug可能与应用程序代码的分叉有关,但我不知道如何继续。
答案 0 :(得分:0)
10.5 require_dependency和Initializers
可以考虑在初始化程序中执行一些require_dependency调用,以确保预先加载某些常量,例如尝试用STI解决陷阱。
问题是,在开发模式下,如果文件系统中有任何相关更改,则会擦除自动加载的常量。如果发生这种情况,那么我们处于初始化者希望避免的相同情况下!
来自http://guides.rubyonrails.org/autoloading_and_reloading_constants.html。从我在此处阅读的内容来看,children
中的require_dependency
似乎在不同环境之间有所不同。我发现了一个类似的问题here - 看看是否有帮助。
由于模型损坏,我会避免创建动态常量。