Skip to content
Browse files

Documentation and API changes to Search class

  • Loading branch information...
1 parent 7fd5b4e commit e769fabc0232cbbcb9d0fa5a07277fb9f50b17c8 @sferik committed Oct 31, 2010
Showing with 269 additions and 168 deletions.
  1. +11 −11 README.md
  2. +251 −152 lib/twitter/search.rb
  3. +7 −5 spec/twitter/search_spec.rb
View
22 README.md
@@ -18,18 +18,18 @@ calls to the <tt>Twitter::Client</tt> class](http://github.com/jnunemaker/twitte
In a handful of cases, method names were changed to resolve namespace conflicts.
* **Pre-1.0**
- Twitter::Base.new.user('sferik').name
+ Twitter::Base.new.user("sferik").name
* **Post-1.0**
- Twitter::Client.new.user('sferik').name
+ Twitter::Client.new.user("sferik").name
The <tt>Twitter::Search</tt> class has remained largely the same, however it no longer accepts a
query in its constructor. You can specify a query using the <tt>#containing</tt> method, which is
aliased to <tt>#q</tt>.
* **Pre-1.0**
- Twitter::Search.new('query').fetch.results.first.text
+ Twitter::Search.new("query").fetch.first.text
* **Post-1.0**
- Twitter::Search.new.q('query').fetch.results.first.text
+ Twitter::Search.new.q("query").fetch.first.text
The error classes have gone through a transformation to make them consistent with [Twitter's documented
response codes](http://dev.twitter.com/pages/responses_errors). These changes should make it easier to
@@ -104,7 +104,7 @@ you would like to continue using the [oauth gem](http://github.com/oauth/oauth-r
simply require it and make the following changes:
* **Pre-1.0**
- options = {:api_endpoint => 'http://api.twitter.com', :signing_endpoint => 'http://api.twitter.com'}
+ options = {:api_endpoint => "http://api.twitter.com", :signing_endpoint => "http://api.twitter.com"}
oauth_wrapper = Twitter::OAuth.new(YOUR_CONSUMER_TOKEN, YOUR_CONSUMER_SECRET, options)
oauth_wrapper.set_callback_url(CALLBACK_URL)
signing_consumer = oauth_wrapper.signing_consumer
@@ -113,7 +113,7 @@ simply require it and make the following changes:
oauth_wrapper.authorize_from_request(request_token.token, request_token.secret, params[:oauth_verifier])
* **Post-1.0**
- options = {:site => 'http://api.twitter.com', :request_endpoint => 'http://api.twitter.com'}
+ options = {:site => "http://api.twitter.com", :request_endpoint => "http://api.twitter.com"}
signing_consumer = OAuth::Consumer.new(YOUR_CONSUMER_TOKEN, YOUR_CONSUMER_SECRET, options)
request_token = signing_consumer.get_request_token(:oauth_callback => CALLBACK_URL)
redirect_to request_token.authorize_url
@@ -151,8 +151,8 @@ Documentation
Usage Examples
--------------
- require 'rubygems'
- require 'twitter'
+ require "rubygems"
+ require "twitter"
# Get a user's location
puts Twitter.user("sferik").location
@@ -167,18 +167,18 @@ Usage Examples
search = Twitter::Search.new
# Find the 3 most recent marriage proposals to @justinbieber
- search.containing('marry me').to('justinbieber').result_type('recent').per_page(3).each do |r|
+ search.containing("marry me").to("justinbieber").result_type("recent").per_page(3).each do |r|
puts "#{r.from_user}: #{r.text}"
end
# Enough about Justin Bieber
search.clear
# Let's find a Japanese-language status update tagged #ruby
- puts search.hashtag('ruby').language('ja').no_retweets.per_page(1).fetch.results.first.text
+ puts search.hashtag("ruby").language("ja").no_retweets.per_page(1).fetch.first.text
# And another
- puts search.fetch_next_page.results.first.text
+ puts search.fetch_next_page.first.text
# Certain methods require authentication. To get your Twitter OAuth credentials,
# register an app at http://dev.twitter.com/apps
View
403 lib/twitter/search.rb
@@ -2,305 +2,404 @@
module Twitter
- # Handles the Twitter Search API
+ # Wrapper for the Twitter Search API.
#
- # @see http://dev.twitter.com/doc/get/search Twitter Search API docs
+ # *Notice*: As of April 1st 2010, the Search API provides an option to retrieve
+ # "popular tweets" in addition to real-time search results. In an upcoming release,
+ # this will become the default and clients that don't want to receive popular tweets
+ # in their search results will have to explicitly opt-out. See {Twitter::Search#result_type}
+ # for more information.
+ #
+ # *Warning*: The user ids in the Search API are different from those in the REST API
+ # ({http://dev.twitter.com/pages/api_overview about the two APIs}). This defect is
+ # being tracked by {http://code.google.com/p/twitter-api/issues/detail?id=214 Issue 214}.
+ # This means that the to_user_id and from_user_id field vary from the actualy user id on
+ # Twitter.com. Applications will have to perform a screen name-based lookup with
+ # {Twitter::Client::User#user} to get the correct user id if necessary.
+ # @see http://dev.twitter.com/doc/get/search Twitter Search API Documentation
class Search < API
- attr_reader :fetch, :query
+ attr_reader :query
- # Creates a new instance of a search
- #
- # @param [String] query the optional keyword to search
+ # Creates a new search
def initialize(*)
clear
super
end
alias :api_endpoint :search_endpoint
- # Clears all the query filters to make a new search
+ # Clears all query filters and cached results
+ #
+ # @return [Twitter::Search] self
+ # @example
+ # search = Twitter::Search.new
+ # search.containing("twitter").fetch
+ # search.clear
+ # search.fetch_next_page #=> 403 Forbidden: You must enter a query.
def clear
- @fetch = nil
+ @cache = nil
@query = {}
@query[:q] = []
self
end
- # Search only tweets containing a keyword
+ # @group Generic filters
+
+ # Search query
#
- # @param word [String] word by which to filter
- def containing(word)
- @query[:q] << word
+ # @param query [String] The search query
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").fetch # Returns an array of tweets containing "twitter"
+ # Twitter::Search.new.contains("twitter").fetch # Shortcut for the above
+ # Twitter::Search.new.q("twitter").fetch # Even shorter-cut
+ def containing(query)
+ @query[:q] << query
self
end
alias :contains :containing
alias :q :containing
- # Search only tweets containing a keyword
+ # Negative search query
#
- # @param word [String] word by which to filter
- def not_containing(word)
- @query[:q] << "-#{word}"
+ # @param query [String] The negative search query
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("beer").not_containing("root").fetch # Returns an array of tweets containing "beer" but not "root"
+ # Twitter::Search.new.containing("beer").does_not_contain("root").fetch # Same as above
+ # Twitter::Search.new.containing("beer").excluding("root").fetch # Shortcut for the above
+ # Twitter::Search.new.contains("beer").excludes("root").fetch # Even shorter
+ # Twitter::Search.new.q("beer").exclude("root").fetch # Shorter still
+ def not_containing(query)
+ @query[:q] << "-#{query}"
self
end
alias :does_not_contain :not_containing
alias :excluding :not_containing
alias :excludes :not_containing
alias :exclude :not_containing
- # @group Search parameters
+ # Search for a specific phrase instead of a group of words
+ #
+ # @param phrase [String] The search phrase
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.phrase("happy hour").fetch # Returns an array of tweets containing the phrase "happy hour"
+ def phrase(phrase)
+ @query[:phrase] = phrase
+ self
+ end
+
+ # Only include tweets that contain URLs
+ #
+ # @param filter [String] A type of search filter. Only 'links' is currently effective.
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").filter.fetch # Returns an array of tweets containing "twitter" and URLs
+ def filter(filter='links')
+ @query[:q] << "filter:#{filter}"
+ self
+ end
+
+ # Only include tweets from after a given date, specified in the formatted YYYY-MM-DD
+ #
+ # @param date [String] A date in the format YYYY-MM-DD
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").since_date("2010-10-01").fetch # Return an array of tweets containing "twitter" since October 1, 2010
+ def since_date(date)
+ @query[:since] = date
+ self
+ end
+ alias :since :since_date
+
+ # Only include tweets from before a given date, specified in the formatted YYYY-MM-DD
#
- # Restricts tweets to the given language
+ # @param date [String] A date in the format YYYY-MM-DD
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").since_date("2010-10-01").fetch # Return an array of tweets containing "twitter" up until October 1, 2010
+ def until_date(date)
+ @query[:until] = date
+ self
+ end
+ alias :until :until_date
+
+ # @group Demographic filters
+
+ # Only include tweets in a given language, specified by an ISO 639-1 code
#
- # @param code [String] the ISO 639-1 language code (en, fr, de, ja, etc.)
+ # @param code [String] An ISO 639-1 code
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").language("fr").fetch # Returns an array of French-language tweets containing "twitter"
+ # Twitter::Search.new.containing("twitter").lang("fr").fetch # Shortcut for the above
# @see http://en.wikipedia.org/wiki/ISO_639-1
def language(code)
@query[:lang] = code
self
end
alias :lang :language
- # Specify the language of the query you are sending
- # (only ja is currently effective). This is intended for
- # language-specific clients and the default should work in
- # the majority of cases.
+ # Specify the language of the query you are sending.
+ # This is intended for language-specific clients and
+ # the default should work in the majority of cases.
+ #
+ # @param code [String] An ISO 639-1 code (only 'ja' is currently effective)
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").locale("ja").fetch # Returns an array of tweets from Japan containing "twitter"
+ # @see http://en.wikipedia.org/wiki/ISO_639-1
+ def locale(code)
+ @query[:locale] = code
+ self
+ end
+
+ # Only include tweets from users located within a given radius of a given location, specified by latitude and longitude
#
- # @param locale [String] the language code for your query
- def locale(locale)
- @query[:locale] = locale
+ # @param lat [Float] A latitude
+ # @param long [Float] A longitude
+ # @param radius [String] A search radius, specified in either 'mi' or 'km'
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").geocode(37.781157, -122.398720, "1mi").fetch # Returns an array of tweets within a 1-mile radius of Twitter HQ
+ def geocode(lat, long, radius)
+ @query[:geocode] = [lat, long, radius].join(",")
self
end
- # Search only tweets from a particular user
+ # @group User filters
+
+ # Only include tweets from a given user, specified by screen_name
#
- # @param user [String] screen name of user by which to filter
- def from(user)
- @query[:q] << "from:#{user}"
+ # @param screen_name [String] A Twitter user name
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").from("sferik").fetch # Returns an array of tweets containing "twitter" from @sferik
+ def from(screen_name)
+ @query[:q] << "from:#{screen_name}"
self
end
- # Exclude tweets from a particular user
+ # Exclude tweets from a given user, specified by screen_name
#
- # @param user [String] screen name of user by which to filter
- def not_from(user)
- @query[:q] << "-from:#{user}"
+ # @param screen_name [String] A Twitter user name
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").not_from("sferik").fetch # Returns an array of tweets containing "twitter" from everyone except @sferik
+ def not_from(screen_name)
+ @query[:q] << "-from:#{screen_name}"
self
end
- # Search only tweets to a particular user
+ # Only include tweets to a given user, specified by screen_name
#
- # @param user [String] screen name of user by which to filter
- def to(user)
- @query[:q] << "to:#{user}"
+ # @param screen_name [String] A Twitter user name
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").to("sferik").fetch # Returns an array of tweets containing "twitter" to @sferik
+ def to(screen_name)
+ @query[:q] << "to:#{screen_name}"
self
end
- # Exclude tweets to a particular user
+ # Exclude tweets to a given user, specified by screen_name
#
- # @param user [String] screen name of user by which to filter
- def not_to(user)
- @query[:q] << "-to:#{user}"
+ # @param screen_name [String] A Twitter user name
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").not_to("sferik").fetch # Returns an array of tweets containing "twitter" to everyone except @sferik
+ def not_to(screen_name)
+ @query[:q] << "-to:#{screen_name}"
self
end
- # Search only tweets referencing a particular user
+ # Only include tweets mentioning a given user, specified by screen_name
#
- # @param user [String] screen name of user by which to filter
- def mentioning(user)
- @query[:q] << "@#{user.gsub('@', '')}"
+ # @param screen_name [String] A Twitter user name
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").mentioning("sferik").fetch # Returns an array of tweets containing "twitter" and mentioning @sferik
+ def mentioning(screen_name)
+ @query[:q] << "@#{screen_name.gsub('@', '')}"
self
end
alias :referencing :mentioning
alias :mentions :mentioning
alias :references :mentioning
- # Search only tweets referencing a particular user
+ # Exclude tweets mentioning a given user, specified by screen_name
#
- # @param user [String] screen name of user by which to filter
- def not_mentioning(user)
- @query[:q] << "-@#{user.gsub('@', '')}"
+ # @param screen_name [String] A Twitter user name
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").not_mentioning("sferik").fetch # Returns an array of tweets containing "twitter" but not mentioning @sferik
+ def not_mentioning(screen_name)
+ @query[:q] << "-@#{screen_name.gsub('@', '')}"
self
end
alias :not_referencing :not_mentioning
alias :does_not_mention :not_mentioning
alias :does_not_reference :not_mentioning
- # Add a search filter
+ # @group Twitter filters
+
+ # Only include retweets
#
+ # @return [Twitter::Search] self
# @example
- # Twitter::Search.new.containing('twitter').filter('links')
- #
- # @param filter [String] filter to add to the search
- def filter(filter)
- @query[:q] << "filter:#{filter}"
- self
- end
-
- # Only show retweets
+ # Twitter::Search.new.containing("twitter").retweets.fetch # Returns an array of retweets containing "twitter"
def retweets
@query[:q] << "rt"
self
end
- # Only show original status updates (i.e., not retweets)
+ # Only include original status updates (i.e., not retweets)
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").no_retweets.fetch # Returns an array of tweets containing "twitter", excluding retweets
def no_retweets
@query[:q] << "-rt"
self
end
- # Search a hashtag
+ # Only include tweets containing a given hashtag
#
- # @param tag [String] the hashtag
+ # @param tag [String] A Twitter hashtag
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.hashtag("FollowFriday").fetch # Returns an array of tweets containing the hashtag #FollowFriday
def hashtag(tag)
@query[:q] << "\##{tag.gsub('#', '')}"
self
end
- alias :hashed :hashtag
- # Search a hashtag
+ # Exclude tweets containing a given hashtag
#
- # @param tag [String] the hashtag
- def not_hashtag(tag)
+ # @param tag [String] A Twitter hashtag
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.hashtag("FollowFriday").excluding_hashtag("FF").fetch # Returns an array of tweets containing the hashtag #FollowFriday but not #FF
+ # Twitter::Search.new.hashtag("FollowFriday").excludes_hashtag("FF").fetch # Shortcut for the above
+ # Twitter::Search.new.hashtag("FollowFriday").exclude_hashtag("FF").fetch # Even shorter
+ def excluding_hashtag(tag)
@query[:q] << "-\##{tag.gsub('#', '')}"
self
end
- alias :not_hashed :not_hashtag
+ alias :excludes_hashtag :excluding_hashtag
+ alias :exclude_hashtag :excluding_hashtag
- # Search for a phrase instead of a group of words
+ # Specify what type of search results you want to receive
#
- # @param phrase [String] the search phrase
- def phrase(phrase)
- @query[:phrase] = phrase
- self
- end
-
- # Specifies what type of search results you would prefer to receive
- #
- # @param result_type [String] the results you want to receive: 'mixed', 'recent', 'popular'
- def result_type(result_type='mixed')
+ # @param result_type [String] The type of results you want to receive ('recent', 'popular', or 'mixed')
+ # @return [Twitter::Search] self
+ # @example
+ # Twitter::Search.new.containing("twitter").result_type('recent').fetch # Returns an array of recent tweets containing "twitter"
+ def result_type(result_type="mixed")
@query[:result_type] = result_type
self
end
- # Returns results with an ID greater than (that is, more recent than)
- # the specified ID.
+ # Only include tweets with an ID greater than (that is, more recent than) the specified ID.
#
+ # @param id [Integer] A Twitter status ID
+ # @return [Twitter::Search] self
# @example
- # Twitter::Search.new.containing('twitter').since_id(123456789)
- #
- # @param id [Integer] the status ID of the tweet after which to search
+ # Twitter::Search.new.containing("twitter").since_id(123456789).fetch # Returns an array of tweets containing "twitter" with an ID greater than 123456789
def since_id(id)
@query[:since_id] = id
self
end
- alias :since :since_id
- # Returns results with an ID less than (that is, older than)
- # the specified ID.
+ # Only include tweets with an ID less than or equal to the specified ID
#
+ # @param id [Integer] A Twitter status ID
+ # @return [Twitter::Search] self
# @example
- # Twitter::Search.new.containing('twitter').max_id(123456789)
+ # Twitter::Search.new.containing("twitter").max_id(123456789).fetch # Returns an array of tweets containing "twitter" with an ID less than or equal to 123456789
#
- # @param id [Integer] the status ID of the tweet before which to search
def max_id(id)
@query[:max_id] = id
self
end
alias :max :max_id
- # Returns results with a date greater than the specified date
- #
- # ** From the advanced search form, not documented in the API
- # Format: YYYY-MM-DD
- #
- # @example
- # Twitter::Search.new.containing('twitter').since_date('2010-10-10')
- #
- # @param since_date [String] the search date in YYYY-MM-DD format
- def since_date(since_date)
- @query[:since] = since_date
- self
- end
+ # @group Paging
- # Returns results with a date less than the specified date
+ # Specify the number of tweets to return per page
#
+ # @param number [Integer] The number of tweets to return per page, maximum 100
+ # @return [Twitter::Search] self
# @example
- # Twitter::Search.new.containing('twitter').until_date('2010-10-10')
- #
- # @param until_date [String] the search date in YYYY-MM-DD format
- def until_date(until_date)
- @query[:until] = until_date
+ # Twitter::Search.new.containing("twitter").per_page(100).fetch # Returns an array of 100 tweets containing "twitter"
+ def per_page(number=15)
+ @query[:rpp] = number
self
end
- alias :until :until_date
+ alias :rpp :per_page
- # Returns tweets by users located within a given radius of the
- # given latitude/longitude.
+ # Specify the page number to return, up to a maximum of roughly 1500 results
#
+ # @param number [Integer] A page number, starting at 1
+ # @return [Twitter::Search] self
# @example
- # Twitter::Search.new.containing('twitter').geocode(37.781157, -122.398720, "1mi")
- #
- # @param lat [Float] the latitude to search
- # @param long [Float] the longitude to search
- # @param range [String] the radius of the search in either 'mi' or 'km'
- def geocode(lat, long, range)
- @query[:geocode] = [lat, long, range].join(",")
- self
- end
-
- # @group Paging
- #
- # The number of tweets to return per page
- #
- # @param num [Integer] the number of tweets per page, maximum 100
- def per_page(num)
- @query[:rpp] = num
+ # Twitter::Search.new.containing("twitter").page(2).fetch # Returns the second page of tweets containing "twitter"
+ def page(number)
+ @query[:page] = number
self
end
- alias :rpp :per_page
- # The page number (starting at 1) to return, up to a max of roughly 1500 results
+ # Indicates if there are additional results to be fetched
#
- # @param page [Integer] the page number to return
- def page(num)
- @query[:page] = num
- self
- end
-
- # Indicates if the results have more to be fetched
+ # @return [Boolean]
+ # @example
+ # search = Twitter::Search.new.containing("twitter").fetch
+ # search.next_page? #=> true
def next_page?
- !!fetch["next_page"]
+ fetch if @cache.nil?
+ !!@cache["next_page"]
end
# @group Fetching
+
+ # Fetch the next page of results of the query
#
- # Fetch the next page of results in the query
+ # @return [Array] Tweets that match specified query.
+ # @example
+ # search = Twitter::Search.new.containing("twitter").fetch # Returns the first page of results
+ # search.fetch_next_page # Returns the second page of results
def fetch_next_page
if next_page?
- @fetch = get("search", CGI.parse(fetch["next_page"][1..-1]))
+ @cache = get("search", CGI.parse(@cache["next_page"][1..-1]))
+ @cache.results
end
end
- # Perform the search, hitting the API
+ # Fetch the results of the query
#
- # @param force [Boolean] optionally ignore cache and hit the API again
+ # @param force [Boolean] Ignore the cache and hit the API again.
+ # @return [Array] Tweets that match specified query.
+ # @example
+ # search = Twitter::Search.new.containing("twitter").fetch # Returns an array of tweets containing "twitter"
def fetch(force=false)
- if @fetch.nil? || force
- query = @query.dup
- query[:q] = query[:q].join(" ")
- @fetch = get("search", query)
+ if @cache.nil? || force
+ options = query.dup
+ options[:q] = options[:q].join(" ")
+ @cache = get("search", options)
end
- @fetch
+ @cache.results
end
- # Iterate over the results
+ # Calls block once for each element in self, passing that element as a parameter
#
+ # @yieldparam [Hashie::Mash] result Tweet that matches specified query.
+ # @yieldreturn [Array] Tweets that match specified query.
# @example
- # Twitter::Search.new.containing('twitter').each {|t| puts t.from_user}
+ # Twitter::Search.new.containing('marry me').to('justinbieber').each do |result|
+ # puts "#{result.from_user}: #{result.text}"
+ # end
def each
- results = fetch['results']
- return if results.nil?
- results.each{|result| yield result}
+ fetch.each{|result| yield result}
end
end
View
12 spec/twitter/search_spec.rb
@@ -193,10 +193,10 @@
end
- describe ".not_hashtag" do
+ describe ".excluding_hashtag" do
it "should set the query not to include hashtag" do
- @client.not_hashtag('twitter').query[:q].should include '-#twitter'
+ @client.excluding_hashtag('twitter').query[:q].should include '-#twitter'
end
end
@@ -279,17 +279,18 @@
stub_request(:get, "https://search.twitter.com/search.json").
with(:query => {"q" => "twitter"}).
to_return(:body => fixture("search.json"), :headers => {:content_type => "application/json; charset=utf-8"})
+ @client.containing('twitter')
end
it "should get the correct resource" do
- @client.containing('twitter').fetch.next_page?
+ @client.next_page?
a_request(:get, "https://search.twitter.com/search.json").
with(:query => {"q" => "twitter"}).
should have_been_made
end
it "should be true if there's another page" do
- next_page = @client.containing('twitter').fetch.next_page?
+ next_page = @client.next_page?
next_page.should be_true
end
@@ -304,10 +305,11 @@
stub_request(:get, "https://search.twitter.com/search.json").
with(:query => {"q" => "twitter", "page" => "2", "max_id" => "28857935752"}).
to_return(:body => fixture("search.json"), :headers => {:content_type => "application/json; charset=utf-8"})
+ @client.containing('twitter')
end
it "should get the correct resource" do
- @client.containing('twitter').fetch_next_page
+ @client.fetch_next_page
a_request(:get, "https://search.twitter.com/search.json").
with(:query => {"q" => "twitter"}).
should have_been_made

0 comments on commit e769fab

Please sign in to comment.
Something went wrong with that request. Please try again.