我们有一个应用程序,它使用Sequel gem连接到数据源,执行一些工作,然后返回一个结果,该结果具有附加到该对象的singleton_class
的许多便利方法。在ruby 2.3中,此代码按预期工作:
result = EpulseDB::Employee.where(normalized_args)
result.singleton_class.include(EpulseNormalization)
我们可以看到使用ruby 2.3.4,singleton_class没有被冻结:
[1] pry(main)> result = EpulseDB::Employee.where(employee_id: 2)
=> #<Sequel::Postgres::Dataset: "SELECT * FROM \"employee\" WHERE (\"employee_id\" = 2)">
[2] pry(main)> result.frozen?
=> true
[3] pry(main)> result.singleton_class.frozen?
=> false
[4] pry(main)> result.singleton_class.include(EpulseNormalization)
=> #<Class:#<Sequel::Postgres::Dataset:0x007feff0903660>>
但是在Ruby 2.4.2中,singleton_class
似乎被冻结了,我们无法再扩展它。是否有一种新方法可以扩展我应该使用的单例?
[1] pry(main)> result = EpulseDB::Employee.where(employee_id: 2)
=> #<Sequel::Postgres::Dataset: "SELECT * FROM \"employee\" WHERE (\"employee_id\" = 2)">
[2] pry(main)> result.frozen?
=> true
[3] pry(main)> result.singleton_class.frozen?
=> true
[4] pry(main)> result.singleton_class.include(EpulseNormalization)
RuntimeError: can't modify frozen object
from (pry):4:in `append_features'
答案 0 :(得分:2)
使用Dataset#with_extend
返回使用模块扩展的数据集的修改副本,而不是调用Dataset#extend
来修改数据集本身。这适用于Sequel支持的所有版本的ruby。
背景故事:这与Ruby本身无关,这是由于Sequel中缺少Ruby&lt; 2.4中的功能而导致的解决方法。
在Ruby&lt; 2.4中,Object#freeze
无法处理Object#clone
用于创建冻结对象的修改副本(包括对象的单例类的副本)的情况。 Ruby 2.4将freeze: false
选项添加到Object#clone
以允许创建冻结对象的修改副本,包括其单例类(请参阅https://bugs.ruby-lang.org/issues/12300)。
Sequel :: Dataset在内部使用#clone
来返回修改后的数据集,并且要求数据集包含用于正常运行的任何单例类的副本。因为我希望冻结Sequel :: Dataset,但仍然可以使用ruby&lt; 2.4,它基本上假装被红宝石冻结<2.4。它只在红宝石2.4中真正冻结。参见: