2

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

1 回答 1

2

用于Dataset#with_extend返回使用模块扩展的数据集的修改副本,而不是调用Dataset#extend来修改数据集本身。这适用于 Sequel 支持的所有 ruby​​ 版本。

背景故事:这与 Ruby 本身无关,这是由于 Sequel 中的一种解决方法,因为 Ruby <2.4 中缺少一个功能。

在 Ruby <2.4 中,Object#freeze无法处理Object#clone用于创建冻结对象的修改副本(包括对象的单例类的副本)的情况。Ruby 2.4 添加了允许创建冻结对象的修改副本的freeze: false选项,包括它们的单例类(请参阅https://bugs.ruby-lang.org/issues/12300)。Object#clone

Sequel::Dataset#clone在内部使用以返回修改后的数据集,并且要求数据集包含用于正常运行的任何单例类的副本。因为我希望 Sequel::Dataset 被冻结,但仍然在 ruby​​ < 2.4 上工作,它基本上假装在 ruby​​ <2.4 上被冻结。它仅在 ruby​​ 2.4 中真正冻结。看:

于 2017-12-12T00:41:22.343 回答