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

Add Twitter::SearchResults methods for handling next results #365

Merged
merged 2 commits into from
Apr 22, 2013

Conversation

KentonWhite
Copy link
Contributor

Twitter API v1.1 pages the search results. The information for the next page of search results is contained in the search_metadata field as a query string for the URL. This makes it difficult to use with the Twitter gem, which stores the query parameters as an options hash.

Added 2 methods to Twitter::SearchResults: next_results? and next_results

next_results? returns true if next_results if defined, false otherwise

next_results returns a hash of query parameters that can be merged into the original search options hash. Using the new hash then returns the next page of results.

Example usage:

search_results = twitter.search(query, opts)
while search_results.next_page? 
  opts.merge! search_results.next_page
  search_results = twitter.search(query, opts)
end

For backwards compatibility with v1 API, aliased next_results? to next_page? and next_results to next_page

Returns true if there is a next page for a Twitter SearchResult.
Returns false otherwise

Aliased to next_page? for backward compatibility with v1 API
Returns a hash of query parameters for accessing the next page of search results
Can be merged into the previous search options for easy access
@sferik
Copy link
Owner

sferik commented Mar 14, 2013

Thanks for the patch. My biggest concern is that Twitter::SearchResults#next_page returns a Hash that contains a variety of keys, many of which are unrelated to the next page.

When I needed to fetch all the results for a particular query, I wrote code like this:

def collect_with_max_id(collection=[], max_id=nil, &block)
  tweets = yield(max_id)
  return collection if tweets.nil?
  collection += tweets
  tweets.empty? ? collection.flatten : collect_with_max_id(collection, tweets.last.id - 1, &block)
end

query = "ruby on rails"
opts = {:count => 100} # specify whatever opts you want here
tweets = collect_with_max_id do |max_id|
  opts[:max_id] = max_id unless max_id.nil?
  Twitter.search(query, opts).results
end

Is there a compelling reason why your solution is better than this?

@KentonWhite
Copy link
Contributor Author

I agree with your concern that the hash contains keys that might be unrelated to the next page. However one doesn't need to use all of the keys. If all you are interested in is the :max_id parameter then just this parameter can be extracted from the hash and your options updated as per your example.

Would the interface be better if we return just the :max_id parameter and discarded the rest? I toyed with this idea but rejected it as potentially over restrictive. Do you have any thoughts?

The example you give is very good but has 2 drawbacks over using the next_results hash as returned by the Twitter API.

  1. Efficiency Each search must make one additional call to the Twitter API to check if there are any additional entries. In the worst case this will result in the code making twice as many calls to Twitter than is necessary. This slows down execution and wastes rate limited requests. Using next_requests? does not require a separate call to Twitter to determine if there are more search results to fetch.
  2. Assumed Ordering The method of looking at the last tweet's ID to determine the next : max_id assumes that tweets returned from Twitter are returned ordered descending by their ID. No where in the V1.1 API documentation does Twitter guarantee the ordering of their Tweets. While the ordering is plausible it may be just a coincidence at the moment and could change in the future. Should the ordering be something other than descending by ID it could result in duplicate tweets (e.g. if the ordering is random) or an infinite loop (e.g. if the ordering is reversed to ascending by ID). On the other hand, Twitter guarantees that the next_result field will contain the query for retrieving the next page of results. Using :max_id returned from this field does not require any assumptions about the order of tweets.

@sferik
Copy link
Owner

sferik commented Mar 14, 2013

@KentonWhite:

I agree with your concern that the hash contains keys that might be unrelated to the next page. However one doesn't need to use all of the keys. If all you are interested in is the :max_id parameter then just this parameter can be extracted from the hash and your options updated as per your example.

Would the interface be better if we return just the :max_id parameter and discarded the rest? I toyed with this idea but rejected it as potentially over restrictive. Do you have any thoughts?

There is already a Twitter::SearchResults#max_id method. I'm not sure what benefit you get pulling it out of a Hash? It seems simple enough to say:

results = Twitter.search(query, opts)

results.max_id
# versus
results.next_results[:max_id]

Efficiency Each search must make one additional call to the Twitter API to check if there are any additional entries. In the worst case this will result in the code making twice as many calls to Twitter than is necessary. This slows down execution and wastes rate limited requests. Using next_requests? does not require a separate call to Twitter to determine if there are more search results to fetch.

To be clear, I have no objection to adding Twitter::SearchResults#next_results?. If you separated this into two pull requests (one that implements Twitter::SearchResults#next_results? and one that implements Twitter::SearchResults#next_results), I'd be happy to immediately merge the former while we continue discussing the latter.

Assumed Ordering The method of looking at the last tweet's ID to determine the next :max_id assumes that tweets returned from Twitter are returned ordered descending by their ID. No where in the V1.1 API documentation does Twitter guarantee the ordering of their Tweets. While the ordering is plausible it may be just a coincidence at the moment and could change in the future. Should the ordering be something other than descending by ID it could result in duplicate tweets (e.g. if the ordering is random) or an infinite loop (e.g. if the ordering is reversed to ascending by ID). On the other hand, Twitter guarantees that the next_result field will contain the query for retrieving the next page of results. Using :max_id returned from this field does not require any assumptions about the order of tweets.

I'm just following the instructions in Twitter's guide to Working with Timelines. Specifically, they recommend:

Subtract 1 from the lowest Tweet ID returned from the previous request and use this for the value of max_id.

That said, your point is reasonable. Someone asked essentially the same question on the developer forums a year ago. This was the official reply, verbatim:

If you're curious about how IDs are generated these days, check out http://engineering.twitter.com/2010/06/announcing-snowflake.html

