Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SearchResults object contains metadata and results array #261

Merged
merged 7 commits into from
Jun 1, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,21 @@ Return the text of the Tweet at https://twitter.com/sferik/statuses/27558893223
Twitter.status(27558893223).text
Find the 3 most recent marriage proposals to [@justinbieber][justinbieber]

Twitter.search("to:justinbieber marry me", :rpp => 3, :result_type => "recent").map do |status|
Twitter.search("to:justinbieber marry me", :rpp => 3, :result_type => "recent").results.map do |status|
"#{status.from_user}: #{status.text}"
end

Let's find a Japanese-language Tweet tagged #ruby (no retweets)

Twitter.search("#ruby -rt", :lang => "ja", :rpp => 1).first.text
Twitter.search("#ruby -rt", :lang => "ja", :rpp => 1).results.first.text

The search result object returned by `Twitter::Client#search` includes some metadata about the search
results:

Twitter.search("to:justinbieber marry me", :rpp => 3, :result_type => "recent").max_id => 28857935752

The `max_id` attribute can be used in your next search query as the `:since_id` parameter to only return newer
tweets.

Certain methods require authentication. To get your Twitter OAuth credentials,
register an app at http://dev.twitter.com/apps
Expand Down
18 changes: 7 additions & 11 deletions lib/twitter/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
require 'twitter/request'
require 'twitter/retweet'
require 'twitter/saved_search'
require 'twitter/search_results'
require 'twitter/settings'
require 'twitter/size'
require 'twitter/status'
Expand Down Expand Up @@ -1787,17 +1788,12 @@ def saved_search_destroy(id, options={})
# @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID. There are limits to the number of Tweets which can be accessed through the API. If the limit of Tweets has occured since the since_id, the since_id will be forced to the oldest ID available.
# @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
# @option options [Boolean, String, Integer] :with_twitter_user_id When set to either true, t or 1, the from_user_id and to_user_id values in the response will map to "official" user IDs which will match those returned by the REST API.
# @return [Array<Twitter::Status>] Return tweets that match a specified query
# @return [Twitter::SearchResults] Return tweets that match a specified query with search metadata
# @example Returns tweets related to twitter
# Twitter.search('twitter')
def search(q, options={})
if results = get("/search.json", options.merge(:q => q), :endpoint => search_endpoint)['results']
results.map do |status|
Twitter::Status.new(status)
end
else
[]
end
response = get("/search.json", options.merge(:q => q), :endpoint => search_endpoint)
Twitter::SearchResults.new(response)
end

# Returns recent statuses related to a query with images and videos embedded
Expand Down Expand Up @@ -1895,7 +1891,7 @@ def suggest_users(slug, options={})
# @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
# @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 200.
# @option options [Boolean, String, Integer] :trim_user Each tweet returned in a timeline will include a user object with only the author's numerical ID when set to true, 't' or 1.
# @option options [Boolean, String, Integer] :exclude_replies This parameter will prevent replies from appearing in the returned timeline. Using exclude_replies with the count parameter will mean you will receive up-to count tweets this is because the count parameter retrieves that many tweets before filtering out retweets and replies.
# @option options [Boolean, String, Integer] :exclude_replies This parameter will prevent replies from appearing in the returned timeline. Using exclude_replies with the count parameter will mean you will receive up-to count tweets - this is because the count parameter retrieves that many tweets before filtering out retweets and replies.
# @return [Array<Twitter::Status>]
# @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
# @example Return the 20 most recent statuses, including retweets if they exist, posted by the authenticating user and the users they follow
Expand Down Expand Up @@ -2038,7 +2034,7 @@ def retweets_of_me(options={})
# @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
# @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 200.
# @option options [Boolean, String, Integer] :trim_user Each tweet returned in a timeline will include a user object with only the author's numerical ID when set to true, 't' or 1.
# @option options [Boolean, String, Integer] :exclude_replies This parameter will prevent replies from appearing in the returned timeline. Using exclude_replies with the count parameter will mean you will receive up-to count tweets this is because the count parameter retrieves that many tweets before filtering out retweets and replies.
# @option options [Boolean, String, Integer] :exclude_replies This parameter will prevent replies from appearing in the returned timeline. Using exclude_replies with the count parameter will mean you will receive up-to count tweets - this is because the count parameter retrieves that many tweets before filtering out retweets and replies.
# @return [Array<Twitter::Status>]
# @example Return the 20 most recent statuses posted by @sferik
# Twitter.user_timeline('sferik')
Expand Down Expand Up @@ -2087,7 +2083,7 @@ def media_timeline(*args)
# @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
# @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 200.
# @option options [Boolean, String, Integer] :trim_user Each tweet returned in a timeline will include a user object with only the author's numerical ID when set to true, 't' or 1.
# @option options [Boolean, String, Integer] :exclude_replies This parameter will prevent replies from appearing in the returned timeline. Using exclude_replies with the count parameter will mean you will receive up-to count tweets this is because the count parameter retrieves that many tweets before filtering out retweets and replies.
# @option options [Boolean, String, Integer] :exclude_replies This parameter will prevent replies from appearing in the returned timeline. Using exclude_replies with the count parameter will mean you will receive up-to count tweets - this is because the count parameter retrieves that many tweets before filtering out retweets and replies.
# @return [Array<Twitter::Status>]
# @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
# @example Return the 20 most recent statuses from the authenticating user's network
Expand Down
63 changes: 63 additions & 0 deletions lib/twitter/search_results.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require 'twitter/base'

