From eac5522edededacfc2a22d6f6879da43b8136d41 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Sun, 28 Jul 2013 08:40:58 +0200 Subject: [PATCH] Add question-mark methods for all possible NullObjects --- README.md | 41 ++++++++++++++++-- lib/twitter/direct_message.rb | 14 +++++- lib/twitter/list.rb | 7 ++- lib/twitter/place.rb | 7 ++- lib/twitter/relationship.rb | 14 +++++- lib/twitter/settings.rb | 7 ++- lib/twitter/trend_results.rb | 2 + lib/twitter/tweet.rb | 26 +++++++++++- spec/twitter/direct_message_spec.rb | 38 +++++++++++++---- spec/twitter/list_spec.rb | 19 +++++++-- spec/twitter/place_spec.rb | 15 ++++++- spec/twitter/relationship_spec.rb | 38 +++++++++++++---- spec/twitter/settings_spec.rb | 23 +++++++--- spec/twitter/trend_results_spec.rb | 4 +- spec/twitter/tweet_spec.rb | 66 ++++++++++++++++++++++++----- 15 files changed, 267 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index a37880a57..5c76aa162 100644 --- a/README.md +++ b/README.md @@ -260,13 +260,46 @@ removed: * `#from_user_id` * `#from_user_name` * `#to_user` -* `$to_user_id` -* `to_user_name` -* `profile_image_url` -* `profile_image_url_https` +* `#to_user_id` +* `#to_user_name` +* `#profile_image_url` +* `#profile_image_url_https` These attributes can be accessed through the `#user` method. +### Null Objects +In version 4, methods you would expect to return a `Twitter` object would +return `nil` if that object was missing. This may have resulted in errors like +this: + + NoMethodError: undefined method for nil:NilClass + +To prevent such errors, you may have introduced checks for the truthiness of +the response, for example: + +```ruby +status = client.status(55709764298092545) +if status.place + # Do something with the Twitter::Place object +elsif status.geo + # Do something with the Twitter::Geo object +end +``` +In version 5, all such methods will return a `Twitter::NullObject` instead of +`nil`. This should prevent `NoMethodError` but may result in unexpected +behavior if you have truthiness checks in place, since everything is truthy in +Ruby execpt `false` and `nil`. For these cases, there are now predicate +methods: + +```ruby +status = client.status(55709764298092545) +if status.place? + # Do something with the Twitter::Place object +elsif status.geo? + # Do something with the Twitter::Geo object +end +``` + ## Configuration Twitter API v1.1 requires you to authenticate via OAuth, so you'll need to [register your application with Twitter][register]. Once you've registered an diff --git a/lib/twitter/direct_message.rb b/lib/twitter/direct_message.rb index 5442dfeb3..dbc6ce420 100644 --- a/lib/twitter/direct_message.rb +++ b/lib/twitter/direct_message.rb @@ -7,15 +7,25 @@ class DirectMessage < Twitter::Identity attr_reader :text alias full_text text - # @return [Twitter::User] + # @return [Twitter::User, Twitter::NullObject] def recipient new_or_null_object(Twitter::User, :recipient) end - # @return [Twitter::User] + # @return [Boolean] + def recipient? + !recipient.nil? + end + + # @return [Twitter::User, Twitter::NullObject] def sender new_or_null_object(Twitter::User, :sender) end + # @return [Boolean] + def sender? + !sender.nil? + end + end end diff --git a/lib/twitter/list.rb b/lib/twitter/list.rb index efe204917..c697233fc 100644 --- a/lib/twitter/list.rb +++ b/lib/twitter/list.rb @@ -7,10 +7,15 @@ class List < Twitter::Identity attr_reader :description, :following, :full_name, :member_count, :mode, :name, :slug, :subscriber_count, :uri - # @return [Twitter::User] + # @return [Twitter::User, Twitter::NullObject] def user new_or_null_object(Twitter::User, :user) end + # @return [Boolean] + def user? + !user.nil? + end + end end diff --git a/lib/twitter/place.rb b/lib/twitter/place.rb index 4c2d2042b..3a4088982 100644 --- a/lib/twitter/place.rb +++ b/lib/twitter/place.rb @@ -5,11 +5,16 @@ class Place < Twitter::Identity attr_reader :attributes, :country, :full_name, :name, :url, :woeid alias woe_id woeid - # @return [Twitter::Geo] + # @return [Twitter::Geo, Twitter::NullObject] def bounding_box new_or_null_object(Twitter::GeoFactory, :bounding_box) end + # @return [Boolean] + def bounding_box? + !bounding_box.nil? + end + # @return [String] def country_code @country_code ||= @attrs[:country_code] || @attrs[:countryCode] diff --git a/lib/twitter/relationship.rb b/lib/twitter/relationship.rb index 3c39cf7db..f41a4e352 100644 --- a/lib/twitter/relationship.rb +++ b/lib/twitter/relationship.rb @@ -11,16 +11,26 @@ def initialize(attrs={}) @attrs = attrs[:relationship] end - # @return [Twitter::SourceUser] + # @return [Twitter::SourceUser, Twitter::NullObject] def source new_or_null_object(Twitter::SourceUser, :source) end - # @return [Twitter::TargetUser] + # @return [Boolean] + def source? + !source.nil? + end + + # @return [Twitter::TargetUser, Twitter::NullObject] def target new_or_null_object(Twitter::TargetUser, :target) end + # @return [Boolean] + def target? + !target.nil? + end + # Update the attributes of a Relationship # # @param attrs [Hash] diff --git a/lib/twitter/settings.rb b/lib/twitter/settings.rb index 696764e96..37510c70c 100644 --- a/lib/twitter/settings.rb +++ b/lib/twitter/settings.rb @@ -6,10 +6,15 @@ class Settings < Twitter::Base :language, :protected, :screen_name, :show_all_inline_media, :sleep_time, :time_zone - # @return [Twitter::Place] + # @return [Twitter::Place, Twitter::NullObject] def trend_location new_or_null_object(Twitter::Place, :trend_location) end + # @return [Boolean] + def trend_location? + !trend_location.nil? + end + end end diff --git a/lib/twitter/trend_results.rb b/lib/twitter/trend_results.rb index 75d1f55d7..346aef337 100644 --- a/lib/twitter/trend_results.rb +++ b/lib/twitter/trend_results.rb @@ -45,6 +45,7 @@ def as_of @as_of ||= Time.parse(@attrs[:as_of]) unless @attrs[:as_of].nil? end + # @return [Twitter::Place, NullObject] def location @location ||= if location? Twitter::Place.new(@attrs[:locations].first) @@ -53,6 +54,7 @@ def location end end + # @return [Boolean] def location? !@attrs[:locations].nil? && !@attrs[:locations].first.nil? end diff --git a/lib/twitter/tweet.rb b/lib/twitter/tweet.rb index 874f8038a..fa709458c 100644 --- a/lib/twitter/tweet.rb +++ b/lib/twitter/tweet.rb @@ -42,6 +42,11 @@ def geo new_or_null_object(Twitter::GeoFactory, :geo) end + # @return [Boolean] + def geo? + !geo.nil? + end + # @note Must include entities in your request for this method to work # @return [Array] def hashtags @@ -54,16 +59,26 @@ def media @media ||= entities(Twitter::MediaFactory, :media) end - # @return [Twitter::Metadata] + # @return [Twitter::Metadata, Twitter::NullObject] def metadata new_or_null_object(Twitter::Metadata, :metadata) end - # @return [Twitter::Place] + # @return [Boolean] + def metadata? + !metadata.nil? + end + + # @return [Twitter::Place, Twitter::NullObject] def place new_or_null_object(Twitter::Place, :place) end + # @return [Boolean] + def place? + !place.nil? + end + # @return [Boolean] def reply? !!in_reply_to_status_id @@ -86,6 +101,13 @@ def retweeted_status alias retweet retweeted_status alias retweeted_tweet retweeted_status + # @return [Boolean] + def retweeted_status? + !retweeted_status.nil? + end + alias retweet? retweeted_status? + alias retweeted_tweet? retweeted_status? + # @note Must include entities in your request for this method to work # @return [Array] def symbols diff --git a/spec/twitter/direct_message_spec.rb b/spec/twitter/direct_message_spec.rb index 04b9de331..8b7eba467 100644 --- a/spec/twitter/direct_message_spec.rb +++ b/spec/twitter/direct_message_spec.rb @@ -44,23 +44,45 @@ describe "#recipient" do it "returns a User when recipient is set" do - recipient = Twitter::DirectMessage.new(:id => 1825786345, :recipient => {:id => 7505382}).recipient - expect(recipient).to be_a Twitter::User + direct_message = Twitter::DirectMessage.new(:id => 1825786345, :recipient => {:id => 7505382}) + expect(direct_message.recipient).to be_a Twitter::User end it "returns nil when recipient is not set" do - recipient = Twitter::DirectMessage.new(:id => 1825786345).recipient - expect(recipient).to be_nil + direct_message = Twitter::DirectMessage.new(:id => 1825786345) + expect(direct_message.recipient).to be_nil + end + end + + describe "#recipient?" do + it "returns true when recipient is set" do + direct_message = Twitter::DirectMessage.new(:id => 1825786345, :recipient => {:id => 7505382}) + expect(direct_message.recipient?).to be_true + end + it "returns false when recipient is not set" do + direct_message = Twitter::DirectMessage.new(:id => 1825786345) + expect(direct_message.recipient?).to be_false end end describe "#sender" do it "returns a User when sender is set" do - sender = Twitter::DirectMessage.new(:id => 1825786345, :sender => {:id => 7505382}).sender - expect(sender).to be_a Twitter::User + direct_message = Twitter::DirectMessage.new(:id => 1825786345, :sender => {:id => 7505382}) + expect(direct_message.sender).to be_a Twitter::User end it "returns nil when sender is not set" do - sender = Twitter::DirectMessage.new(:id => 1825786345).sender - expect(sender).to be_nil + direct_message = Twitter::DirectMessage.new(:id => 1825786345) + expect(direct_message.sender).to be_nil + end + end + + describe "#sender?" do + it "returns true when sender is set" do + direct_message = Twitter::DirectMessage.new(:id => 1825786345, :sender => {:id => 7505382}) + expect(direct_message.sender?).to be_true + end + it "returns false when sender is not set" do + direct_message = Twitter::DirectMessage.new(:id => 1825786345) + expect(direct_message.sender?).to be_false end end diff --git a/spec/twitter/list_spec.rb b/spec/twitter/list_spec.rb index 313c9ea25..6572d96c0 100644 --- a/spec/twitter/list_spec.rb +++ b/spec/twitter/list_spec.rb @@ -44,12 +44,23 @@ describe "#user" do it "returns a User when user is set" do - user = Twitter::List.new(:id => 8863586, :user => {:id => 7505382}).user - expect(user).to be_a Twitter::User + list = Twitter::List.new(:id => 8863586, :user => {:id => 7505382}) + expect(list.user).to be_a Twitter::User end it "returns nil when status is not set" do - user = Twitter::List.new(:id => 8863586).user - expect(user).to be_nil + list = Twitter::List.new(:id => 8863586) + expect(list.user).to be_nil + end + end + + describe "#user?" do + it "returns true when user is set" do + list = Twitter::List.new(:id => 8863586, :user => {:id => 7505382}) + expect(list.user?).to be_true + end + it "returns false when user is not set" do + list = Twitter::List.new(:id => 8863586) + expect(list.user?).to be_false end end diff --git a/spec/twitter/place_spec.rb b/spec/twitter/place_spec.rb index 712c43d60..215c30483 100644 --- a/spec/twitter/place_spec.rb +++ b/spec/twitter/place_spec.rb @@ -21,16 +21,27 @@ end describe "#bounding_box" do - it "returns a Twitter::Place when set" do + it "returns a Twitter::Place when bounding_box is set" do place = Twitter::Place.new(:id => "247f43d441defc03", :bounding_box => {:type => "Polygon", :coordinates => [[[-122.40348192, 37.77752898], [-122.387436, 37.77752898], [-122.387436, 37.79448597], [-122.40348192, 37.79448597]]]}) expect(place.bounding_box).to be_a Twitter::Geo::Polygon end - it "returns nil when not set" do + it "returns nil when not bounding_box is not set" do place = Twitter::Place.new(:id => "247f43d441defc03") expect(place.bounding_box).to be_nil end end + describe "#bounding_box?" do + it "returns true when bounding_box is set" do + place = Twitter::Place.new(:id => "247f43d441defc03", :bounding_box => {:type => "Polygon", :coordinates => [[[-122.40348192, 37.77752898], [-122.387436, 37.77752898], [-122.387436, 37.79448597], [-122.40348192, 37.79448597]]]}) + expect(place.bounding_box?).to be_true + end + it "returns false when bounding_box is not set" do + place = Twitter::Place.new(:id => "247f43d441defc03") + expect(place.bounding_box?).to be_false + end + end + describe "#country_code" do it "returns a country code when set with country_code" do place = Twitter::Place.new(:id => "247f43d441defc03", :country_code => "US") diff --git a/spec/twitter/relationship_spec.rb b/spec/twitter/relationship_spec.rb index e0e50ed8c..1128c38dd 100644 --- a/spec/twitter/relationship_spec.rb +++ b/spec/twitter/relationship_spec.rb @@ -4,23 +4,45 @@ describe "#source" do it "returns a User when source is set" do - source = Twitter::Relationship.new(:relationship => {:source => {:id => 7505382}}).source - expect(source).to be_a Twitter::SourceUser + relationship = Twitter::Relationship.new(:relationship => {:source => {:id => 7505382}}) + expect(relationship.source).to be_a Twitter::SourceUser end it "returns nil when source is not set" do - source = Twitter::Relationship.new(:relationship => {}).source - expect(source).to be_nil + relationship = Twitter::Relationship.new(:relationship => {}) + expect(relationship.source).to be_nil + end + end + + describe "#source?" do + it "returns true when source is set" do + relationship = Twitter::Relationship.new(:relationship => {:source => {:id => 7505382}}) + expect(relationship.source?).to be_true + end + it "returns false when source is not set" do + relationship = Twitter::Relationship.new(:relationship => {}) + expect(relationship.source?).to be_false end end describe "#target" do it "returns a User when target is set" do - target = Twitter::Relationship.new(:relationship => {:target => {:id => 7505382}}).target - expect(target).to be_a Twitter::TargetUser + relationship = Twitter::Relationship.new(:relationship => {:target => {:id => 7505382}}) + expect(relationship.target).to be_a Twitter::TargetUser end it "returns nil when target is not set" do - target = Twitter::Relationship.new(:relationship => {}).target - expect(target).to be_nil + relationship = Twitter::Relationship.new(:relationship => {}) + expect(relationship.target).to be_nil + end + end + + describe "#target?" do + it "returns true when target is set" do + relationship = Twitter::Relationship.new(:relationship => {:target => {:id => 7505382}}) + expect(relationship.target?).to be_true + end + it "returns false when target is not set" do + relationship = Twitter::Relationship.new(:relationship => {}) + expect(relationship.target?).to be_false end end diff --git a/spec/twitter/settings_spec.rb b/spec/twitter/settings_spec.rb index 760be109a..891982cd7 100644 --- a/spec/twitter/settings_spec.rb +++ b/spec/twitter/settings_spec.rb @@ -3,13 +3,24 @@ describe Twitter::Settings do describe "#trend_location" do - it "returns a Twitter::Place when set" do - place = Twitter::Settings.new(:trend_location => {:countryCode => "US", :name => "San Francisco", :country => "United States", :placeType => {:name => "Town", :code => 7}, :woeid => 2487956, :parentid => 23424977, :url => "http://where.yahooapis.com/v1/place/2487956"}) - expect(place.trend_location).to be_a Twitter::Place + it "returns a Twitter::Place when trend_location is set" do + settings = Twitter::Settings.new(:trend_location => {:countryCode => "US", :name => "San Francisco", :country => "United States", :placeType => {:name => "Town", :code => 7}, :woeid => 2487956, :parentid => 23424977, :url => "http://where.yahooapis.com/v1/place/2487956"}) + expect(settings.trend_location).to be_a Twitter::Place end - it "returns nil when not set" do - place = Twitter::Settings.new - expect(place.trend_location).to be_nil + it "returns nil when trend_location is not set" do + settings = Twitter::Settings.new + expect(settings.trend_location).to be_nil + end + end + + describe "#trend_location?" do + it "returns true when trend_location is set" do + settings = Twitter::Settings.new(:trend_location => {:countryCode => "US", :name => "San Francisco", :country => "United States", :placeType => {:name => "Town", :code => 7}, :woeid => 2487956, :parentid => 23424977, :url => "http://where.yahooapis.com/v1/place/2487956"}) + expect(settings.trend_location?).to be_true + end + it "returns false when trend_location is not set" do + settings = Twitter::Settings.new + expect(settings.trend_location?).to be_false end end diff --git a/spec/twitter/trend_results_spec.rb b/spec/twitter/trend_results_spec.rb index f04a6b8a6..30c671382 100644 --- a/spec/twitter/trend_results_spec.rb +++ b/spec/twitter/trend_results_spec.rb @@ -67,11 +67,11 @@ describe "#location?" do it "returns true when location is set" do trend_result = Twitter::TrendResults.new(:id => 1, :locations => [{:name => "Worldwide", :woeid => 1}]) - expect(trend_result.location).to be_true + expect(trend_result.location?).to be_true end it "returns false when location is not set" do trend_result = Twitter::TrendResults.new(:id => 1) - expect(trend_result.location).to be_false + expect(trend_result.location?).to be_false end end diff --git a/spec/twitter/tweet_spec.rb b/spec/twitter/tweet_spec.rb index 3ececdf5b..a02a2b9e1 100644 --- a/spec/twitter/tweet_spec.rb +++ b/spec/twitter/tweet_spec.rb @@ -106,16 +106,27 @@ end describe "#geo" do - it "returns a Twitter::Geo::Point when set" do + it "returns a Twitter::Geo::Point when geo is set" do tweet = Twitter::Tweet.new(:id => 28669546014, :geo => {:id => 1, :type => "Point"}) expect(tweet.geo).to be_a Twitter::Geo::Point end - it "returns nil when not set" do + it "returns nil when geo is not set" do tweet = Twitter::Tweet.new(:id => 28669546014) expect(tweet.geo).to be_nil end end + describe "#geo?" do + it "returns true when geo is set" do + tweet = Twitter::Tweet.new(:id => 28669546014, :geo => {:id => 1, :type => "Point"}) + expect(tweet.geo?).to be_true + end + it "returns false when geo is not set" do + tweet = Twitter::Tweet.new(:id => 28669546014) + expect(tweet.geo?).to be_false + end + end + describe "#hashtags" do it "returns an Array of Entity::Hashtag when entities are set" do hashtags_array = [ @@ -157,27 +168,49 @@ end describe "#metadata" do - it "returns a User when user is set" do - metadata = Twitter::Tweet.new(:id => 28669546014, :metadata => {}).metadata - expect(metadata).to be_a Twitter::Metadata + it "returns a Twitter::Metadata when metadata is set" do + tweet = Twitter::Tweet.new(:id => 28669546014, :metadata => {}) + expect(tweet.metadata).to be_a Twitter::Metadata end - it "returns nil when user is not set" do - metadata = Twitter::Tweet.new(:id => 28669546014).metadata - expect(metadata).to be_nil + it "returns nil when metadata is not set" do + tweet = Twitter::Tweet.new(:id => 28669546014) + expect(tweet.metadata).to be_nil + end + end + + describe "#metadata?" do + it "returns true when metadata is set" do + tweet = Twitter::Tweet.new(:id => 28669546014, :metadata => {}) + expect(tweet.metadata?).to be_true + end + it "returns false when metadata is not set" do + tweet = Twitter::Tweet.new(:id => 28669546014) + expect(tweet.metadata?).to be_false end end describe "#place" do - it "returns a Twitter::Place when set" do + it "returns a Twitter::Place when place is set" do tweet = Twitter::Tweet.new(:id => 28669546014, :place => {:id => "247f43d441defc03"}) expect(tweet.place).to be_a Twitter::Place end - it "returns nil when not set" do + it "returns nil when place is not set" do tweet = Twitter::Tweet.new(:id => 28669546014) expect(tweet.place).to be_nil end end + describe "#place?" do + it "returns true when place is set" do + tweet = Twitter::Tweet.new(:id => 28669546014, :place => {:id => "247f43d441defc03"}) + expect(tweet.place?).to be_true + end + it "returns false when place is not set" do + tweet = Twitter::Tweet.new(:id => 28669546014) + expect(tweet.place?).to be_false + end + end + describe "#reply?" do it "returns true when there is an in-reply-to status" do tweet = Twitter::Tweet.new(:id => 28669546014, :in_reply_to_status_id => 114749583439036416) @@ -201,7 +234,7 @@ end describe "#retweeted_status" do - it "has text when retweeted_status is set" do + it "returns a Tweet when retweeted_status is set" do tweet = Twitter::Tweet.new(:id => 28669546014, :retweeted_status => {:id => 25938088801, :text => "BOOSH"}) expect(tweet.retweeted_tweet).to be_a Twitter::Tweet expect(tweet.retweeted_tweet.text).to eq "BOOSH" @@ -212,6 +245,17 @@ end end + describe "#retweeted_status?" do + it "returns true when retweeted_status is set" do + tweet = Twitter::Tweet.new(:id => 28669546014, :retweeted_status => {:id => 25938088801, :text => "BOOSH"}) + expect(tweet.retweeted_status?).to be_true + end + it "returns false when retweeted_status is not set" do + tweet = Twitter::Tweet.new(:id => 28669546014) + expect(tweet.retweeted_status?).to be_false + end + end + describe "#symbols" do it "returns an Array of Entity::Symbol when symbols are set" do symbols_array = [