为什么在Ruby 2.4中冻结singleton_class对象而在2.3中没有?

时间:2017-12-11 19:46:07

标签: ruby sequel ruby-2.4

我们有一个应用程序,它使用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'

1 个答案:

答案 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中真正冻结。参见: