From e14cc3391ebe8229184e9e83806c870df3baa24c Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Tue, 24 Dec 2013 17:31:10 -0500 Subject: [PATCH] Fix enumerable search interface --- Rakefile | 2 +- lib/twitter/cursor.rb | 32 ++------ lib/twitter/enumerable.rb | 10 +++ lib/twitter/rest/api/search.rb | 9 ++- lib/twitter/search_results.rb | 77 +++++++----------- spec/fixtures/search.json | 2 +- spec/fixtures/search2.json | 1 + spec/twitter/cursor_spec.rb | 38 --------- spec/twitter/rest/api/search_spec.rb | 20 ++--- spec/twitter/search_results_spec.rb | 116 +++------------------------ 10 files changed, 74 insertions(+), 233 deletions(-) create mode 100644 spec/fixtures/search2.json diff --git a/Rakefile b/Rakefile index 0170ccc33..fd8a1171a 100644 --- a/Rakefile +++ b/Rakefile @@ -32,7 +32,7 @@ end require 'yardstick/rake/verify' Yardstick::Rake::Verify.new do |verify| - verify.threshold = 60.1 + verify.threshold = 59.9 end task :default => [:spec, :rubocop, :verify_measurements] diff --git a/lib/twitter/cursor.rb b/lib/twitter/cursor.rb index a8013deb3..ececc903e 100644 --- a/lib/twitter/cursor.rb +++ b/lib/twitter/cursor.rb @@ -1,6 +1,8 @@ +require 'twitter/enumerable' + module Twitter class Cursor - include Enumerable + include Twitter::Enumerable attr_reader :attrs alias_method :to_h, :attrs alias_method :to_hash, :attrs @@ -32,7 +34,7 @@ def from_response(response, key, klass, client, request_method, path, options) # # @param path [String] # @param options [Hash] # @return [Twitter::Cursor] - def initialize(attrs, key, klass, client, request_method, path, options) # rubocop:disable ParameterLists + def initialize(attrs, key, klass, client, request_method, path, options = {}) # rubocop:disable ParameterLists @key = key.to_sym @klass = klass @client = client @@ -43,42 +45,18 @@ def initialize(attrs, key, klass, client, request_method, path, options) # ruboc set_attrs(attrs) end - # @return [Enumerator] - def each(start = 0, &block) - return to_enum(:each) unless block_given? - Array(@collection[start..-1]).each do |element| - yield element - end - unless last? - start = [@collection.size, start].max - fetch_next_page - each(start, &block) - end - self - end + private def next_cursor @attrs[:next_cursor] || -1 end alias_method :next, :next_cursor - def previous_cursor - @attrs[:previous_cursor] - end - alias_method :previous, :previous_cursor - - # @return [Boolean] - def first? - previous_cursor.zero? - end - # @return [Boolean] def last? next_cursor.zero? end - private - def fetch_next_page response = @client.send(@request_method, @path, @options.merge(:cursor => next_cursor)) set_attrs(response[:body]) diff --git a/lib/twitter/enumerable.rb b/lib/twitter/enumerable.rb index 7efede809..03394d83e 100644 --- a/lib/twitter/enumerable.rb +++ b/lib/twitter/enumerable.rb @@ -8,7 +8,17 @@ def each(start = 0, &block) Array(@collection[start..-1]).each do |element| yield element end + unless last? + start = [@collection.size, start].max + fetch_next_page + each(start, &block) + end self end + + # @return [Boolean] + def last? + true + end end end diff --git a/lib/twitter/rest/api/search.rb b/lib/twitter/rest/api/search.rb index 230043102..a798b66d6 100644 --- a/lib/twitter/rest/api/search.rb +++ b/lib/twitter/rest/api/search.rb @@ -28,7 +28,14 @@ module Search # @option options [Boolean, String, Integer] :include_entities The tweet entities node will be disincluded when set to false. # @return [Twitter::SearchResults] Return tweets that match a specified query with search metadata def search(q, options = {}) - object_from_response(Twitter::SearchResults, :get, '/1.1/search/tweets.json', options.merge(:q => q)) + search_results_from_response(:get, '/1.1/search/tweets.json', options.merge(:q => q)) + end + + private + + def search_results_from_response(request_method, path, options = {}) # rubocop:disable ParameterLists + response = send(request_method.to_sym, path, options) + Twitter::SearchResults.from_response(response, self, request_method, path, options) end end end diff --git a/lib/twitter/search_results.rb b/lib/twitter/search_results.rb index 912492713..83d966bad 100644 --- a/lib/twitter/search_results.rb +++ b/lib/twitter/search_results.rb @@ -12,53 +12,36 @@ class << self # Construct a new SearchResults object from a response hash # # @param response [Hash] - # @return [Twitter::Base] - def from_response(response = {}) - new(response[:body]) + # @param client [Twitter::REST::Client] + # @param path [String] + # @return [Twitter::SearchResults] + def from_response(response, client, request_method, path, options) # rubocop:disable ParameterLists + new(response[:body], client, request_method, path, options) end end # Initializes a new SearchResults object # # @param attrs [Hash] + # @param client [Twitter::REST::Client] + # @param request_method [String, Symbol] + # @param path [String] + # @param options [Hash] # @return [Twitter::SearchResults] - def initialize(attrs = {}) - @attrs = attrs - @collection = Array(@attrs[:statuses]).map do |tweet| - Tweet.new(tweet) - end - end - - # @return [Float] - def completed_in - @attrs[:search_metadata][:completed_in] if @attrs[:search_metadata] - end - - # @return [Integer] - def max_id - @attrs[:search_metadata][:max_id] if @attrs[:search_metadata] - end - - # @return [Integer] - def page - @attrs[:search_metadata][:page] if @attrs[:search_metadata] - end - - # @return [String] - def query - @attrs[:search_metadata][:query] if @attrs[:search_metadata] + def initialize(attrs, client, request_method, path, options = {}) # rubocop:disable ParameterLists + @client = client + @request_method = request_method.to_sym + @path = path + @options = options + @collection = [] + set_attrs(attrs) end - # @return [Integer] - def results_per_page - @attrs[:search_metadata][:count] if @attrs[:search_metadata] - end - alias_method :rpp, :results_per_page - alias_method :count, :results_per_page + private - # @return [Integer] - def since_id - @attrs[:search_metadata][:since_id] if @attrs[:search_metadata] + # @return [Boolean] + def last? + !next_results? end # @return [Boolean] @@ -66,6 +49,7 @@ def next_results? !!(@attrs[:search_metadata] && @attrs[:search_metadata][:next_results]) end alias_method :next_page?, :next_results? + alias_method :next?, :next_results? # Returns a Hash of query parameters for the next result in the search # @@ -78,18 +62,19 @@ def next_results end end alias_method :next_page, :next_results + alias_method :next, :next_results - # Returns a Hash of query parameters for the refresh URL in the search - # - # @note Returned Hash can be merged into the previous search options list to easily access the refresh page. - # @return [Hash] The parameters needed to refresh the page. - def refresh_results - query_string = strip_first_character(@attrs[:search_metadata][:refresh_url]) - query_string_to_hash(query_string) + def fetch_next_page + response = @client.send(@request_method, @path, next_page) + set_attrs(response[:body]) end - alias_method :refresh_page, :refresh_results - private + def set_attrs(attrs) + @attrs = attrs + Array(@attrs[:statuses]).map do |tweet| + @collection << Tweet.new(tweet) + end + end # Returns the string with the first character removed # diff --git a/spec/fixtures/search.json b/spec/fixtures/search.json index 3113c1f68..974c0ec88 100644 --- a/spec/fixtures/search.json +++ b/spec/fixtures/search.json @@ -1 +1 @@ -{"statuses":[{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Mon Sep 17 22:41:52 +0000 2012","id":247827742178021376,"id_str":"247827742178021376","text":"Bubble Mailer #freebandnames","source":"\u003ca href=\"http:\/\/tapbots.com\" rel=\"nofollow\"\u003eTweetbot for Mac\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":671293,"id_str":"671293","name":"Richard Allen","screen_name":"richrad","location":"Kansas City","url":"http:\/\/richardallen.me","description":"Give me a break, Jeffery.","protected":false,"followers_count":174,"friends_count":273,"listed_count":2,"created_at":"Sat Jan 20 16:02:45 +0000 2007","favourites_count":383,"utc_offset":-21600,"time_zone":"Central Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":1370,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"0099B9","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme4\/bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme4\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1399177739\/notlookingup_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1399177739\/notlookingup_normal.jpeg","profile_link_color":"0099B9","profile_sidebar_border_color":"5ED4DC","profile_sidebar_fill_color":"95E8EC","profile_text_color":"3C3940","profile_use_background_image":true,"show_all_inline_media":true,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"entities":{"hashtags":[{"text":"freebandnames","indices":[14,28]}],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false}],"search_metadata":{"completed_in":0.029,"max_id":250126199840518145,"max_id_str":"250126199840518145","next_page":"?page=2&max_id=250126199840518145&q=%23freebandnames&rpp=4&include_entities=1&result_type=mixed","page":1,"query":"%23freebandnames","refresh_url":"?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1","count":4,"since_id":24012619984051000,"since_id_str":"24012619984051000"}} \ No newline at end of file +{"statuses":[{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Dec 20 16:52:25 +0000 2013","id":414075829182676992,"id_str":"414075829182676992","text":"@Just_Reboot #FreeBandNames mono surround","source":"\u003ca href=\"http:\/\/www.myplume.com\/\" rel=\"nofollow\"\u003ePlume\u00a0for\u00a0Android\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":414069174856843264,"in_reply_to_status_id_str":"414069174856843264","in_reply_to_user_id":29296581,"in_reply_to_user_id_str":"29296581","in_reply_to_screen_name":"Just_Reboot","user":{"id":546527520,"id_str":"546527520","name":"Phil Empanada","screen_name":"ItsFuckinOhSo","location":"By cacti and sand","description":"Insert cheesy warnings about this account","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":257,"friends_count":347,"listed_count":10,"created_at":"Fri Apr 06 02:15:16 +0000 2012","favourites_count":345,"utc_offset":-25200,"time_zone":"Arizona","geo_enabled":false,"verified":false,"statuses_count":21765,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/414512901680930816\/AcmN-ByT_normal.jpeg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/414512901680930816\/AcmN-ByT_normal.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/546527520\/1371244104","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"FreeBandNames","indices":[13,27]}],"symbols":[],"urls":[],"user_mentions":[{"screen_name":"Just_Reboot","name":"Reboot","id":29296581,"id_str":"29296581","indices":[0,12]}]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Dec 20 16:43:32 +0000 2013","id":414073595372265472,"id_str":"414073595372265472","text":"RT @Just_Reboot: The Dick Tonsils #FreeBandNames\u00a0","source":"\u003ca href=\"http:\/\/twitter.com\/download\/android\" rel=\"nofollow\"\u003eTwitter for Android\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":109670150,"id_str":"109670150","name":"Derek Rodriguez","screen_name":"drod2169","location":"Tampa, Florida","description":"Web Developer, College Student, Workout Addict, Taco Fiend. #FBGT","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":3119,"friends_count":714,"listed_count":142,"created_at":"Fri Jan 29 21:29:16 +0000 2010","favourites_count":3728,"utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":46991,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"C0DEED","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/3347464379\/1046c92ab1834f1a3c1692ea585a1d69_normal.jpeg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/3347464379\/1046c92ab1834f1a3c1692ea585a1d69_normal.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/109670150\/1368729387","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":true,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Dec 20 16:34:40 +0000 2013","id":414071361066532864,"id_str":"414071361066532864","text":"The Dick Tonsils #FreeBandNames\u00a0","source":"\u003ca href=\"http:\/\/www.myplume.com\/\" rel=\"nofollow\"\u003ePlume\u00a0for\u00a0Android\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":29296581,"id_str":"29296581","name":"Reboot","screen_name":"Just_Reboot","location":"NC","description":"G+ http:\/\/t.co\/MyXmhCAtnD\r\n#TeamKang PR, Attempted Writer, IT Tech, Social Media Berserker and Entertainer, Adjectives, Assertions, Disclaimers","url":null,"entities":{"description":{"urls":[{"url":"http:\/\/t.co\/MyXmhCAtnD","expanded_url":"http:\/\/goo.gl\/giVTc","display_url":"goo.gl\/giVTc","indices":[3,25]}]}},"protected":false,"followers_count":2244,"friends_count":435,"listed_count":60,"created_at":"Mon Apr 06 21:21:27 +0000 2009","favourites_count":79,"utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":27703,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"131516","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/871578268\/5db26751bf732750f5ca1ed4f9f59309.png","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/871578268\/5db26751bf732750f5ca1ed4f9f59309.png","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/378800000480531175\/93e8b5cb95a635ebb81302f5b6044a76_normal.jpeg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/378800000480531175\/93e8b5cb95a635ebb81302f5b6044a76_normal.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/29296581\/1368898490","profile_link_color":"009999","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"EFEFEF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":1,"entities":{"hashtags":[{"text":"FreeBandNames","indices":[17,31]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},"retweet_count":1,"favorite_count":0,"entities":{"hashtags":[{"text":"FreeBandNames","indices":[34,48]}],"symbols":[],"urls":[],"user_mentions":[{"screen_name":"Just_Reboot","name":"Reboot","id":29296581,"id_str":"29296581","indices":[3,15]}]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Dec 20 16:34:40 +0000 2013","id":414071361066532864,"id_str":"414071361066532864","text":"The Dick Tonsils #FreeBandNames\u00a0","source":"\u003ca href=\"http:\/\/www.myplume.com\/\" rel=\"nofollow\"\u003ePlume\u00a0for\u00a0Android\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":29296581,"id_str":"29296581","name":"Reboot","screen_name":"Just_Reboot","location":"NC","description":"G+ http:\/\/t.co\/MyXmhCAtnD\r\n#TeamKang PR, Attempted Writer, IT Tech, Social Media Berserker and Entertainer, Adjectives, Assertions, Disclaimers","url":null,"entities":{"description":{"urls":[{"url":"http:\/\/t.co\/MyXmhCAtnD","expanded_url":"http:\/\/goo.gl\/giVTc","display_url":"goo.gl\/giVTc","indices":[3,25]}]}},"protected":false,"followers_count":2244,"friends_count":435,"listed_count":60,"created_at":"Mon Apr 06 21:21:27 +0000 2009","favourites_count":79,"utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":27703,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"131516","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/871578268\/5db26751bf732750f5ca1ed4f9f59309.png","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/871578268\/5db26751bf732750f5ca1ed4f9f59309.png","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/378800000480531175\/93e8b5cb95a635ebb81302f5b6044a76_normal.jpeg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/378800000480531175\/93e8b5cb95a635ebb81302f5b6044a76_normal.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/29296581\/1368898490","profile_link_color":"009999","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"EFEFEF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":1,"favorite_count":1,"entities":{"hashtags":[{"text":"FreeBandNames","indices":[17,31]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"}],"search_metadata":{"completed_in":0.012,"max_id":414075829182676992,"max_id_str":"414075829182676992","next_results":"?max_id=414071361066532863&q=%23freebandnames&count=3&include_entities=1","query":"%23freebandnames","refresh_url":"?since_id=414075829182676992&q=%23freebandnames&include_entities=1","count":3,"since_id":0,"since_id_str":"0"}} \ No newline at end of file diff --git a/spec/fixtures/search2.json b/spec/fixtures/search2.json new file mode 100644 index 000000000..c3c7fc768 --- /dev/null +++ b/spec/fixtures/search2.json @@ -0,0 +1 @@ +{"statuses":[{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Dec 20 16:24:22 +0000 2013","id":414068770484019200,"id_str":"414068770484019200","text":"The Thumb Dumpsters #FreeBandNames","source":"\u003ca href=\"http:\/\/www.myplume.com\/\" rel=\"nofollow\"\u003ePlume\u00a0for\u00a0Android\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":29296581,"id_str":"29296581","name":"Reboot","screen_name":"Just_Reboot","location":"NC","description":"G+ http:\/\/t.co\/MyXmhCAtnD\r\n#TeamKang PR, Attempted Writer, IT Tech, Social Media Berserker and Entertainer, Adjectives, Assertions, Disclaimers","url":null,"entities":{"description":{"urls":[{"url":"http:\/\/t.co\/MyXmhCAtnD","expanded_url":"http:\/\/goo.gl\/giVTc","display_url":"goo.gl\/giVTc","indices":[3,25]}]}},"protected":false,"followers_count":2244,"friends_count":435,"listed_count":60,"created_at":"Mon Apr 06 21:21:27 +0000 2009","favourites_count":79,"utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":27703,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"131516","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/871578268\/5db26751bf732750f5ca1ed4f9f59309.png","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/871578268\/5db26751bf732750f5ca1ed4f9f59309.png","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/378800000480531175\/93e8b5cb95a635ebb81302f5b6044a76_normal.jpeg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/378800000480531175\/93e8b5cb95a635ebb81302f5b6044a76_normal.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/29296581\/1368898490","profile_link_color":"009999","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"EFEFEF","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"FreeBandNames","indices":[20,34]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Fri Dec 20 05:23:19 +0000 2013","id":413902412236083201,"id_str":"413902412236083201","text":"The Shit Smelters #freebandnames","source":"web","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":41614761,"id_str":"41614761","name":"Grizzlam","screen_name":"mattyp90","location":"","description":"Chicago via Nashville via Memphis","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":585,"friends_count":745,"listed_count":8,"created_at":"Thu May 21 16:05:09 +0000 2009","favourites_count":9873,"utc_offset":-21600,"time_zone":"Central Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":13716,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"1A1B1F","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme9\/bg.gif","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme9\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/378800000726393028\/b17facdd394b0992dad251e3cc6b3f60_normal.png","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/378800000726393028\/b17facdd394b0992dad251e3cc6b3f60_normal.png","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/41614761\/1375052004","profile_link_color":"2FC2EF","profile_sidebar_border_color":"181A1E","profile_sidebar_fill_color":"252429","profile_text_color":"666666","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"freebandnames","indices":[18,32]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"},{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Tue Dec 17 17:04:32 +0000 2013","id":412991713435975680,"id_str":"412991713435975680","text":"Big yellow moon #skyatnight #freebandnames","source":"\u003ca href=\"http:\/\/twitter.com\/download\/android\" rel=\"nofollow\"\u003eTwitter for Android\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":352463222,"id_str":"352463222","name":"Dean Weston","screen_name":"thehatsofdean","location":"Leicester","description":"Creative web designer & East Midlands hat wearer of the year 1993","url":"http:\/\/t.co\/l2Sn5tMK5W","entities":{"url":{"urls":[{"url":"http:\/\/t.co\/l2Sn5tMK5W","expanded_url":"http:\/\/www.deanweston.co.uk","display_url":"deanweston.co.uk","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":37,"friends_count":68,"listed_count":0,"created_at":"Wed Aug 10 17:16:14 +0000 2011","favourites_count":5,"utc_offset":0,"time_zone":"London","geo_enabled":true,"verified":false,"statuses_count":914,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"EDE1B3","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/308734021\/twitter_bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/308734021\/twitter_bg.gif","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/2796373361\/83a960755f87b2557a44da86a11da9e4_normal.png","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/2796373361\/83a960755f87b2557a44da86a11da9e4_normal.png","profile_link_color":"993300","profile_sidebar_border_color":"ADA47E","profile_sidebar_fill_color":"D1C69D","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"favorite_count":0,"entities":{"hashtags":[{"text":"skyatnight","indices":[16,27]},{"text":"freebandnames","indices":[28,42]}],"symbols":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false,"lang":"en"}],"search_metadata":{"completed_in":0.072,"max_id":414068770484019200,"max_id_str":"414068770484019200","query":"%23freebandnames","refresh_url":"?since_id=414068770484019200&q=%23freebandnames&include_entities=1","count":15,"since_id":0,"since_id_str":"0"}} \ No newline at end of file diff --git a/spec/twitter/cursor_spec.rb b/spec/twitter/cursor_spec.rb index c04238286..99c444ee1 100644 --- a/spec/twitter/cursor_spec.rb +++ b/spec/twitter/cursor_spec.rb @@ -27,42 +27,4 @@ end end - describe '#first?' do - context 'when previous cursor equals zero' do - before do - @cursor = Twitter::Cursor.new({:previous_cursor => 0}, :ids, nil, Twitter::REST::Client.new, :get, '/1.1/followers/ids.json', {}) - end - it 'returns true' do - expect(@cursor.first?).to be true - end - end - context 'when previous cursor does not equal zero' do - before do - @cursor = Twitter::Cursor.new({:previous_cursor => 1}, :ids, nil, Twitter::REST::Client.new, :get, '/1.1/followers/ids.json', {}) - end - it 'returns true' do - expect(@cursor.first?).to be false - end - end - end - - describe '#last?' do - context 'when next cursor equals zero' do - before do - @cursor = Twitter::Cursor.new({:next_cursor => 0}, :ids, nil, Twitter::REST::Client.new, :get, '/1.1/followers/ids.json', {}) - end - it 'returns true' do - expect(@cursor.last?).to be true - end - end - context 'when next cursor does not equal zero' do - before do - @cursor = Twitter::Cursor.new({:next_cursor => 1}, :ids, nil, Twitter::REST::Client.new, :get, '/1.1/followers/ids.json', {}) - end - it 'returns false' do - expect(@cursor.last?).to be false - end - end - end - end diff --git a/spec/twitter/rest/api/search_spec.rb b/spec/twitter/rest/api/search_spec.rb index c3df6bc10..6e95ed44e 100644 --- a/spec/twitter/rest/api/search_spec.rb +++ b/spec/twitter/rest/api/search_spec.rb @@ -8,30 +8,24 @@ describe '#search' do before do - stub_get('/1.1/search/tweets.json').with(:query => {:q => 'twitter'}).to_return(:body => fixture('search.json'), :headers => {:content_type => 'application/json; charset=utf-8'}) + stub_get('/1.1/search/tweets.json').with(:query => {:q => '#freebandnames'}).to_return(:body => fixture('search.json'), :headers => {:content_type => 'application/json; charset=utf-8'}) end it 'requests the correct resource' do - @client.search('twitter') - expect(a_get('/1.1/search/tweets.json').with(:query => {:q => 'twitter'})).to have_been_made + @client.search('#freebandnames') + expect(a_get('/1.1/search/tweets.json').with(:query => {:q => '#freebandnames'})).to have_been_made end it 'returns recent Tweets related to a query with images and videos embedded' do - search = @client.search('twitter') + search = @client.search('#freebandnames') expect(search).to be_a Twitter::SearchResults expect(search.first).to be_a Twitter::Tweet - expect(search.first.text).to eq('Bubble Mailer #freebandnames') + expect(search.first.text).to eq('@Just_Reboot #FreeBandNames mono surround') end - it 'returns the max_id value for a search result' do - search = @client.search('twitter') - expect(search.max_id).to eq(250_126_199_840_518_145) - end - context 'when search API responds a malformed result' do before do - stub_get('/1.1/search/tweets.json').with(:query => {:q => 'twitter'}).to_return(:body => fixture('search_malformed.json'), :headers => {:content_type => 'application/json; charset=utf-8'}) + stub_get('/1.1/search/tweets.json').with(:query => {:q => '#freebandnames'}).to_return(:body => fixture('search_malformed.json'), :headers => {:content_type => 'application/json; charset=utf-8'}) end - it 'returns an empty array' do - search = @client.search('twitter') + search = @client.search('#freebandnames') expect(search.to_a).to be_an Array expect(search.to_a).to be_empty end diff --git a/spec/twitter/search_results_spec.rb b/spec/twitter/search_results_spec.rb index 350366052..abf50bac9 100644 --- a/spec/twitter/search_results_spec.rb +++ b/spec/twitter/search_results_spec.rb @@ -4,123 +4,27 @@ describe '#each' do before do - @search_results = Twitter::SearchResults.new(:statuses => [{:id => 1}, {:id => 2}, {:id => 3}, {:id => 4}, {:id => 5}, {:id => 6}]) + @client = Twitter::REST::Client.new(:consumer_key => 'CK', :consumer_secret => 'CS', :access_token => 'AT', :access_token_secret => 'AS') + stub_get('/1.1/search/tweets.json').with(:query => {:q => '#freebandnames'}).to_return(:body => fixture('search.json'), :headers => {:content_type => 'application/json; charset=utf-8'}) + stub_get('/1.1/search/tweets.json').with(:query => {:q => '#freebandnames', :count => '3', :include_entities => '1', :max_id => '414071361066532863'}).to_return(:body => fixture('search2.json'), :headers => {:content_type => 'application/json; charset=utf-8'}) + end + it 'requests the correct resources' do + @client.search('#freebandnames').each {} + expect(a_get('/1.1/search/tweets.json').with(:query => {:q => '#freebandnames'})).to have_been_made + expect(a_get('/1.1/search/tweets.json').with(:query => {:q => '#freebandnames', :count => '3', :include_entities => '1', :max_id => '414071361066532863'})).to have_been_made end it 'iterates' do count = 0 - @search_results.each { count += 1 } + @client.search('#freebandnames').each { count += 1 } expect(count).to eq(6) end context 'with start' do it 'iterates' do count = 0 - @search_results.each(5) { count += 1 } + @client.search('#freebandnames').each(5) { count += 1 } expect(count).to eq(1) end end end - describe '#completed_in' do - it 'returns a number of seconds' do - completed_in = Twitter::SearchResults.new(:search_metadata => {:completed_in => 0.029}).completed_in - expect(completed_in).to be_a Float - expect(completed_in).to eq(0.029) - end - it 'is nil when not set' do - completed_in = Twitter::SearchResults.new.completed_in - expect(completed_in).to be_nil - end - end - - describe '#max_id' do - it 'returns an ID' do - max_id = Twitter::SearchResults.new(:search_metadata => {:max_id => 250_126_199_840_518_145}).max_id - expect(max_id).to be_an Integer - expect(max_id).to eq(250_126_199_840_518_145) - end - it 'is nil when not set' do - max_id = Twitter::SearchResults.new.max_id - expect(max_id).to be_nil - end - end - - describe '#page' do - it 'returns page number' do - page = Twitter::SearchResults.new(:search_metadata => {:page => 1}).page - expect(page).to be_an Integer - expect(page).to eq(1) - end - it 'is nil when not set' do - page = Twitter::SearchResults.new.page - expect(page).to be_nil - end - end - - describe '#query' do - it 'returns the query' do - query = Twitter::SearchResults.new(:search_metadata => {:query => '%23freebandnames'}).query - expect(query).to be_a String - expect(query).to eq('%23freebandnames') - end - it 'is nil when not set' do - query = Twitter::SearchResults.new.query - expect(query).to be_nil - end - end - - describe '#results_per_page' do - it 'returns the number of results per page' do - results_per_page = Twitter::SearchResults.new(:search_metadata => {:count => 4}).results_per_page - expect(results_per_page).to be_an Integer - expect(results_per_page).to eq(4) - end - it 'is nil when not set' do - results_per_page = Twitter::SearchResults.new.results_per_page - expect(results_per_page).to be_nil - end - end - - describe '#since_id' do - it 'returns an ID' do - since_id = Twitter::SearchResults.new(:search_metadata => {:since_id => 250_126_199_840_518_145}).since_id - expect(since_id).to be_an Integer - expect(since_id).to eq(250_126_199_840_518_145) - end - it 'is nil when not set' do - since_id = Twitter::SearchResults.new.since_id - expect(since_id).to be_nil - end - end - - describe '#next_results?' do - it 'returns true when next_results is set' do - next_results = Twitter::SearchResults.new(:search_metadata => {:next_results => '?'}).next_results? - expect(next_results).to be true - end - it 'returns false when next_results is not set' do - next_results = Twitter::SearchResults.new(:search_metadata => {}).next_results? - expect(next_results).to be false - end - it 'returns false is search_metadata is not set' do - next_results = Twitter::SearchResults.new.next_results? - expect(next_results).to be false - end - end - - describe '#next_results' do - it 'returns a hash of query parameters' do - search_results = Twitter::SearchResults.new(:search_metadata => {:next_results => '?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed'}) - expect(search_results.next_results).to be_a Hash - expect(search_results.next_results[:max_id]).to eq('249279667666817023') - end - end - - describe '#refresh_results' do - it 'returns a hash of query parameters' do - search_results = Twitter::SearchResults.new(:search_metadata => {:refresh_url => '?since_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=recent'}) - expect(search_results.refresh_results).to be_a Hash - expect(search_results.refresh_results[:since_id]).to eq('249279667666817023') - end - end - end