Skip to content

Commit

Permalink
Make object equality more strict
Browse files Browse the repository at this point in the history
Closes #277.
  • Loading branch information
sferik committed Jun 29, 2012
1 parent 29781c2 commit 77d9283
Show file tree
Hide file tree
Showing 29 changed files with 473 additions and 131 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,7 @@
3.1.0 - June 30, 2012
---------------------
* [Make object equality more strict](https://github.com/jnunemaker/twitter/commit/537a5463d568e9a07ef5de5ce4dcad701b068ff3)

3.0.0 - June 28, 2012
---------------------
* [All returned hashes now use symbols as keys instead of strings](https://github.com/jnunemaker/twitter/commit/d5b5d8788dc0c0cef6f2c28e6fa2dc6ffcf389eb)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -167,7 +167,7 @@ Here are some fun facts about the 3.0 release:

* The entire library is implemented in just 2,170 source lines of code
* With over 5,000 lines of specs, the spec-to-code ratio is over 2.3:1
* The spec suite contains 610 examples and runs in under 3 seconds on a MacBook
* The spec suite contains 665 examples and runs in under 3 seconds on a MacBook
* This project has 100% C0 code coverage (the tests execute every line of
source code at least once)
* At the time of release, this library is comprehensive: you can request all
Expand Down
8 changes: 8 additions & 0 deletions lib/twitter/base.rb
Expand Up @@ -67,5 +67,13 @@ def update(attrs)
self
end

protected

# @param other [Twitter::Base]
# @return [Boolean]
def attrs_equal(other)
self.class == other.class && !other.attrs.empty? && self.attrs == other.attrs
end

end
end
2 changes: 0 additions & 2 deletions lib/twitter/client.rb
Expand Up @@ -16,8 +16,6 @@
require 'twitter/oembed'
require 'twitter/photo'
require 'twitter/place'
require 'twitter/point'
require 'twitter/polygon'
require 'twitter/rate_limit'
require 'twitter/rate_limit_status'
require 'twitter/relationship'
Expand Down
23 changes: 23 additions & 0 deletions lib/twitter/geo.rb
@@ -0,0 +1,23 @@
require 'twitter/base'

module Twitter
class Geo < Twitter::Base
attr_reader :coordinates
alias coords coordinates

# @param other [Twitter::Geo]
# @return [Boolean]
def ==(other)
super || self.coordinates_equal(other) || self.attrs_equal(other)
end

protected

# @param other [Twitter::Geo]
# @return [Boolean]
def coordinates_equal(other)
self.class == other.class && !other.coordinates.nil? && self.coordinates == other.coordinates
end

end
end
20 changes: 20 additions & 0 deletions lib/twitter/geo/point.rb
@@ -0,0 +1,20 @@
require 'twitter/geo'

module Twitter
class Point < Twitter::Geo

# @return [Integer]
def latitude
self.coordinates[0]
end
alias lat latitude

# @return [Integer]
def longitude
self.coordinates[1]
end
alias long longitude
alias lng longitude

end
end
6 changes: 6 additions & 0 deletions lib/twitter/geo/polygon.rb
@@ -0,0 +1,6 @@
require 'twitter/geo'

module Twitter
class Polygon < Twitter::Geo
end
end
4 changes: 2 additions & 2 deletions lib/twitter/geo_factory.rb
@@ -1,5 +1,5 @@
require 'twitter/point'
require 'twitter/polygon'
require 'twitter/geo/point'
require 'twitter/geo/polygon'

module Twitter
class GeoFactory
Expand Down
10 changes: 9 additions & 1 deletion lib/twitter/identifiable.rb
Expand Up @@ -26,13 +26,21 @@ def initialize(attrs={})
# @param other [Twitter::Identifiable]
# @return [Boolean]
def ==(other)
super || (other.class == self.class && other.id == self.id)
super || self.ids_equal(other) || self.attrs_equal(other)
end

# @return [Integer]
def id
@attrs[:id]
end

protected

# @param other [Twitter::Identifiable]
# @return [Boolean]
def ids_equal(other)
self.class == other.class && !other.id.nil? && self.id == other.id
end

end
end
27 changes: 0 additions & 27 deletions lib/twitter/point.rb

This file was deleted.

14 changes: 0 additions & 14 deletions lib/twitter/polygon.rb

This file was deleted.

10 changes: 9 additions & 1 deletion lib/twitter/size.rb
Expand Up @@ -9,7 +9,15 @@ class Size < Twitter::Base
# @param other [Twitter::Size]
# @return [Boolean]
def ==(other)
super || (other.class == self.class && other.h == self.h && other.w == self.w)
super || self.size_equal(other) || self.attrs_equal(other)
end

protected

# @param other [Twitter::Size]
# @return [Boolean]
def size_equal(other)
self.class == other.class && !other.h.nil? && self.h == other.h && !other.w.nil? && self.w == other.w
end

end
Expand Down
14 changes: 11 additions & 3 deletions lib/twitter/suggestion.rb
Expand Up @@ -8,14 +8,22 @@ class Suggestion < Twitter::Base
# @param other [Twitter::Suggestion]
# @return [Boolean]
def ==(other)
super || (other.class == self.class && other.slug == self.slug)
super || self.slug_equal(other) || self.attrs_equal(other)
end

# @return [Array<Twitter::User>]
def users
@users = Array(@attrs[:users]).map do |user|
@users ||= Array(@attrs[:users]).map do |user|
Twitter::User.fetch_or_new(user)
end
end unless @attrs[:users].nil?
end

protected

# @param other [Twitter::Suggestion]
# @return [Boolean]
def slug_equal(other)
self.class == other.class && !other.slug.nil? && self.slug == other.slug
end

end
Expand Down
10 changes: 9 additions & 1 deletion lib/twitter/trend.rb
Expand Up @@ -7,7 +7,15 @@ class Trend < Twitter::Base
# @param other [Twitter::Trend]
# @return [Boolean]
def ==(other)
super || (other.class == self.class && other.name == self.name)
super || self.name_equal(other) || self.attrs_equal(other)
end

protected

# @param other [Twitter::Trend]
# @return [Boolean]
def name_equal(other)
self.class == other.class && !other.name.nil? && self.name == other.name
end

end
Expand Down
29 changes: 22 additions & 7 deletions spec/twitter/direct_message_spec.rb
Expand Up @@ -3,19 +3,34 @@
describe Twitter::DirectMessage do

describe "#==" do
it "returns true when ids and classes are equal" do
direct_message = Twitter::DirectMessage.new(:id => 1)
other = Twitter::DirectMessage.new(:id => 1)
it "returns false for empty objects" do
direct_message = Twitter::DirectMessage.new
other = Twitter::DirectMessage.new
(direct_message == other).should be_false
end
it "returns true when objects IDs are the same" do
direct_message = Twitter::DirectMessage.new(:id => 1, :text => "foo")
other = Twitter::DirectMessage.new(:id => 1, :text => "bar")
(direct_message == other).should be_true
end
it "returns false when classes are not equal" do
it "returns false when objects IDs are different" do
direct_message = Twitter::DirectMessage.new(:id => 1)
other = Twitter::User.new(:id => 1)
other = Twitter::DirectMessage.new(:id => 2)
(direct_message == other).should be_false
end
it "returns false when ids are not equal" do
it "returns false when classes are different" do
direct_message = Twitter::DirectMessage.new(:id => 1)
other = Twitter::DirectMessage.new(:id => 2)
other = Twitter::Identifiable.new(:id => 1)
(direct_message == other).should be_false
end
it "returns true when objects non-ID attributes are the same" do
direct_message = Twitter::DirectMessage.new(:text => "foo")
other = Twitter::DirectMessage.new(:text => "foo")
(direct_message == other).should be_true
end
it "returns false when objects non-ID attributes are different" do
direct_message = Twitter::DirectMessage.new(:text => "foo")
other = Twitter::DirectMessage.new(:text => "bar")
(direct_message == other).should be_false
end
end
Expand Down
13 changes: 11 additions & 2 deletions spec/twitter/point_spec.rb → spec/twitter/geo/point_spec.rb
Expand Up @@ -7,14 +7,23 @@
end

describe "#==" do
it "returns true when coordinates are equal" do
it "returns false for empty objects" do
point = Twitter::Point.new
other = Twitter::Point.new
(point == other).should be_false
end
it "returns true when objects coordinates are the same" do
other = Twitter::Point.new(:coordinates => [-122.399983, 37.788299])
(@point == other).should be_true
end
it "returns false when coordinates are not equal" do
it "returns false when objects coordinates are different" do
other = Twitter::Point.new(:coordinates => [37.788299, -122.399983])
(@point == other).should be_false
end
it "returns false when classes are different" do
other = Twitter::Geo.new(:coordinates => [-122.399983, 37.788299])
(@point == other).should be_false
end
end

describe "#latitude" do
Expand Down
13 changes: 11 additions & 2 deletions spec/twitter/polygon_spec.rb → spec/twitter/geo/polygon_spec.rb
Expand Up @@ -7,14 +7,23 @@
end

describe "#==" do
it "returns true when coordinates are equal" do
it "returns false for empty objects" do
polygon = Twitter::Polygon.new
other = Twitter::Polygon.new
(polygon == other).should be_false
end
it "returns true when objects coordinates are the same" do
other = Twitter::Polygon.new(:coordinates => [[[-122.40348192, 37.77752898], [-122.387436, 37.77752898], [-122.387436, 37.79448597], [-122.40348192, 37.79448597]]])
(@polygon == other).should be_true
end
it "returns false when coordinates are not equal" do
it "returns false when objects coordinates are different" do
other = Twitter::Polygon.new(:coordinates => [[[37.77752898, -122.40348192], [37.77752898, -122.387436], [37.79448597, -122.387436], [37.79448597, -122.40348192]]])
(@polygon == other).should be_false
end
it "returns false when classes are different" do
other = Twitter::Geo.new(:coordinates => [[[-122.40348192, 37.77752898], [-122.387436, 37.77752898], [-122.387436, 37.79448597], [-122.40348192, 37.79448597]]])
(@polygon == other).should be_false
end
end

end
29 changes: 29 additions & 0 deletions spec/twitter/geo_spec.rb
@@ -0,0 +1,29 @@
require 'helper'

describe Twitter::Geo do

before do
@geo = Twitter::Geo.new(:coordinates => [[[-122.40348192, 37.77752898], [-122.387436, 37.77752898], [-122.387436, 37.79448597], [-122.40348192, 37.79448597]]])
end

describe "#==" do
it "returns false for empty objects" do
geo = Twitter::Geo.new
other = Twitter::Geo.new
(geo == other).should be_false
end
it "returns true when objects coordinates are the same" do
other = Twitter::Geo.new(:coordinates => [[[-122.40348192, 37.77752898], [-122.387436, 37.77752898], [-122.387436, 37.79448597], [-122.40348192, 37.79448597]]])
(@geo == other).should be_true
end
it "returns false when objects coordinates are different" do
other = Twitter::Geo.new(:coordinates => [[[37.77752898, -122.40348192], [37.77752898, -122.387436], [37.79448597, -122.387436], [37.79448597, -122.40348192]]])
(@geo == other).should be_false
end
it "returns false when classes are different" do
other = Twitter::Polygon.new(:coordinates => [[[-122.40348192, 37.77752898], [-122.387436, 37.77752898], [-122.387436, 37.79448597], [-122.40348192, 37.79448597]]])
(@geo == other).should be_false
end
end

end
38 changes: 38 additions & 0 deletions spec/twitter/identifiable_spec.rb
@@ -0,0 +1,38 @@
require 'helper'

describe Twitter::Identifiable do

describe "#==" do
it "returns false for empty objects" do
one = Twitter::Identifiable.new
two = Twitter::Identifiable.new
(one == two).should be_false
end
it "returns true when objects IDs are the same" do
one = Twitter::Identifiable.new(:id => 1, :screen_name => "sferik")
two = Twitter::Identifiable.new(:id => 1, :screen_name => "garybernhardt")
(one == two).should be_true
end
it "returns false when objects IDs are different" do
one = Twitter::Identifiable.new(:id => 1)
two = Twitter::Identifiable.new(:id => 2)
(one == two).should be_false
end
it "returns false when classes are different" do
one = Twitter::Identifiable.new(:id => 1)
two = Twitter::Base.new(:id => 1)
(one == two).should be_false
end
it "returns true when objects non-ID attributes are the same" do
one = Twitter::Identifiable.new(:screen_name => "sferik")
two = Twitter::Identifiable.new(:screen_name => "sferik")
(one == two).should be_true
end
it "returns false when objects non-ID attributes are different" do
one = Twitter::Identifiable.new(:screen_name => "sferik")
two = Twitter::Identifiable.new(:screen_name => "garybernhardt")
(one == two).should be_false
end
end

end

0 comments on commit 77d9283

Please sign in to comment.