2011-01-19 14 views
43

Rails 3.0.3 पर Ruby 1.9.2 में, मैं ऑब्जेक्ट समानता के लिए दो Friend (वर्ग ActiveRecord::Base से प्राप्त विरासत) वस्तुओं के बीच परीक्षण करने का प्रयास कर रहा हूं।ऑब्जेक्ट समानता (ActiveRecord) के लिए परीक्षण कैसे करें

ऑब्जेक्ट्स बराबर हैं, लेकिन परीक्षण विफल रहता है:

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?) 

सिर्फ grins के लिए, मैं ऑब्जेक्ट पहचान के लिए भी परीक्षण करता हूं, जो विफल होने पर विफल रहता है:

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. 

क्या कोई समझा सकता है मेरे लिए ऑब्जेक्ट समानता के लिए पहला परीक्षण विफल क्यों होता है, और मैं उन दो वस्तुओं को सफलतापूर्वक कैसे जोर दे सकता हूं?

उत्तर

36

रेल जानबूझकर पहचान कॉलम पर समानता जांच का प्रतिनिधित्व करते हैं। यदि आप जानना चाहते हैं कि दो एआर ऑब्जेक्ट्स में एक ही सामान है, तो दोनों पर #attributes कॉल करने के परिणाम की तुलना करें।

+3

पर लेकिन इस मामले में उपलब्ध पहचान स्तंभ 'दोनों मामलों के लिए nil' क्योंकि न तो मधुमक्खी है एन बचाया। 'eql?()' मान और मान दोनों प्रकार की जांच करता है। 'nil.class == nil.class'' true' है और 'nil == nil'' true' है, इसलिए ओपी का पहला उदाहरण अभी भी सत्य होना चाहिए था। आपका उत्तर यह नहीं बताता कि यह झूठी क्यों लौट रहा है। – Jazz

+2

यह सिर्फ अंधा की तुलना नहीं करता है, यह केवल आईडी की तुलना करता है यदि आईडी अर्थपूर्ण हैं। जैसा कि एंडी लिंडमैन के जवाब में बताया गया है: "नए रिकॉर्ड परिभाषा के किसी भी अन्य रिकॉर्ड से अलग हैं"। – Lambart

33

ActiveRecord::Base

रिटर्न सच के लिए == (उर्फ eql?) संचालन पर API docs पर एक नजर डालें, तो comparison_object एक ही सटीक वस्तु है, या comparison_object एक ही प्रकार के है और स्वयं एक आईडी है और यह compar_object.id के बराबर है।

ध्यान दें कि नए रिकॉर्ड परिभाषा द्वारा किसी अन्य रिकॉर्ड से अलग हैं, जब तक कि अन्य रिकॉर्ड रिसीवर स्वयं न हो। इसके अलावा, यदि आप मौजूदा रिकॉर्ड्स को चुनकर चुनते हैं और आईडी छोड़ देते हैं, तो आप स्वयं ही हैं, यह भविष्य गलत होगा।

ध्यान दें कि एक रिकॉर्ड को नष्ट करने से मॉडल उदाहरण में इसकी आईडी को बरकरार रखा जाता है, इसलिए हटाए गए मॉडल अभी भी तुलनीय हैं। id, created_at, और updated_at:

+1

रेल 3.2.8 के लिए अद्यतन एपीआई दस्तावेज़ लिंक http://www.rubydoc.info/docs/rails/3.2.8/frames इसके अलावा, उल्लेखनीय है कि 'eql?' ओवरराइड है, लेकिन उपनाम 'बराबर नहीं है? जो अभी भी 'object_id' की तुलना करता है –

16

आप दो मॉडल उनकी विशेषताओं के आधार पर उदाहरणों की तुलना करना चाहते हैं, तो आप शायद जैसे अपनी तुलना से कुछ अप्रासंगिक गुण, बाहर करना चाहेंगे। (मैं उन अधिक मेटाडाटा रिकॉर्ड के डेटा का ही एक हिस्सा से रिकॉर्ड के बारे में होना करने के लिए विचार किया जाएगा।)

यह कोई फर्क नहीं दे सकते हैं, दो नए (सहेजे गए) रिकॉर्ड (id के बाद से तुलना कर रहे हैं, created_at, और updated_at होगा सभी nil सहेजे जाने तक), लेकिन मुझे कभी-कभी ऑब्जेक्ट सहेजे गए एक के साथ तुलना करने के लिए आवश्यक लगता है (जिसमें मामला == आपको शून्य से झूठा देगा! = 5)। या मैं, दो बचाया वस्तुओं पता लगाने के लिए अगर वे एक ही डेटा (ताकि ActiveRecord == ऑपरेटर काम नहीं करता है को शामिल तुलना करना चाहते हैं, क्योंकि यह गलत रिटर्न ही वे अलग id की है, भले ही वे अन्यथा समान हैं)।

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 
तब मेरे चश्मा में

मैं इस रूप में इस तरह पठनीय और संक्षिप्त बातें लिख सकते हैं::

इस समस्या का समाधान मेरे मॉडल है कि आप तुलनीय का उपयोग कर गुण होना चाहता हूँ में कुछ इस तरह जोड़ना है

Address.last.should be_identical(Address.new({city: 'City', country: 'USA'})) 

मैं active_record_attributes_equality मणि को फोर्क करने और इस व्यवहार का उपयोग करने के लिए इसे बदलने पर योजना बना रहा हूं ताकि इसे अधिक आसानी से पुन: उपयोग किया जा सके।

कुछ सवाल मेरे पास है, हालांकि, शामिल हैं:

  • इस तरह के एक मणि को पहले से ही मौजूद है ??
  • विधि को क्या कहा जाना चाहिए? मुझे नहीं लगता कि मौजूदा == ऑपरेटर ओवरराइड करना एक अच्छा विचार है, इसलिए अब मैं इसे identical? पर कॉल कर रहा हूं। लेकिन शायद practically_identical? या attributes_eql? की तरह कुछ ज्यादा सही होगा, क्योंकि यह जाँच नहीं कर रहा है अगर वे सख्ती से समान हैं (विशेषताओं में से कुछ अलग होने की अनुमति दी जाती है।) ...
  • attributes_to_ignore_when_comparing भी वर्बोज़ है। ऐसा नहीं है कि अगर वे मणि के डिफ़ॉल्ट का उपयोग करना चाहते हैं तो इसे प्रत्येक मॉडल में स्पष्ट रूप से जोड़ा जाना चाहिए। active_record_attributes_equality forking के बजाय, मैं एक नया मणि, active_record_ignored_attributes लिखा है: हो सकता है कि डिफ़ॉल्ट की तरह ignore_for_attributes_eql :last_signed_in_at, :updated_at

टिप्पणियाँ स्वागत कर रहे हैं ...

अद्यतन एक वर्ग मैक्रो के साथ अधिरोहित जा करने की अनुमति , http://github.com/TylerRick/active_record_ignored_attributes और http://rubygems.org/gems/active_record_ignored_attributes

1
META = [:id, :created_at, :updated_at, :interacted_at, :confirmed_at] 

def eql_attributes?(original,new) 
    original = original.attributes.with_indifferent_access.except(*META) 
    new = new.attributes.symbolize_keys.with_indifferent_access.except(*META) 
    original == new 
end 

eql_attributes? attrs, attrs2 
संबंधित मुद्दे