Skip to content

Commit

Permalink
Add Twitter::RateLimit class
Browse files Browse the repository at this point in the history
Progress toward #268.
  • Loading branch information
sferik committed Jun 24, 2012
1 parent 2b37b6a commit 4c63a73
Show file tree
Hide file tree
Showing 45 changed files with 598 additions and 481 deletions.
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ wiki][apps]!
[apps]: https://github.com/jnunemaker/twitter/wiki/apps

## What's new in version 3?

### Methods

The following methods now accept multiple users or ids as arguments and return
arrays:

Expand All @@ -57,6 +55,8 @@ better performance than calling these methods multiple times in serial.
The `Twitter::Client#direct_messages` method has been renamed to
`Twitter::Client#direct_messages_received`.

The `Twitter::Client#profile_image` method has been removed.

Additionally, the `Twitter::Client#follow` method now checks to make sure the
user isn't already being followed. If you don't wish to perform that check
(which does require an extra HTTP request), you can use the new
Expand All @@ -78,11 +78,7 @@ object.
The `Twitter::Status#expanded_urls` method has been removed. Use
`Twitter::Status#urls` instead.

Support for API gateways via `gateway` configuration has been also removed.
This functionality may be replicated by inserting custom Faraday middleware.

### Configuration

The Faraday middleware stack is now fully configurable and is exposed as a
`Faraday::Builder` which can be manipulated in place:

Expand All @@ -107,14 +103,16 @@ The adapter can also be configured as part of the middleware stack:
builder.adapter :some_other_adapter
})

Support for API gateways via `gateway` configuration has removed. This
functionality may be replicated by inserting custom Faraday middleware.

The `proxy` and `user_agent` configuration have also been removed. These can be
set via the `connection_options` configuration.

Twitter.connection_options[:proxy] = 'http://erik:[email protected]:8080'
Twitter.connection_options[:headers][:user_agent] = 'Custom User Agent'

### Authentication

This library now attempts to pull credentials from `ENV` if they are not
otherwise specified. In `bash`:

Expand All @@ -124,7 +122,6 @@ otherwise specified. In `bash`:
export TWITTER_OAUTH_TOKEN_SECRET=YOUR_OAUTH_TOKEN_SECRET

### Identity Map

This version introduces an identity map, which ensures that the same objects
only get initialized once:

Expand All @@ -134,7 +131,6 @@ only get initialized once:
false.)

### Errors

Any Faraday client errors are captured and re-raised as a
`Twitter::Error::ClientError`, so there's no longer a need to separately rescue
`Faraday::Error::ClientError`.
Expand All @@ -144,8 +140,18 @@ API requests are made via api.twitter.com, which does not return HTTP 420. When
you hit your rate limit, Twitter returns HTTP 400, which raises a
`Twitter::Error::BadRequest`.

### Additional notes
All `Twitter::Error.ratelimit` methods (including `Twitter::Error.retry_at`)
have been replaced by the `Twitter::RateLimit` singleton class. After making
any request, you can check the `Twitter::RateLimit` object for your current
rate limit status.

rate_limit = Twitter::RateLimit.instance
rate_limit.limit #=> 150
rate_limit.remaining #=> 149
rate_limit.reset_at #=> 2012-06-23 20:04:36 -0700
rate_limit.reset_in #=> 3540 (seconds)

### Additional notes
This will be the last major version of this library to support Ruby 1.8.
Requiring Ruby 1.9 will allow us to remove [various][each_with_object]
[hacks][singleton_class] put in place to maintain Ruby 1.8 compatibility.
Expand Down
2 changes: 1 addition & 1 deletion lib/twitter/action/favorite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Favorite < Twitter::Action::Status
# @return [Array<Twitter::Status>]
def targets
@targets = Array(@attrs['targets']).map do |status|
Twitter::Status.get_or_new(status)
Twitter::Status.fetch_or_new(status)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/twitter/action/follow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Follow < Twitter::Base
# @return [Array<Twitter::User>]
def sources
@sources = Array(@attrs['sources']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand All @@ -22,7 +22,7 @@ def sources
# @return [Array<Twitter::User>]
def targets
@targets = Array(@attrs['targets']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/twitter/action/list_member_added.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ListMemberAdded < Twitter::Base
# @return [Array<Twitter::User>]
def sources
@sources = Array(@attrs['sources']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand All @@ -23,7 +23,7 @@ def sources
# @return [Array<Twitter::List>]
def target_objects
@target_objects = Array(@attrs['target_objects']).map do |list|
Twitter::List.get_or_new(list)
Twitter::List.fetch_or_new(list)
end
end

Expand All @@ -32,7 +32,7 @@ def target_objects
# @return [Array<Twitter::User>]
def targets
@targets = Array(@attrs['targets']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/twitter/action/mention.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Mention < Twitter::Base
# @return [Array<Twitter::User>]
def sources
@sources = Array(@attrs['sources']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand All @@ -30,7 +30,7 @@ def source
# @return [Array<Twitter::Status>]
def target_objects
@target_objects = Array(@attrs['target_objects']).map do |status|
Twitter::Status.get_or_new(status)
Twitter::Status.fetch_or_new(status)
end
end

Expand All @@ -39,7 +39,7 @@ def target_objects
# @return [Array<Twitter::User>]
def targets
@targets = Array(@attrs['targets']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/twitter/action/reply.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Reply < Twitter::Action::Status
# @return [Array<Twitter::Status>]
def target_objects
@target_objects = Array(@attrs['target_objects']).map do |status|
Twitter::Status.get_or_new(status)
Twitter::Status.fetch_or_new(status)
end
end

Expand All @@ -18,7 +18,7 @@ def target_objects
# @return [Array<Twitter::Status>]
def targets
@targets = Array(@attrs['targets']).map do |status|
Twitter::Status.get_or_new(status)
Twitter::Status.fetch_or_new(status)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/twitter/action/retweet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Retweet < Twitter::Action::Status
# @return [Array<Twitter::Status>]
def target_objects
@target_objects = Array(@attrs['target_objects']).map do |status|
Twitter::Status.get_or_new(status)
Twitter::Status.fetch_or_new(status)
end
end

Expand All @@ -18,7 +18,7 @@ def target_objects
# @return [Array<Twitter::User>]
def targets
@targets = Array(@attrs['targets']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/twitter/action/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Status < Twitter::Base
# @return [Array<Twitter::User>]
def sources
@sources = Array(@attrs['sources']).map do |user|
Twitter::User.get_or_new(user)
Twitter::User.fetch_or_new(user)
end
end

Expand Down
7 changes: 3 additions & 4 deletions lib/twitter/action_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ class ActionFactory
# @param attrs [Hash]
# @raise [ArgumentError] Error raised when supplied argument is missing an 'action' key.
# @return [Twitter::Action::Favorite, Twitter::Action::Follow, Twitter::Action::ListMemberAdded, Twitter::Action::Mention, Twitter::Action::Reply, Twitter::Action::Retweet]
def self.new(action={})
type = action.delete('action')
if type
Twitter::Action.const_get(camelize(type).to_sym).get_or_new(action)
def self.new(attrs={})
if type = attrs.delete('action')
Twitter::Action.const_get(camelize(type).to_sym).fetch_or_new(attrs)
else
raise ArgumentError, "argument must have an 'action' key"
end
Expand Down
30 changes: 23 additions & 7 deletions lib/twitter/base.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require 'twitter/identity_map'
require 'twitter/rate_limit'

module Twitter
class Base
attr_accessor :attrs
attr_reader :attrs
alias body attrs
alias to_hash attrs

@@identity_map = IdentityMap.new
Expand All @@ -23,21 +25,27 @@ def self.attr_reader(*attrs)
end
end

def self.get(attrs={})
def self.fetch(attrs)
@@identity_map[self] ||= {}
@@identity_map[self][Marshal.dump(attrs)]
end

def self.get_or_new(attrs={})
self.get(attrs) || self.new(attrs)
def self.from_response(response={})
self.fetch(response[:body]) || self.new(response[:body], response[:response_headers])
end

def self.fetch_or_new(attrs={})
self.fetch(attrs) || self.new(attrs)
end

# Initializes a new object
#
# @param attrs [Hash]
# @param response_headers [Hash]
# @return [Twitter::Base]
def initialize(attrs={})
def initialize(attrs={}, response_headers={})
self.update(attrs)
self.update_rate_limit(response_headers) unless response_headers.empty?
@@identity_map[self.class] ||= {}
@@identity_map[self.class][Marshal.dump(attrs)] = self
end
Expand All @@ -46,7 +54,7 @@ def initialize(attrs={})
#
# @param method [String, Symbol] Message to send to the object
def [](method)
self.__send__(method.to_sym)
self.send(method.to_sym)
rescue NoMethodError
nil
end
Expand All @@ -57,9 +65,17 @@ def [](method)
# @return [Twitter::Base]
def update(attrs)
@attrs ||= {}
@attrs.merge!(attrs)
@attrs.update(attrs)
self
end

# Update the RateLimit object
#
# @param response_headers [Hash]
# @return [Twitter::RateLimit]
def update_rate_limit(response_headers)
Twitter::RateLimit.instance.update(response_headers)
end

end
end
Loading

0 comments on commit 4c63a73

Please sign in to comment.