在从Ruby 1.9.3升级到Ruby 2.2.3(MRI)的过程中,我发现一个问题会影响从Hash
继承的任何类。如果在继承自Hash
的类的实例上调用#reject
,它将始终返回Hash
而不是调用它的类的实例。Ruby 2.2哈希#拒绝返回哈希继承类?
例如,给定下面的代码:
class CustomHash < Hash
def count_in_english
"There are #{self.count} items in this hash."
end
end
对于红宝石1.9.3,下面的成功:
1.9.3-p547 :060 > hash = CustomHash.new
=> {}
1.9.3-p547 :061 > hash[1] = 'a'
=> "a"
1.9.3-p547 :062 > hash[2] = 'b'
=> "b"
1.9.3-p547 :063 > hash[3] = 'c'
=> "c"
1.9.3-p547 :064 > odds_only_hash = hash.reject { |k,v| k % 2 == 0 }
=> {1=>"a", 3=>"c"}
1.9.3-p547 :065 > odds_only_hash.count_in_english
=> "There are 2 items in this hash."
1.9.3-p547 :066 > odds_only_hash.class
=> CustomHash
但是在Ruby 2.2.3:
2.2.3 :019 > hash = CustomHash.new
=> {}
2.2.3 :020 > hash[1] = 'a'
=> "a"
2.2.3 :021 > hash[2] = 'b'
=> "b"
2.2.3 :022 > hash[3] = 'c'
=> "c"
2.2.3 :023 > odds_only_hash = hash.reject { |k,v| k % 2 == 0 }
=> {1=>"a", 3=>"c"}
2.2.3 :024 > odds_only_hash.count_in_english
NoMethodError: undefined method `count_in_english' for {1=>"a", 3=>"c"}:Hash
from (irb):24
from /Users/davidelner/.rvm/rubies/ruby-2.2.3/bin/irb:15:in `<main>'
2.2.3 :025 > odds_only_hash.class
=> Hash
经过一番搜索后,它看起来像this is known by the Ruby devs,是discussed a little bit,详细我n this blog post。这种变化也打破Rails的HashWithIndifferentAccess
按this issue,为此,他们发出a pull request for Rails 4(但仍然在Rails的3.2.22坏了?)
显然,这种行为抓了很多人猝不及防,并改变声音可笑考虑如何它打破了已知的Ruby gem宇宙(包括Rails,Hashie等),它们依赖于这个基本思想,即对象不应该意外地改变类型。
我的问题,对于一些更明智的Ruby开发者的是:
- 是红宝石承诺的想法,在Ruby中2.2.3,和未来的所有版本,
Hash#reject
将始终返回Hash
? (与调用此函数的类的实例相反,例如1.9.3 C源return rb_hash_delete_if(rb_obj_dup(hash));
) - 如果是这样,为什么现在这种默认行为?这是不是有效'密封'
#reject
,并打破合理的期望,使用这些Enumerable
函数仍然会返回一个相同类型的对象? - 此外,如果是这样,开发人员希望如何适应这种行为变化? (难道我们都会像Rails团队那样做?)
我认为这属于不从核心类继承的普遍共识。 [Nice Article](http://words.steveklabnik.com/beware-subclassing-ruby-core-classes) – engineersmnky
对链接的Ruby错误的评论不是第一个回答的两个问题吗? 1.是的,2.因为之前的行为是错误的。查看其对应的'Hash#select',它总是返回一个“Hash”实例 - 即使在1.9.3和以前的版本中。 – cremno
不要错,这不是'Hash'的专业化。你可以告诉这种情况,因为派生类只是使用底层的'Hash'实例,并且与它没有其他关系。我认为这里的根本问题是你的'Hash'的子类并不真正和'Hash'有一个“是-α”的关系。它只是用'Hash'来实现的,显然你希望它有一个类似于'Hash'的接口,但是单凭这一点不足以使用继承。这显然是一种“一种”的关系。 –