module Twitter
class SearchResults < Twitter::Base
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add specs for this new class.


# @return [Array<Twitter::Status>]
def results
@results ||= (@attrs['results'] || []).map{ |status| Twitter::Status.new(status) }
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, it's not necessary to declare a lazy_attr_reader if you're going to define a custom reader, like this one. It should be one or the other.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider aliasing collection to results to mirror the Twitter::Cursor class, which server a similar function as an envelope/wrapper class.

alias :collection :results

# @return [Float]
def completed_in
@attrs['completed_in']
end
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary to define basic reader methods if you've declared a lazy_attr_reader. This is precisely what lazy_attr_reader provides.


# @return [Fixnum]
def max_id
@attrs['max_id']
end

# @return [String]
def max_id_str
@attrs['max_id_str']
end

# @return [String]
def next_page
@attrs['next_page']
end

# @return [Fixnum]
def page
@attrs['page']
end

# @return [String]
def query
@attrs['query']
end

# @return [String]
def refresh_url
@attrs['refresh_url']
end

# @return [Fixnum]
def results_per_page
@attrs['results_per_page']
end

# @return [Fixnum]
def since_id
@attrs['since_id']
end

# @return [String]
def since_id_str
@attrs['since_id_str']
end
end
end

16 changes: 11 additions & 5 deletions spec/twitter/client/search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@
end
it "should return recent statuses related to a query with images and videos embedded" do
search = @client.search('twitter')
search.should be_an Array
search.first.should be_a Twitter::Status
search.first.text.should == "@KaiserKuo from not too far away your new twitter icon looks like Vader."
search.should be_a Twitter::SearchResults
search.results.should be_an Array
search.results.first.should be_a Twitter::Status
search.results.first.text.should == "@KaiserKuo from not too far away your new twitter icon looks like Vader."
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to assert that the response is, in fact, a Twitter:: SearchResults object, no?

end

it "should return the max_id value for a search result" do
search = @client.search('twitter')
search.max_id.should eq(28857935752)
end

context "when search API responds a malformed result" do
Expand All @@ -34,8 +40,8 @@

it "should not fail and return blank Array" do
search = @client.search('twitter')
search.should be_an Array
search.should have(0).items
search.results.should be_an Array
search.results.should have(0).items
end
end
end
Expand Down
135 changes: 135 additions & 0 deletions spec/twitter/search_results_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
require 'helper'

describe Twitter::SearchResults do

describe "#completed_in" do
it "should return completed_in" do
results = Twitter::SearchResults.new('completed_in' => 23.5)
results.completed_in.should == 23.5
end
it "should return nil when no value exists" do
results = Twitter::SearchResults.new
results.completed_in.should be_nil
end
end

describe "#max_id" do
it "should contain the max_id" do
results = Twitter::SearchResults.new('max_id' => 123456)
results.max_id.should == 123456
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.max_id.should be_nil
end
end

describe "#max_id_str" do
it "should contain the max_id_str" do
results = Twitter::SearchResults.new('max_id_str' => '123456')
results.max_id_str.should == '123456'
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.max_id_str.should be_nil
end
end

describe "#next_page" do
it "should contain the next_page" do
results = Twitter::SearchResults.new('next_page' => "?page=2&max_id=122078461840982016&q=blue%20angels&rpp=5")
results.next_page.should == "?page=2&max_id=122078461840982016&q=blue%20angels&rpp=5"
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.next_page.should be_nil
end
end

describe "#page" do
it "should contain the page" do
results = Twitter::SearchResults.new('page' => 2)
results.page.should == 2
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.page.should be_nil
end
end

describe "#query" do
it "should contain the query" do
results = Twitter::SearchResults.new('query' => 'blue+angels')
results.query.should == 'blue+angels'
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.query.should be_nil
end
end

describe "#refresh_url" do
it "should contain the refresh_url" do
results = Twitter::SearchResults.new('refresh_url' => '?since_id=122078461840982016&q=blue%20angels')
results.refresh_url.should == '?since_id=122078461840982016&q=blue%20angels'
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.refresh_url.should be_nil
end
end

describe "#results_per_page" do
it "should contain the results_per_page" do
results = Twitter::SearchResults.new('results_per_page' => 10)
results.results_per_page.should == 10
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.results_per_page.should be_nil
end
end

describe "#since_id" do
it "should contain the since_id" do
results = Twitter::SearchResults.new('since_id' => 123456)
results.since_id.should == 123456
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.since_id.should be_nil
end
end

describe "#since_id_str" do
it "should contain the since_id_str" do
results = Twitter::SearchResults.new('since_id_str' => '123456')
results.since_id_str.should == '123456'
end
it "should be nil when no value passed" do
results = Twitter::SearchResults.new
results.since_id_str.should be_nil
end
end

describe "#results" do
it "should contain twitter status objects" do
search_results = Twitter::SearchResults.new('results' => [{'text' => 'tweet!'}])
search_results.results.should be_a Array
search_results.results.first.should be_a Twitter::Status
end
it "should be an empty array if no search results passed" do
search_results = Twitter::SearchResults.new
search_results.results.should be_a Array
search_results.results.should == []
end
end

describe "#collection" do
it "should alias collection to results" do
search_results = Twitter::SearchResults.new('results' => [{'text' => 'tweet!'}])
search_results.collection.should be_a Array
search_results.collection.first.should be_a Twitter::Status
end
end
end