They are mostly sequential, but they've got a bit more complexity to them. In the vast majority of cases, doing a -1 or +1 trick on a status is sufficient. If you want to be sure, you can always just use a preserved ID and tolerate duplicates in your result.

I'm not saying "no". I just want to make sure this is necessary.

@KentonWhite
Copy link
Contributor Author

To be clear, I have no objection to adding Twitter::SearchResults#next_results?. If you separated this into two pull requests (one that implements Twitter::SearchResults#next_results? and one that implements Twitter::SearchResults#next_results), I'd be happy to immediately merge the former while we continue discussing the latter.

Closing this pull request and splitting into 2 separate pull requests. Will continue the discussion on Twitter::SearchResults#next_results on the new pull request.

@KentonWhite KentonWhite reopened this Mar 14, 2013
@KentonWhite
Copy link
Contributor Author

There is already a Twitter::SearchResults#max_id method. I'm not sure what benefit you get pulling it out of a Hash? It seems simple enough to say:

results = Twitter.search(query, opts)

results.max_id
# versus
results.next_results[:max_id]

Yes there is such a method, and it pulls the :max_id from the :search_metadata structure. Unfortunately this is not the correct :max_id for making a request to the next page. It is instead the :max_id that was sent with the original request. From the example in the Twitter v1.1 documentation:

https://api.twitter.com/1.1/search/tweets.json?q=%23freebandnames&since_id=24012619984051000&max_id=250126199840518145&result_type=mixed&count=4

"search_metadata": {
  "max_id": 250126199840518145,
  "since_id": 24012619984051000,
  "refresh_url": "?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1",
  "next_results": "?max_id=249279667666817023&q=%23freebandnames&count=4&include_entities=1&result_type=mixed",
  "count": 4,
  "completed_in": 0.035,
  "since_id_str": "24012619984051000",
  "query": "%23freebandnames",
  "max_id_str": "250126199840518145"
}

The only way to get the correct :max_id for the next page is to

  1. Find the smallest ID in the current results and subtract 1.
  2. Parse the :max_id from the :next_results string.

Looking again at the documentation example, Twitter is just subtracting 1 from the ID of the last returned tweet. So the 2 methods are equivalent.

I guess the question is do we want a convenience method for getting the :max_id to use in paging through search results? If the answer is yes, how should this convenience method work? Call itself something different, e.g. next_page_id or return aHash of values for the developer as I implemented with next_results.

Both have obvious advantages and disadvantages. Providing a Hash of values with a next_results method mirrors the Twitter search result structure, but is confusing since users have to pick the correct key from the Hash. On the other hand a method next_page_id is simple but now introduces a field that is not in the original returned structure, which can also be confusing.

Perhaps the best solution is to just use Twitter.search(query, opts).results.last.id - 1. Even though verbose it's intention is quite explicit.

@sferik
Copy link
Owner

sferik commented Mar 15, 2013

I need some more time to think about the best thing to do here.

In the mean time, I've merged #366 into master.

@paracycle
Copy link
Collaborator

I recently also felt the need for something like next_results and I had come up with:

def next_page_params(next_page)
  p = URI.decode_www_form next_page[1..-1]
  params = Hash[p]
  params.keys.each do |key|
    params[(key.to_sym rescue key) || key] = params.delete(key)
  end
  q = params.delete(:q)
  return  q, params
end

to feed the next call to Twitter.search. @KentonWhite has a much cleaner solution that I would have used.

I agree with @KentonWhite that we should let the application developer decide if they want to use the parameters that Twitter returns for making a call for the next page, or to come up with other solutions.

@sferik
Copy link
Owner

sferik commented Apr 22, 2013

Okay, I am convinced that this is necessary.

sferik added a commit that referenced this pull request Apr 22, 2013
Add Twitter::SearchResults methods for handling next results
@sferik sferik merged commit c890e7d into sferik:master Apr 22, 2013
@sferik
Copy link
Owner

sferik commented Jun 12, 2013

You may be interested in this recent refactoring: fdd8388

jsonn pushed a commit to jsonn/pkgsrc that referenced this pull request Jan 18, 2014
Changelog (from CHANGELOG.md)

