chi*_*ior 51 ruby activerecord identity equality ruby-on-rails
在Ruby 1.9.2on中Rails 3.0.3,我试图测试两个Friend(类继承自ActiveRecord::Base)对象之间的对象相等性.
对象相等,但测试失败:
Failure/Error: Friend.new(name: 'Bob').should eql(Friend.new(name: 'Bob'))
expected #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
(compared using eql?)
Run Code Online (Sandbox Code Playgroud)
只是为了笑容,我还测试了对象身份,它失败了,正如我所料:
Failure/Error: Friend.new(name: 'Bob').should equal(Friend.new(name: 'Bob'))
expected #<Friend:2190028040> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
got #<Friend:2190195380> => #<Friend id: nil, event_id: nil, name: 'Bob', created_at: nil, updated_at: nil>
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
'actual.should == expected' if you don't care about
object identity in this example.
Run Code Online (Sandbox Code Playgroud)
有人可以向我解释为什么对象相等的第一次测试失败,以及我如何成功断言这两个对象是否相等?
noo*_*odl 47
Rails故意将相等性检查委托给标识列.如果您想知道两个AR对象是否包含相同的内容,请比较两者上调用#attributes的结果.
Tyl*_*ick 20
如果你想比较根据其属性两种型情况下,你可能会想排除从您比较某些无关的属性,如:id,created_at,和updated_at.(我会认为那些关于记录的元数据比记录数据本身的一部分更多.)
当您比较两个新的(未保存的)记录时,这可能无关紧要(因为id,created_at并且updated_at将nil一直保存),但我有时发现有必要将保存的对象与未保存的对象进行比较(在这种情况下==会给你假,因为没有!= 5).或者我想比较两个保存的对象以确定它们是否包含相同的数据(因此ActiveRecord ==运算符不起作用,因为如果它们具有不同id的,则返回false ,即使它们在其他方面相同).
我对这个问题的解决方案是在你想要使用属性进行比较的模型中添加这样的东西:
def self.attributes_to_ignore_when_comparing
[:id, :created_at, :updated_at]
end
def identical?(other)
self. attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s)) ==
other.attributes.except(*self.class.attributes_to_ignore_when_comparing.map(&:to_s))
end
Run Code Online (Sandbox Code Playgroud)
然后在我的规范中,我可以编写如此可读和简洁的东西:
Address.last.should be_identical(Address.new({city: 'City', country: 'USA'}))
Run Code Online (Sandbox Code Playgroud)
我正在计划分支active_record_attributes_equality宝石并更改它以使用此行为,以便更容易重复使用.
但是我的一些问题包括:
==运算符是一个好主意,所以现在我正在调用它identical?.不过,也许是这样practically_identical?或attributes_eql?会更准确,因为如果他们不是检查严格相同的(某些属性被允许有所不同.)...attributes_to_ignore_when_comparing太冗长了.如果他们想要使用gem的默认值,则不需要将其显式添加到每个模型中.也许允许使用类宏覆盖默认值ignore_for_attributes_eql :last_signed_in_at, :updated_at 欢迎评论......
更新:active_record_attributes_equality我写了一个全新的gem,active_record_ignored_attributes,而不是分叉,可以在http://github.com/TylerRick/active_record_ignored_attributes和http://rubygems.org/gems/active_record_ignored_attributes上找到.