From what I have understood, the equal method checks if the object is the same.
person = Person.create!(:name => "David")
Person.find_by_name("David").should equal(person)
This should be true.
But aren't there two different objects here?
How could two objects be the same? I don't understand that.
Rails and RSpec equality tests have a variety of choices.
Rails 3.2 ActiveRecord::Base uses the == equality matcher.
It returns true two different ways:
If self is the same exact object as the comparison object
If self is the same type as the comparison object and has the same ID
Note that ActiveRecord::Base has the == method which is aliased as eql?. This is different than typical Ruby objects, which define == and eql? differently.
RSpec 2.0 has these equality matchers in rspec-expectations:
a.should equal(b) # passes if a.equal?(b)
a.should eql(b) # passes if a.eql?(b)
a.should == b # passes if a == b
RSpec also has two equality matchers intended to have more of a DSL feel to them:
a.should be(b) # passes if a.equal?(b)
a.should eq(b) # passes if a == b
In your example you're creating a record then finding it.
So you have two choices for testing #find_by_name:
To test if it retrieves the exact same object OR an equivalent Person record with the same ID, then use should == or its equivalent a.should eql or its DSL version should eq
To test if it uses the exact same object NOT an equivalent Person record with the same ID, then use should equal or its DSL version should be
equal checks if the reference is the same. It corresponds to the Object#equal? method. You want to use == to compare these objects.
Related
What is the difference between using eq and eql in rspec tests? Is there a difference between:
it "adds the correct information to entries" do
# book = AddressBook.new # => Replaced by line 4
book.add_entry('Ada Lovelace', '010.012.1815', 'augusta.king#lovelace.com')
new_entry = book.entries[0]
expect(new_entry.name).to eq('Ada Lovelace')
expect(new_entry.phone_number).to eq('010.012.1815')
expect(new_entry.email).to eq('augusta.king#lovelace.com')
end
and:
it "adds the correct information to entries" do
# book = AddressBook.new # => Replaced by line 4
book.add_entry('Ada Lovelace', '010.012.1815', 'augusta.king#lovelace.com')
new_entry = book.entries[0]
expect(new_entry.name).to eql('Ada Lovelace')
expect(new_entry.phone_number).to eql('010.012.1815')
expect(new_entry.email).to eql('augusta.king#lovelace.com')
end
There are subtle differences here, based on the type of equality being used in the comparison.
From the Rpsec docs:
Ruby exposes several different methods for handling equality:
a.equal?(b) # object identity - a and b refer to the same object
a.eql?(b) # object equivalence - a and b have the same value
a == b # object equivalence - a and b have the same value with type conversions]
eq uses the == operator for comparison, and eql ignores type conversions.
The differences are subtle.eq is the same as the ruby implementation of ==. On the other hand eql is the same as the ruby implementation of eql?.
eq checks object equivalence and will type cast to convert different object to the same type.
Two objects are equivalent if they are of the same class and have the same value but they are not necessarily the same object in memory.
expect(:my_symbol).to eq(:my_symbol)
# passes, both are identical.
expect('my string').to eq('my string')
# passes, objects are equivalent
expect(5).to eq(5.0)
# passes, Objects are not equivalent but was type cast to same object type.
eql checks object equivalence and does not try type casting.
expect(:my_symbol).to eql(:my_symbol)
# passes, both are identical.
expect('my string').to eql('my string')
# passes, objects are equivalent but not identical
expect(5).to eql(5.0)
# fails, Objects are not equivalence, did not try to type cast
equal checks object identity.
Two object are identical if they are the same object meaning they have same object id (share the same address in memory).
expect(:my_symbol).to equal(:my_symbol)
# passes, both are identical.
expect('my string').to equal('my string')
# fails, objects are equivalent but not identical
expect(5).to equal(5.0)
# fails, objects are not equivalent and not identical
Since Ruby does type conversion, how do I get the index correctly?
I would like this to return 1
[1,2.0,2,3].index(2.0)
#=> 1
I would like this to return 2
[1,2.0,2,3].index(2)
#=> 1
Using a block together with eql? is one way:
[1,2.0,2,3].index {|e| e.eql? 2.0}
#=> 1
[1,2.0,2,3].index {|e| e.eql? 2}
#=> 2
Unlike ==, eql? returns true only if the receiver and the argument have the same type and equal values. So 2 == 2.0 returns true while 2.eql? 2.0 returns false.
Array#index and Equality
You aren't getting the results you expect because Array#index uses the more-generic BasicObject#== instead of Object#eql? to compare values, which doesn't take the type of the argument/value into account. In a duck-typing language, this is usually what you want.
Consider the following example, which uses == to compare a Float to a Fixnum:
2 == 2.0
#=> true
Note that Ruby considers the two numeric values to be equal, despite being of different types. This is documented behavior. Since the non-block form of Array#index returns the first index where the argument is == to the indexed value, [1,2.0,2,3].index(2.0) and [1,2.0,2,3].index(2) will both return the same index.
Use Block Form of Array#index
All Ruby methods accept an optional block, and some core classes behave differently when Kernel#block_given? is true. The documentation for Array#index says:
If a block is given...returns the index of the first object for which the block returns true. Returns nil if no match is found.
The canonical way to differentiate between two different types of values would use the block-form of Array#index to check for object equality with #eql? rather than with #==. For example:
array = [1,2.0,2,3]
array.index { |i| i.eql? 2 }
array.index { |i| i.eql? 2.0 }
This returns the values you'd expect, at the cost of a little extra typing. This is really the preferred solution to your problem.
Monkey-Patch the Array Class
Monkey-patching a core class like Array is generally a Bad Idea™, but you can force Array#index to behave the way you want by re-opening the Array class and modifying the behavior of Array#index to check for both type and value equality. One way to do this is with the help of Module#alias_method, and by using the block syntax of Array#old_index to check Numeric#eql? whenever you call Array#index with a numeric argument.
Consider the following:
class Array
alias_method :old_index, :index
def index value
old_index { |i| i.eql? value }
end
end
[1,2.0,2,3].index 2.0
#=> 1
[1,2.0,2,3].index 2
#=> 2
This works the way you seem to expect, and you can still use Array#old_index anytime you want the original type-agnostic equality check. However, use with care, as other modules or gems might not behave as expected once you've changed the normal behavior of the Array class.
It's nice to know you can do this, but juggling with chainsaws is an inherently risky activity. Your mileage may vary.
Ruby API says:
The eql? method returns true if obj and other refer to the same hash key.
I changed the hash method for Object:
class Object
def hash
1
end
end
Object.new.hash == Object.new.hash
# => true
Object.new.eql? Object.new
# => false
I don't understand why the second statement returns false; according to Ruby Object API above, it should return true.
That's not what the docs say, and "the same hash key" isn't really relevant to the code you post.
hash creates a hash key, with the implication that a.eql?(b) means a.hash == b.hash. That's different than breaking hash and expecting an unmodified eql? to work the way you expect.
eql? must be overridden to provide the semantics you want, e.g., a custom class could override eql? to provide a domain-specific equivalency. The above hash contract implications would still need to be followed if you want other code to work appropriately.
(This is similar to the Java mantra "override hashCode if you override equals, e.g., http://www.xyzws.com/javafaq/why-always-override-hashcode-if-overriding-equals/20.)
This is a documentation bug. You read it correctly, but the documentation is contradictory.
On the one hand, the documentation says:
The eql? method returns true if obj and other refer to the same hash key.
from which you can expect as you did in your question:
Object.new.eql? Object.new
# => true
On the other hand, it also says:
For objects of class Object, eql? is synonymous with ==.
where the definition of == is given as:
At the Object level, == returns true only if obj and other are the same object.
It logically follows that:
For objects of class Object, eql? returns true only if obj and other are the same object.
from which you should expect:
Object.new.eql? Object.new
# => false
So the documentation makes contradictory claims. You relied on one of them, and made an expectation, but looking at the actual result, the reality seems to support the second claim.
You're creating two new objects, they will never be the same.
a = Object.new
=> #<Object:0x007fd16b35c8b8>
b = Object.new
=> #<Object:0x007fd16b355540>
And I will refer you back to this SO question
I am writing a Ruby app at the moment which is going to search twitter for various things. One of the problems I am going to face is shared results between searches in close proximity to each other time-wise. The results are returned in an array of objects each of which is a single tweet. I know of the Array.uniq method in ruby which returns an array with all the duplicates removed.
My question is this. Does the uniq method remove duplicates in so far as these objects point to the same space in memory or that they contain identical information?
If the former, whats the best way of removing duplicates from an array based on their content?
Does the uniq method remove duplicates
in so far as these objects point to
the same space in memory or that they
contain identical information?
The method relies on the eql? method so it removes all the elements where a.eql?(b) returns true.
The exact behavior depends on the specific object you are dealing with.
Strings, for example, are considered equal if they contain the same text regardless they share the same memory allocation.
a = b = "foo"
c = "foo"
[a, b, c].uniq
# => ["foo"]
This is true for the most part of core objects but not for ruby objects.
class Foo
end
a = Foo.new
b = Foo.new
a.eql? b
# => false
Ruby encourages you to redefine the == operator depending on your class context.
In your specific case I would suggest to create an object representing a twitter result and implement your comparison logic so that Array.uniq will behave as you expect.
class Result
attr_accessor :text, :notes
def initialize(text = nil, notes = nil)
self.text = text
self.notes = notes
end
def ==(other)
other.class == self.class &&
other.text == self.text
end
alias :eql? :==
end
a = Result.new("first")
b = Result.new("first")
c = Result.new("third")
[a, b, c].uniq
# => [a, c]
For anyone else stumbling upon this question, it looks like things have changed a bit since this question was first asked and in newer Ruby versions (1.9.3 at least), Array.uniq assumes that your object also has a meaningful implementation of the #hash method, in addition to .eql? or ==.
uniq uses eql?, as documented in this thread.
See the official ruby documentation for the distinction between ==, equal?, and eql?.
I believe that Array.uniq detects duplicates via the objects' eql? or == methods, which means its comparing based on content, not location in memory (assuming the objects provide a meaningful implementation of eql? based on content).
I know eql? is used by Hashes to see if an object matches a key*, and you do
def ==(rb)
if you want to support the == operator, but there must be a good reason that Hashes don't use == instead. Why is that? When are you going to have definitions for == and eql? that are not equivalent (e.g. one is an alias to the other) ?
Similarly, why have to_ary in addition to to_a?
This question came up in response to an answer someone gave me on another question.
* Of course, a Hash also assumes eql? == true implies that the hashes codes are equal.
Also, is it basically a terribly idea to override equal? ?
== checks if two values are equal, while eql? checks if they are equal AND of the same type.
irb(main):001:0> someint = 17
=> 17
irb(main):002:0> someint == 17
=> true
irb(main):003:0> someint.eql? 17
=> true
irb(main):004:0> someint.eql? 17.0
=> false
irb(main):005:0> someint == 17.0
=> true
irb(main):006:0>
as you can see above, eql? will also test if both values are the same type. In the case of comparing to 17.0, which equates to false, it is because someint was not a floating point value.
I don't know the reasoning for this particular choice in ruby, but I'll just point out that equality is a difficult concept.
Common Lisp, for example has eq, eql, equal, equalp, and for that matter =
It can be very useful to be able to tell the difference between two references to the same object, two different objects of the same type with the same value, two objects with the same value but of different types, etc. How many variations make sense depends on what makes sense in the language.
If I recall it correctly (I don't use ruby), rubys predicates are implementing three of these cases
== is equality of value
eql? is equality of value and type
equal? is true only for the same object
This mentions that to_a and to_ary (and to_s and to_str , and to_i and to_int) have different levels of strictness. For example,
17.to_s
makes sense,
17.to_str
doesn't.
It seems that there is no to_ary method for the Hash class (no to_a), but for the Array class, to_a and to_ary have different behavior :
to_a :
Returns self. If called on a subclass of Array, converts the receiver to an Array object.
to_ary :
Returns self.
The answers above more that answer about eql? but here is something on to_a and to_ary.
In Ruby's duck-typing scheme, objects can be converted two ways--loosely and firmly. Loose conversion is like saying:
Can foo represent itself as an array (to_a). This is what to_a, to_s, to_i and other single letter ones are for. So a String can represent itself as an array, so it implements to_a. Firm conversion says something very different: Is foo a string (to_ary). Note that this is not wheather the class of foo is String, but whether foo and strings are interchangeable--whether anywhere a string is expected a foo could be logically used. The example in my Ruby book is of a Roman numeral class. We can use a Roman numeral anywhere we could use a positive integer, so Roman can implement to_int.
Classes that have an are interchangeable relationship need to implement firm conversion, while loose is for almost all classes. Make sure not to use firm conversion where loose is right--code built into the interpreter will severely misunderstand you and you'll end up with bugs comparable to C++'s reinterpet_cast<>. Not good.