5.5.1
-----
* [Fix bug where `Twitter::Error::AlreadyFavorited` would never be raised](sferik/twitter-ruby#512) ([@polestarw](https://twitter.com/polestarw))
* [Fix bug where `Twitter::Error::AlreadyPosted` would never be raised](sferik/twitter-ruby@e6b37b9)
* [Restore `Twitter::Entities#entities?` as a public method](sferik/twitter-ruby@234a9e3)

5.5.0
-----
* [Add entities to `Twitter::DirectMessage`](sferik/twitter-ruby@d911deb)
* [Add conversion methods to `Twitter::NullObject`](sferik/twitter-ruby@4900fee)

5.4.1
-----
* [Default to maximum number of tweets per request](sferik/twitter-ruby@1e41b5d)

5.4.0
-----
* [Fix enumerable search interface](sferik/twitter-ruby@e14cc33)

5.3.1
-----
* [Add `Twitter::Utils` module](sferik/twitter-ruby@a1f47fb) ([@charliesome](https://twitter.com/charliesome))
* [Remove `Enumerable` monkey patch](sferik/twitter-ruby@818b28d) ([@charliesome](https://twitter.com/charliesome))
* [Don't spawning a new thread if there's only one element](sferik/twitter-ruby@c01ea83)
* [Introduce meaningful constant names](sferik/twitter-ruby@215c808) ([@futuresanta](https://twitter.com/futuresanta))
* [Automatically flatten `Twitter::Arguments`](sferik/twitter-ruby@a556028)

5.3.0
-----
* [Add `UNABLE_TO_VERIFY_CREDENTIALS` error code](sferik/twitter-ruby@6a47e71)
* [Don't suppress `Twitter::Error::Forbidden` in #follow and #follow!](sferik/twitter-ruby@b949c04)
* [Update memoizable dependency to ~> 0.3.1](sferik/twitter-ruby#501)

5.2.0
-----
* [Replace `URI` with `adressable`](sferik/twitter-ruby@7ea2f53)
* [Make `Twitter::Streaming::FriendList` an array](sferik/twitter-ruby@1a38e5e)
* [Add `Twitter::Streaming::DeletedTweet`](sferik/twitter-ruby@084025b)
* [Add `Twitter::Streaming::StallWarning`](sferik/twitter-ruby@b07ac50)
* [Add error code for "User is over daily status update limit"](sferik/twitter-ruby@76c088d)
* [`Twitter::Streaming::Client#site` can take a `String` or `Twitter::User`](sferik/twitter-ruby@e3ad4f2)
* [Update `http_parser.rb` dependency to `~> 0.6.0`](sferik/twitter-ruby@6d2f81b)

5.1.1
-----
* [Custom equalizer for `Twitter::Place`](sferik/twitter-ruby@79c76a9)

5.1.0
-----
* [Use `Addressable::URI` everywhere](sferik/twitter-ruby@97d7c68) ([@matthewrudy](https://twitter.com/matthewrudy))
* [Allow use of `Twitter::Place` instead of `place_id`](sferik/twitter-ruby@c2b31dd)
* [Allow use of `Twitter::Tweet` instead of `in_reply_to_status_id`](sferik/twitter-ruby@6b7d6c2)

5.0.1
-----
* [Fix `buftok` delimiter handling](sferik/twitter-ruby#484)
* [Started handling streaming deletes](sferik/twitter-ruby@8860b97)

5.0.0
-----
* [Remove `Twitter::API::Undocumented#status_activity` and `#statuses_activity`](sferik/twitter-ruby@7f97081)
* [Remove `Twitter::Tweet#favoriters`, `#repliers`, `#repliers_count`, and `#retweeters`](sferik/twitter-ruby@77cc963)
* [Remove identity map](sferik/twitter-ruby@ec7c2df)
* [Remove `Twitter::Cursor#all`](sferik/twitter-ruby@72be414)
* [Remove `Twitter::Cursor#collection`](sferik/twitter-ruby@9ae4621)
* [Remove `Twitter#from_user`](sferik/twitter-ruby@d2ae9f1)
* [Remove `ClientError`, `ServerError`, and `ParserError`](sferik/twitter-ruby@7284394)
* [Remove global configuration](sferik/twitter-ruby@239c5a8)
* [Remove ability to configure client with environment variables](sferik/twitter-ruby@17e9585)
* [Remove Brittish English aliases](sferik/twitter-ruby@572813b)
* [Replace `multi_json` with `json`](sferik/twitter-ruby@e5fc292)
* [Rename `oauth_token` to `access_token`](sferik/twitter-ruby@d360f80)
* [Move `Twitter::Arguments` out of `REST::API` namespace](sferik/twitter-ruby@8faa153)
* [Move `Twitter::Client` into `REST` namespace](sferik/twitter-ruby@5b8c3fd)
* [Add `Twitter::Streaming::Client`](sferik/twitter-ruby@23afe90)
* [Add `Twitter::Error::AlreadyPosted`](sferik/twitter-ruby@e11d2a2)
* [Add `Twitter::REST::Client#reverse_token`](sferik/twitter-ruby@39139c4)
* [Add `#url` methods to `Twitter::List`, `Twitter::Tweet`, and `Twitter::User`](sferik/twitter-ruby@a89ec0f)
* [Add `Twitter::Place#contained_within` and `#contained_within?`](sferik/twitter-ruby@23cc247)
* [Add `Twitter::GeoResults`](sferik/twitter-ruby@be1a0a1)
* [Add `NullObject`](sferik/twitter-ruby@17880f4)
* [Add predicate methods for any possible `NullObject`](sferik/twitter-ruby@eac5522)
* [Always return `URI` instead of `String`](sferik/twitter-ruby@341f68d)
* [Allow `URI` as argument](sferik/twitter-ruby@c207567)
* [Allow `String` in addition to `URI` objects](sferik/twitter-ruby@89a46fb)
* [Collection caching](sferik/twitter-ruby@d484d7d)
* [Implement `Twitter::Cursor#each` without making an extra HTTP request](sferik/twitter-ruby@8eeff57)
* [Make `Twitter::SearchResults` enumerable](sferik/twitter-ruby@d5ce853)
* [Make `Twitter::Base` objects immutable](sferik/twitter-ruby@69b1ef7)
* [Missing key now raises `KeyError`, not `ArgumentError`](sferik/twitter-ruby@f56698c)
* [Use `equalizer` instead of manually overwriting #==](sferik/twitter-ruby@a7ddf71)
* [Give methods more natural names](sferik/twitter-ruby@e593194)
* [Fix `Twitter::SearchResults#rpp` return value](sferik/twitter-ruby@28d7320)

4.8.1
-----
* [Ignore case of profile image extension](sferik/twitter-ruby@7376061)
* [Allow use of Twitter::Token in place of bearer token string](sferik/twitter-ruby@13596bc)
* [Add Twitter::API::Undocumented#tweet_count](sferik/twitter-ruby@795458a)
* [Add missing dependencies](sferik/twitter-ruby@e07e034) ([@tmatilai](https://twitter.com/tmatilai))

4.8.0
-----
* [Add `Twitter::SearchResults#refresh_url`](sferik/twitter-ruby@6bf08c0) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Fix issue with wrong signature being generated when multipart data is posted](sferik/twitter-ruby@65ab90a) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Restore compatibility with Ruby 1.8.7](sferik/twitter-ruby@fb63970)
* [Remove undocumented methods, retired in the APIpocalypse](sferik/twitter-ruby@cf6a91f)

4.7.0
-----
* [Add support for application-only authentication](sferik/twitter-ruby#387) ([@paracycle](https://twitter.com/paracycle))
* [Add support for `Twitter::Entity::Symbol` entities](sferik/twitter-ruby@a14a0cd) ([@anno](https://twitter.com/anno))
* [Add `Twitter::API::OAuth#invalidate_token`](sferik/twitter-ruby#372) ([@terenceponce](https://twitter.com/terenceponce))
* [Add `Twitter::API::Lists#lists_owned` method](sferik/twitter-ruby@9e97b51)
* [Add `Twitter::API::Tweets#retweeters_ids` method](sferik/twitter-ruby@8cf5b2d)
* [Add `Twitter::SearchResults#next_results`](sferik/twitter-ruby#365) ([@KentonWhite](https://twitter.com/KentonWhite))
* [Make consumer_key readable](sferik/twitter-ruby@a318869)
* [Loosen required_rubygems_version for compatibility with Ubuntu 10.04](sferik/twitter-ruby@41bd565)
* [Remove default SSL configuration options and override](sferik/twitter-ruby@113b14b)
jsonn pushed a commit to jsonn/pkgsrc that referenced this pull request Jan 20, 2014
Changelog (from CHANGELOG.md)

5.5.1
-----
* [Fix bug where `Twitter::Error::AlreadyFavorited` would never be raised](sferik/twitter-ruby#512) ([@polestarw](https://twitter.com/polestarw))
* [Fix bug where `Twitter::Error::AlreadyPosted` would never be raised](sferik/twitter-ruby@e6b37b9)
* [Restore `Twitter::Entities#entities?` as a public method](sferik/twitter-ruby@234a9e3)

5.5.0
-----
* [Add entities to `Twitter::DirectMessage`](sferik/twitter-ruby@d911deb)
* [Add conversion methods to `Twitter::NullObject`](sferik/twitter-ruby@4900fee)

5.4.1
-----
* [Default to maximum number of tweets per request](sferik/twitter-ruby@1e41b5d)

5.4.0
-----
* [Fix enumerable search interface](sferik/twitter-ruby@e14cc33)

5.3.1
-----
* [Add `Twitter::Utils` module](sferik/twitter-ruby@a1f47fb) ([@charliesome](https://twitter.com/charliesome))
* [Remove `Enumerable` monkey patch](sferik/twitter-ruby@818b28d) ([@charliesome](https://twitter.com/charliesome))
* [Don't spawning a new thread if there's only one element](sferik/twitter-ruby@c01ea83)
* [Introduce meaningful constant names](sferik/twitter-ruby@215c808) ([@futuresanta](https://twitter.com/futuresanta))
* [Automatically flatten `Twitter::Arguments`](sferik/twitter-ruby@a556028)

5.3.0
-----
* [Add `UNABLE_TO_VERIFY_CREDENTIALS` error code](sferik/twitter-ruby@6a47e71)
* [Don't suppress `Twitter::Error::Forbidden` in #follow and #follow!](sferik/twitter-ruby@b949c04)
* [Update memoizable dependency to ~> 0.3.1](sferik/twitter-ruby#501)

5.2.0
-----
* [Replace `URI` with `adressable`](sferik/twitter-ruby@7ea2f53)
* [Make `Twitter::Streaming::FriendList` an array](sferik/twitter-ruby@1a38e5e)
* [Add `Twitter::Streaming::DeletedTweet`](sferik/twitter-ruby@084025b)
* [Add `Twitter::Streaming::StallWarning`](sferik/twitter-ruby@b07ac50)
* [Add error code for "User is over daily status update limit"](sferik/twitter-ruby@76c088d)
* [`Twitter::Streaming::Client#site` can take a `String` or `Twitter::User`](sferik/twitter-ruby@e3ad4f2)
* [Update `http_parser.rb` dependency to `~> 0.6.0`](sferik/twitter-ruby@6d2f81b)

5.1.1
-----
* [Custom equalizer for `Twitter::Place`](sferik/twitter-ruby@79c76a9)

5.1.0
-----
* [Use `Addressable::URI` everywhere](sferik/twitter-ruby@97d7c68) ([@matthewrudy](https://twitter.com/matthewrudy))
* [Allow use of `Twitter::Place` instead of `place_id`](sferik/twitter-ruby@c2b31dd)
* [Allow use of `Twitter::Tweet` instead of `in_reply_to_status_id`](sferik/twitter-ruby@6b7d6c2)

5.0.1
-----
* [Fix `buftok` delimiter handling](sferik/twitter-ruby#484)
* [Started handling streaming deletes](sferik/twitter-ruby@8860b97)

5.0.0
-----
* [Remove `Twitter::API::Undocumented#status_activity` and `#statuses_activity`](sferik/twitter-ruby@7f97081)
* [Remove `Twitter::Tweet#favoriters`, `#repliers`, `#repliers_count`, and `#retweeters`](sferik/twitter-ruby@77cc963)
* [Remove identity map](sferik/twitter-ruby@ec7c2df)
* [Remove `Twitter::Cursor#all`](sferik/twitter-ruby@72be414)
* [Remove `Twitter::Cursor#collection`](sferik/twitter-ruby@9ae4621)
* [Remove `Twitter#from_user`](sferik/twitter-ruby@d2ae9f1)
* [Remove `ClientError`, `ServerError`, and `ParserError`](sferik/twitter-ruby@7284394)
* [Remove global configuration](sferik/twitter-ruby@239c5a8)
* [Remove ability to configure client with environment variables](sferik/twitter-ruby@17e9585)
* [Remove Brittish English aliases](sferik/twitter-ruby@572813b)
* [Replace `multi_json` with `json`](sferik/twitter-ruby@e5fc292)
* [Rename `oauth_token` to `access_token`](sferik/twitter-ruby@d360f80)
* [Move `Twitter::Arguments` out of `REST::API` namespace](sferik/twitter-ruby@8faa153)
* [Move `Twitter::Client` into `REST` namespace](sferik/twitter-ruby@5b8c3fd)
* [Add `Twitter::Streaming::Client`](sferik/twitter-ruby@23afe90)
* [Add `Twitter::Error::AlreadyPosted`](sferik/twitter-ruby@e11d2a2)
* [Add `Twitter::REST::Client#reverse_token`](sferik/twitter-ruby@39139c4)
* [Add `#url` methods to `Twitter::List`, `Twitter::Tweet`, and `Twitter::User`](sferik/twitter-ruby@a89ec0f)
* [Add `Twitter::Place#contained_within` and `#contained_within?`](sferik/twitter-ruby@23cc247)
* [Add `Twitter::GeoResults`](sferik/twitter-ruby@be1a0a1)
* [Add `NullObject`](sferik/twitter-ruby@17880f4)
* [Add predicate methods for any possible `NullObject`](sferik/twitter-ruby@eac5522)
* [Always return `URI` instead of `String`](sferik/twitter-ruby@341f68d)
* [Allow `URI` as argument](sferik/twitter-ruby@c207567)
* [Allow `String` in addition to `URI` objects](sferik/twitter-ruby@89a46fb)
* [Collection caching](sferik/twitter-ruby@d484d7d)
* [Implement `Twitter::Cursor#each` without making an extra HTTP request](sferik/twitter-ruby@8eeff57)
* [Make `Twitter::SearchResults` enumerable](sferik/twitter-ruby@d5ce853)
* [Make `Twitter::Base` objects immutable](sferik/twitter-ruby@69b1ef7)
* [Missing key now raises `KeyError`, not `ArgumentError`](sferik/twitter-ruby@f56698c)
* [Use `equalizer` instead of manually overwriting #==](sferik/twitter-ruby@a7ddf71)
* [Give methods more natural names](sferik/twitter-ruby@e593194)
* [Fix `Twitter::SearchResults#rpp` return value](sferik/twitter-ruby@28d7320)

4.8.1
-----
* [Ignore case of profile image extension](sferik/twitter-ruby@7376061)
* [Allow use of Twitter::Token in place of bearer token string](sferik/twitter-ruby@13596bc)
* [Add Twitter::API::Undocumented#tweet_count](sferik/twitter-ruby@795458a)
* [Add missing dependencies](sferik/twitter-ruby@e07e034) ([@tmatilai](https://twitter.com/tmatilai))

4.8.0
-----
* [Add `Twitter::SearchResults#refresh_url`](sferik/twitter-ruby@6bf08c0) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Fix issue with wrong signature being generated when multipart data is posted](sferik/twitter-ruby@65ab90a) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Restore compatibility with Ruby 1.8.7](sferik/twitter-ruby@fb63970)
* [Remove undocumented methods, retired in the APIpocalypse](sferik/twitter-ruby@cf6a91f)

4.7.0
-----
* [Add support for application-only authentication](sferik/twitter-ruby#387) ([@paracycle](https://twitter.com/paracycle))
* [Add support for `Twitter::Entity::Symbol` entities](sferik/twitter-ruby@a14a0cd) ([@anno](https://twitter.com/anno))
* [Add `Twitter::API::OAuth#invalidate_token`](sferik/twitter-ruby#372) ([@terenceponce](https://twitter.com/terenceponce))
* [Add `Twitter::API::Lists#lists_owned` method](sferik/twitter-ruby@9e97b51)
* [Add `Twitter::API::Tweets#retweeters_ids` method](sferik/twitter-ruby@8cf5b2d)
* [Add `Twitter::SearchResults#next_results`](sferik/twitter-ruby#365) ([@KentonWhite](https://twitter.com/KentonWhite))
* [Make consumer_key readable](sferik/twitter-ruby@a318869)
* [Loosen required_rubygems_version for compatibility with Ubuntu 10.04](sferik/twitter-ruby@41bd565)
* [Remove default SSL configuration options and override](sferik/twitter-ruby@113b14b)
jsonn pushed a commit to jsonn/pkgsrc that referenced this pull request Mar 12, 2014
Changelog (from CHANGELOG.md)

5.5.1
-----
* [Fix bug where `Twitter::Error::AlreadyFavorited` would never be raised](sferik/twitter-ruby#512) ([@polestarw](https://twitter.com/polestarw))
* [Fix bug where `Twitter::Error::AlreadyPosted` would never be raised](sferik/twitter-ruby@e6b37b9)
* [Restore `Twitter::Entities#entities?` as a public method](sferik/twitter-ruby@234a9e3)

5.5.0
-----
* [Add entities to `Twitter::DirectMessage`](sferik/twitter-ruby@d911deb)
* [Add conversion methods to `Twitter::NullObject`](sferik/twitter-ruby@4900fee)

5.4.1
-----
* [Default to maximum number of tweets per request](sferik/twitter-ruby@1e41b5d)

5.4.0
-----
* [Fix enumerable search interface](sferik/twitter-ruby@e14cc33)

5.3.1
-----
* [Add `Twitter::Utils` module](sferik/twitter-ruby@a1f47fb) ([@charliesome](https://twitter.com/charliesome))
* [Remove `Enumerable` monkey patch](sferik/twitter-ruby@818b28d) ([@charliesome](https://twitter.com/charliesome))
* [Don't spawning a new thread if there's only one element](sferik/twitter-ruby@c01ea83)
* [Introduce meaningful constant names](sferik/twitter-ruby@215c808) ([@futuresanta](https://twitter.com/futuresanta))
* [Automatically flatten `Twitter::Arguments`](sferik/twitter-ruby@a556028)

5.3.0
-----
* [Add `UNABLE_TO_VERIFY_CREDENTIALS` error code](sferik/twitter-ruby@6a47e71)
* [Don't suppress `Twitter::Error::Forbidden` in #follow and #follow!](sferik/twitter-ruby@b949c04)
* [Update memoizable dependency to ~> 0.3.1](sferik/twitter-ruby#501)

5.2.0
-----
* [Replace `URI` with `adressable`](sferik/twitter-ruby@7ea2f53)
* [Make `Twitter::Streaming::FriendList` an array](sferik/twitter-ruby@1a38e5e)
* [Add `Twitter::Streaming::DeletedTweet`](sferik/twitter-ruby@084025b)
* [Add `Twitter::Streaming::StallWarning`](sferik/twitter-ruby@b07ac50)
* [Add error code for "User is over daily status update limit"](sferik/twitter-ruby@76c088d)
* [`Twitter::Streaming::Client#site` can take a `String` or `Twitter::User`](sferik/twitter-ruby@e3ad4f2)
* [Update `http_parser.rb` dependency to `~> 0.6.0`](sferik/twitter-ruby@6d2f81b)

5.1.1
-----
* [Custom equalizer for `Twitter::Place`](sferik/twitter-ruby@79c76a9)

5.1.0
-----
* [Use `Addressable::URI` everywhere](sferik/twitter-ruby@97d7c68) ([@matthewrudy](https://twitter.com/matthewrudy))
* [Allow use of `Twitter::Place` instead of `place_id`](sferik/twitter-ruby@c2b31dd)
* [Allow use of `Twitter::Tweet` instead of `in_reply_to_status_id`](sferik/twitter-ruby@6b7d6c2)

5.0.1
-----
* [Fix `buftok` delimiter handling](sferik/twitter-ruby#484)
* [Started handling streaming deletes](sferik/twitter-ruby@8860b97)

5.0.0
-----
* [Remove `Twitter::API::Undocumented#status_activity` and `#statuses_activity`](sferik/twitter-ruby@7f97081)
* [Remove `Twitter::Tweet#favoriters`, `#repliers`, `#repliers_count`, and `#retweeters`](sferik/twitter-ruby@77cc963)
* [Remove identity map](sferik/twitter-ruby@ec7c2df)
* [Remove `Twitter::Cursor#all`](sferik/twitter-ruby@72be414)
* [Remove `Twitter::Cursor#collection`](sferik/twitter-ruby@9ae4621)
* [Remove `Twitter#from_user`](sferik/twitter-ruby@d2ae9f1)
* [Remove `ClientError`, `ServerError`, and `ParserError`](sferik/twitter-ruby@7284394)
* [Remove global configuration](sferik/twitter-ruby@239c5a8)
* [Remove ability to configure client with environment variables](sferik/twitter-ruby@17e9585)
* [Remove Brittish English aliases](sferik/twitter-ruby@572813b)
* [Replace `multi_json` with `json`](sferik/twitter-ruby@e5fc292)
* [Rename `oauth_token` to `access_token`](sferik/twitter-ruby@d360f80)
* [Move `Twitter::Arguments` out of `REST::API` namespace](sferik/twitter-ruby@8faa153)
* [Move `Twitter::Client` into `REST` namespace](sferik/twitter-ruby@5b8c3fd)
* [Add `Twitter::Streaming::Client`](sferik/twitter-ruby@23afe90)
* [Add `Twitter::Error::AlreadyPosted`](sferik/twitter-ruby@e11d2a2)
* [Add `Twitter::REST::Client#reverse_token`](sferik/twitter-ruby@39139c4)
* [Add `#url` methods to `Twitter::List`, `Twitter::Tweet`, and `Twitter::User`](sferik/twitter-ruby@a89ec0f)
* [Add `Twitter::Place#contained_within` and `#contained_within?`](sferik/twitter-ruby@23cc247)
* [Add `Twitter::GeoResults`](sferik/twitter-ruby@be1a0a1)
* [Add `NullObject`](sferik/twitter-ruby@17880f4)
* [Add predicate methods for any possible `NullObject`](sferik/twitter-ruby@eac5522)
* [Always return `URI` instead of `String`](sferik/twitter-ruby@341f68d)
* [Allow `URI` as argument](sferik/twitter-ruby@c207567)
* [Allow `String` in addition to `URI` objects](sferik/twitter-ruby@89a46fb)
* [Collection caching](sferik/twitter-ruby@d484d7d)
* [Implement `Twitter::Cursor#each` without making an extra HTTP request](sferik/twitter-ruby@8eeff57)
* [Make `Twitter::SearchResults` enumerable](sferik/twitter-ruby@d5ce853)
* [Make `Twitter::Base` objects immutable](sferik/twitter-ruby@69b1ef7)
* [Missing key now raises `KeyError`, not `ArgumentError`](sferik/twitter-ruby@f56698c)
* [Use `equalizer` instead of manually overwriting #==](sferik/twitter-ruby@a7ddf71)
* [Give methods more natural names](sferik/twitter-ruby@e593194)
* [Fix `Twitter::SearchResults#rpp` return value](sferik/twitter-ruby@28d7320)

4.8.1
-----
* [Ignore case of profile image extension](sferik/twitter-ruby@7376061)
* [Allow use of Twitter::Token in place of bearer token string](sferik/twitter-ruby@13596bc)
* [Add Twitter::API::Undocumented#tweet_count](sferik/twitter-ruby@795458a)
* [Add missing dependencies](sferik/twitter-ruby@e07e034) ([@tmatilai](https://twitter.com/tmatilai))

4.8.0
-----
* [Add `Twitter::SearchResults#refresh_url`](sferik/twitter-ruby@6bf08c0) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Fix issue with wrong signature being generated when multipart data is posted](sferik/twitter-ruby@65ab90a) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Restore compatibility with Ruby 1.8.7](sferik/twitter-ruby@fb63970)
* [Remove undocumented methods, retired in the APIpocalypse](sferik/twitter-ruby@cf6a91f)

4.7.0
-----
* [Add support for application-only authentication](sferik/twitter-ruby#387) ([@paracycle](https://twitter.com/paracycle))
* [Add support for `Twitter::Entity::Symbol` entities](sferik/twitter-ruby@a14a0cd) ([@anno](https://twitter.com/anno))
* [Add `Twitter::API::OAuth#invalidate_token`](sferik/twitter-ruby#372) ([@terenceponce](https://twitter.com/terenceponce))
* [Add `Twitter::API::Lists#lists_owned` method](sferik/twitter-ruby@9e97b51)
* [Add `Twitter::API::Tweets#retweeters_ids` method](sferik/twitter-ruby@8cf5b2d)
* [Add `Twitter::SearchResults#next_results`](sferik/twitter-ruby#365) ([@KentonWhite](https://twitter.com/KentonWhite))
* [Make consumer_key readable](sferik/twitter-ruby@a318869)
* [Loosen required_rubygems_version for compatibility with Ubuntu 10.04](sferik/twitter-ruby@41bd565)
* [Remove default SSL configuration options and override](sferik/twitter-ruby@113b14b)
jsonn pushed a commit to jsonn/pkgsrc that referenced this pull request Oct 11, 2014
Changelog (from CHANGELOG.md)

5.5.1
-----
* [Fix bug where `Twitter::Error::AlreadyFavorited` would never be raised](sferik/twitter-ruby#512) ([@polestarw](https://twitter.com/polestarw))
* [Fix bug where `Twitter::Error::AlreadyPosted` would never be raised](sferik/twitter-ruby@e6b37b9)
* [Restore `Twitter::Entities#entities?` as a public method](sferik/twitter-ruby@234a9e3)

5.5.0
-----
* [Add entities to `Twitter::DirectMessage`](sferik/twitter-ruby@d911deb)
* [Add conversion methods to `Twitter::NullObject`](sferik/twitter-ruby@4900fee)

5.4.1
-----
* [Default to maximum number of tweets per request](sferik/twitter-ruby@1e41b5d)

5.4.0
-----
* [Fix enumerable search interface](sferik/twitter-ruby@e14cc33)

5.3.1
-----
* [Add `Twitter::Utils` module](sferik/twitter-ruby@a1f47fb) ([@charliesome](https://twitter.com/charliesome))
* [Remove `Enumerable` monkey patch](sferik/twitter-ruby@818b28d) ([@charliesome](https://twitter.com/charliesome))
* [Don't spawning a new thread if there's only one element](sferik/twitter-ruby@c01ea83)
* [Introduce meaningful constant names](sferik/twitter-ruby@215c808) ([@futuresanta](https://twitter.com/futuresanta))
* [Automatically flatten `Twitter::Arguments`](sferik/twitter-ruby@a556028)

5.3.0
-----
* [Add `UNABLE_TO_VERIFY_CREDENTIALS` error code](sferik/twitter-ruby@6a47e71)
* [Don't suppress `Twitter::Error::Forbidden` in #follow and #follow!](sferik/twitter-ruby@b949c04)
* [Update memoizable dependency to ~> 0.3.1](sferik/twitter-ruby#501)

5.2.0
-----
* [Replace `URI` with `adressable`](sferik/twitter-ruby@7ea2f53)
* [Make `Twitter::Streaming::FriendList` an array](sferik/twitter-ruby@1a38e5e)
* [Add `Twitter::Streaming::DeletedTweet`](sferik/twitter-ruby@084025b)
* [Add `Twitter::Streaming::StallWarning`](sferik/twitter-ruby@b07ac50)
* [Add error code for "User is over daily status update limit"](sferik/twitter-ruby@76c088d)
* [`Twitter::Streaming::Client#site` can take a `String` or `Twitter::User`](sferik/twitter-ruby@e3ad4f2)
* [Update `http_parser.rb` dependency to `~> 0.6.0`](sferik/twitter-ruby@6d2f81b)

5.1.1
-----
* [Custom equalizer for `Twitter::Place`](sferik/twitter-ruby@79c76a9)

5.1.0
-----
* [Use `Addressable::URI` everywhere](sferik/twitter-ruby@97d7c68) ([@matthewrudy](https://twitter.com/matthewrudy))
* [Allow use of `Twitter::Place` instead of `place_id`](sferik/twitter-ruby@c2b31dd)
* [Allow use of `Twitter::Tweet` instead of `in_reply_to_status_id`](sferik/twitter-ruby@6b7d6c2)

5.0.1
-----
* [Fix `buftok` delimiter handling](sferik/twitter-ruby#484)
* [Started handling streaming deletes](sferik/twitter-ruby@8860b97)

5.0.0
-----
* [Remove `Twitter::API::Undocumented#status_activity` and `#statuses_activity`](sferik/twitter-ruby@7f97081)
* [Remove `Twitter::Tweet#favoriters`, `#repliers`, `#repliers_count`, and `#retweeters`](sferik/twitter-ruby@77cc963)
* [Remove identity map](sferik/twitter-ruby@ec7c2df)
* [Remove `Twitter::Cursor#all`](sferik/twitter-ruby@72be414)
* [Remove `Twitter::Cursor#collection`](sferik/twitter-ruby@9ae4621)
* [Remove `Twitter#from_user`](sferik/twitter-ruby@d2ae9f1)
* [Remove `ClientError`, `ServerError`, and `ParserError`](sferik/twitter-ruby@7284394)
* [Remove global configuration](sferik/twitter-ruby@239c5a8)
* [Remove ability to configure client with environment variables](sferik/twitter-ruby@17e9585)
* [Remove Brittish English aliases](sferik/twitter-ruby@572813b)
* [Replace `multi_json` with `json`](sferik/twitter-ruby@e5fc292)
* [Rename `oauth_token` to `access_token`](sferik/twitter-ruby@d360f80)
* [Move `Twitter::Arguments` out of `REST::API` namespace](sferik/twitter-ruby@8faa153)
* [Move `Twitter::Client` into `REST` namespace](sferik/twitter-ruby@5b8c3fd)
* [Add `Twitter::Streaming::Client`](sferik/twitter-ruby@23afe90)
* [Add `Twitter::Error::AlreadyPosted`](sferik/twitter-ruby@e11d2a2)
* [Add `Twitter::REST::Client#reverse_token`](sferik/twitter-ruby@39139c4)
* [Add `#url` methods to `Twitter::List`, `Twitter::Tweet`, and `Twitter::User`](sferik/twitter-ruby@a89ec0f)
* [Add `Twitter::Place#contained_within` and `#contained_within?`](sferik/twitter-ruby@23cc247)
* [Add `Twitter::GeoResults`](sferik/twitter-ruby@be1a0a1)
* [Add `NullObject`](sferik/twitter-ruby@17880f4)
* [Add predicate methods for any possible `NullObject`](sferik/twitter-ruby@eac5522)
* [Always return `URI` instead of `String`](sferik/twitter-ruby@341f68d)
* [Allow `URI` as argument](sferik/twitter-ruby@c207567)
* [Allow `String` in addition to `URI` objects](sferik/twitter-ruby@89a46fb)
* [Collection caching](sferik/twitter-ruby@d484d7d)
* [Implement `Twitter::Cursor#each` without making an extra HTTP request](sferik/twitter-ruby@8eeff57)
* [Make `Twitter::SearchResults` enumerable](sferik/twitter-ruby@d5ce853)
* [Make `Twitter::Base` objects immutable](sferik/twitter-ruby@69b1ef7)
* [Missing key now raises `KeyError`, not `ArgumentError`](sferik/twitter-ruby@f56698c)
* [Use `equalizer` instead of manually overwriting #==](sferik/twitter-ruby@a7ddf71)
* [Give methods more natural names](sferik/twitter-ruby@e593194)
* [Fix `Twitter::SearchResults#rpp` return value](sferik/twitter-ruby@28d7320)

4.8.1
-----
* [Ignore case of profile image extension](sferik/twitter-ruby@7376061)
* [Allow use of Twitter::Token in place of bearer token string](sferik/twitter-ruby@13596bc)
* [Add Twitter::API::Undocumented#tweet_count](sferik/twitter-ruby@795458a)
* [Add missing dependencies](sferik/twitter-ruby@e07e034) ([@tmatilai](https://twitter.com/tmatilai))

4.8.0
-----
* [Add `Twitter::SearchResults#refresh_url`](sferik/twitter-ruby@6bf08c0) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Fix issue with wrong signature being generated when multipart data is posted](sferik/twitter-ruby@65ab90a) ([@mustafaturan](https://twitter.com/mustafaturan))
* [Restore compatibility with Ruby 1.8.7](sferik/twitter-ruby@fb63970)
* [Remove undocumented methods, retired in the APIpocalypse](sferik/twitter-ruby@cf6a91f)

4.7.0
-----
* [Add support for application-only authentication](sferik/twitter-ruby#387) ([@paracycle](https://twitter.com/paracycle))
* [Add support for `Twitter::Entity::Symbol` entities](sferik/twitter-ruby@a14a0cd) ([@anno](https://twitter.com/anno))
* [Add `Twitter::API::OAuth#invalidate_token`](sferik/twitter-ruby#372) ([@terenceponce](https://twitter.com/terenceponce))
* [Add `Twitter::API::Lists#lists_owned` method](sferik/twitter-ruby@9e97b51)
* [Add `Twitter::API::Tweets#retweeters_ids` method](sferik/twitter-ruby@8cf5b2d)
* [Add `Twitter::SearchResults#next_results`](sferik/twitter-ruby#365) ([@KentonWhite](https://twitter.com/KentonWhite))
* [Make consumer_key readable](sferik/twitter-ruby@a318869)
* [Loosen required_rubygems_version for compatibility with Ubuntu 10.04](sferik/twitter-ruby@41bd565)
* [Remove default SSL configuration options and override](sferik/twitter-ruby@113b14b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants