Skip to content

Commit

Permalink
Replace Faraday with the HTTP gem
Browse files Browse the repository at this point in the history
  • Loading branch information
sferik committed Nov 7, 2014
1 parent 9d70f58 commit 675c01a
Show file tree
Hide file tree
Showing 21 changed files with 95 additions and 385 deletions.
22 changes: 4 additions & 18 deletions etc/erd.dot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ digraph classes {
ArgumentError [label="ArgumentError"]
Array [label="Array"]
Exception [label="Exception"]
Faraday__Middleware [label="Faraday::Middleware"]
Faraday__Response__Middleware [label="Faraday::Response::Middleware"]
Naught__BasicObject [label="Naught::BasicObject"]
StandardError [label="StandardError"]
Twitter__Arguments [label="Twitter::Arguments"]
Expand All @@ -23,21 +21,20 @@ digraph classes {
Twitter__Entity__UserMention [label="Twitter::Entity::UserMention"]
Twitter__Error [label="Twitter::Error"]
Twitter__Error__AlreadyFavorited [label="Twitter::Error::AlreadyFavorited"]
Twitter__Error__AlreadyPosted [label="Twitter::Error::AlreadyPosted"]
Twitter__Error__AlreadyRetweeted [label="Twitter::Error::AlreadyRetweeted"]
Twitter__Error__BadGateway [label="Twitter::Error::BadGateway"]
Twitter__Error__BadRequest [label="Twitter::Error::BadRequest"]
Twitter__Error__ClientError [label="Twitter::Error::ClientError"]
Twitter__Error__ConfigurationError [label="Twitter::Error::ConfigurationError"]
Twitter__Error__EnhanceYourCalm [label="Twitter::Error::EnhanceYourCalm"]
Twitter__Error__DuplicateStatus [label="Twitter::Error::DuplicateStatus"]
Twitter__Error__Forbidden [label="Twitter::Error::Forbidden"]
Twitter__Error__GatewayTimeout [label="Twitter::Error::GatewayTimeout"]
Twitter__Error__InternalServerError [label="Twitter::Error::InternalServerError"]
Twitter__Error__NotAcceptable [label="Twitter::Error::NotAcceptable"]
Twitter__Error__NotFound [label="Twitter::Error::NotFound"]
Twitter__Error__RequestTimeout [label="Twitter::Error::RequestTimeout"]
Twitter__Error__ServerError [label="Twitter::Error::ServerError"]
Twitter__Error__ServiceUnavailable [label="Twitter::Error::ServiceUnavailable"]
Twitter__Error__TooManyRequests [label="Twitter::Error::TooManyRequests"]
Twitter__Error__UnacceptableIO [label="Twitter::Error::UnacceptableIO"]
Twitter__Error__Unauthorized [label="Twitter::Error::Unauthorized"]
Twitter__Error__UnprocessableEntity [label="Twitter::Error::UnprocessableEntity"]
Expand All @@ -60,10 +57,6 @@ digraph classes {
Twitter__ProfileBanner [label="Twitter::ProfileBanner"]
Twitter__REST__Client [label="Twitter::REST::Client"]
Twitter__REST__Request [label="Twitter::REST::Request"]
Twitter__REST__Request__MultipartWithFile [label="Twitter::REST::Request::MultipartWithFile"]
Twitter__REST__Response__ParseErrorJson [label="Twitter::REST::Response::ParseErrorJson"]
Twitter__REST__Response__ParseJson [label="Twitter::REST::Response::ParseJson"]
Twitter__REST__Response__RaiseError [label="Twitter::REST::Response::RaiseError"]
Twitter__RateLimit [label="Twitter::RateLimit"]
Twitter__Relationship [label="Twitter::Relationship"]
Twitter__SavedSearch [label="Twitter::SavedSearch"]
Expand All @@ -90,8 +83,6 @@ digraph classes {
ArgumentError -> StandardError
Array -> Object
Exception -> Object
Faraday__Middleware -> Object
Faraday__Response__Middleware -> Faraday__Middleware
Naught__BasicObject -> Object
StandardError -> Exception
Twitter__Arguments -> Array
Expand All @@ -108,21 +99,20 @@ digraph classes {
Twitter__Entity__UserMention -> Twitter__Entity
Twitter__Error -> StandardError
Twitter__Error__AlreadyFavorited -> Twitter__Error__Forbidden
Twitter__Error__AlreadyPosted -> Twitter__Error__Forbidden
Twitter__Error__AlreadyRetweeted -> Twitter__Error__Forbidden
Twitter__Error__BadGateway -> Twitter__Error__ServerError
Twitter__Error__BadRequest -> Twitter__Error__ClientError
Twitter__Error__ClientError -> Twitter__Error
Twitter__Error__ConfigurationError -> ArgumentError
Twitter__Error__EnhanceYourCalm -> Twitter__Error__ClientError
Twitter__Error__DuplicateStatus -> Twitter__Error__Forbidden
Twitter__Error__Forbidden -> Twitter__Error__ClientError
Twitter__Error__GatewayTimeout -> Twitter__Error__ServerError
Twitter__Error__InternalServerError -> Twitter__Error__ServerError
Twitter__Error__NotAcceptable -> Twitter__Error__ClientError
Twitter__Error__NotFound -> Twitter__Error__ClientError
Twitter__Error__RequestTimeout -> Twitter__Error__ClientError
Twitter__Error__ServerError -> Twitter__Error
Twitter__Error__ServiceUnavailable -> Twitter__Error__ServerError
Twitter__Error__TooManyRequests -> Twitter__Error__ClientError
Twitter__Error__UnacceptableIO -> StandardError
Twitter__Error__Unauthorized -> Twitter__Error__ClientError
Twitter__Error__UnprocessableEntity -> Twitter__Error__ClientError
Expand All @@ -145,10 +135,6 @@ digraph classes {
Twitter__ProfileBanner -> Twitter__Base
Twitter__REST__Client -> Twitter__Client
Twitter__REST__Request -> Object
Twitter__REST__Request__MultipartWithFile -> Faraday__Middleware
Twitter__REST__Response__ParseErrorJson -> Twitter__REST__Response__ParseJson
Twitter__REST__Response__ParseJson -> Faraday__Response__Middleware
Twitter__REST__Response__RaiseError -> Faraday__Response__Middleware
Twitter__RateLimit -> Twitter__Base
Twitter__Relationship -> Twitter__Base
Twitter__SavedSearch -> Twitter__Identity
Expand Down
Binary file modified etc/erd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 7 additions & 8 deletions lib/twitter/error.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'twitter/rate_limit'
require 'twitter/utils'

module Twitter
# Custom error class for rescuing from all Twitter errors
Expand Down Expand Up @@ -34,13 +35,15 @@ module Code
Codes = Code # rubocop:disable ConstantName

class << self
include Twitter::Utils

# Create a new error from an HTTP response
#
# @param response [Faraday::Response]
# @param response [HTTP::Response]
# @return [Twitter::Error]
def from_response(response)
message, code = parse_error(response.body)
new(message, response.response_headers, code)
message, code = parse_error(symbolize_keys!(response.parse))
new(message, response.headers, code)
end

# @return [Hash]
Expand All @@ -51,7 +54,6 @@ def errors
403 => Twitter::Error::Forbidden,
404 => Twitter::Error::NotFound,
406 => Twitter::Error::NotAcceptable,
408 => Twitter::Error::RequestTimeout,
420 => Twitter::Error::EnhanceYourCalm,
422 => Twitter::Error::UnprocessableEntity,
429 => Twitter::Error::TooManyRequests,
Expand All @@ -73,7 +75,7 @@ def forbidden_messages
private

def parse_error(body)
if body.nil?
if body.nil? || body.empty?
['', nil]
elsif body[:error]
[body[:error], nil]
Expand Down Expand Up @@ -141,9 +143,6 @@ def initialize
# Raised when Twitter returns the HTTP status code 406
NotAcceptable = Class.new(ClientError)

# Raised when Twitter returns the HTTP status code 408
RequestTimeout = Class.new(ClientError)

# Raised when Twitter returns the HTTP status code 422
UnprocessableEntity = Class.new(ClientError)

Expand Down
2 changes: 1 addition & 1 deletion lib/twitter/headers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ def oauth_auth_header
def request_headers
bearer_token_request = @options.delete(:bearer_token_request)
headers = {}
headers[:user_agent] = @client.user_agent
if bearer_token_request
headers[:accept] = '*/*'
headers[:authorization] = bearer_token_credentials_auth_header
headers[:content_type] = 'application/x-www-form-urlencoded; charset=UTF-8'
else
headers[:authorization] = auth_header
end
Expand Down
76 changes: 0 additions & 76 deletions lib/twitter/rest/client.rb
Original file line number Diff line number Diff line change
@@ -1,83 +1,14 @@
require 'faraday'
require 'faraday/request/multipart'
require 'twitter/client'
require 'twitter/rest/api'
require 'twitter/rest/request'
require 'twitter/rest/request/multipart_with_file'
require 'twitter/rest/response/parse_json'
require 'twitter/rest/response/raise_error'
require 'twitter/rest/utils'

module Twitter
module REST
class Client < Twitter::Client
include Twitter::REST::API
BASE_URL = 'https://api.twitter.com'
attr_accessor :bearer_token

# @param connection_options [Hash]
# @return [Hash]
def connection_options=(connection_options)
warn "#{Kernel.caller.first}: [DEPRECATION] #{self.class.name}##{__method__} is deprecated and will be removed."
@connection_options = connection_options
end

# @return [Hash]
def connection_options
@connection_options ||= {
:builder => middleware,
:headers => {
:accept => 'application/json',
:user_agent => user_agent,
},
:request => {
:open_timeout => 10,
:timeout => 30,
},
:proxy => proxy,
}
end

# @params middleware [Faraday::RackBuilder]
# @return [Faraday::RackBuilder]
def middleware=(middleware)
warn "#{Kernel.caller.first}: [DEPRECATION] #{self.class.name}##{__method__} is deprecated and will be removed."
@middleware = middleware
end

# @note Faraday's middleware stack implementation is comparable to that of Rack middleware. The order of middleware is important: the first middleware on the list wraps all others, while the last middleware is the innermost one.
# @see https://github.com/technoweenie/faraday#advanced-middleware-usage
# @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
# @return [Faraday::RackBuilder]
def middleware
@middleware ||= Faraday::RackBuilder.new do |faraday|
# Convert file uploads to Faraday::UploadIO objects
faraday.request :twitter_multipart_with_file
# Checks for files in the payload, otherwise leaves everything untouched
faraday.request :multipart
# Encodes as "application/x-www-form-urlencoded" if not already encoded
faraday.request :url_encoded
# Handle error responses
faraday.response :twitter_raise_error
# Parse JSON response bodies
faraday.response :twitter_parse_json
# Set default HTTP adapter
faraday.adapter :net_http
end
end

# Perform an HTTP GET request
def get(path, options = {})
warn "#{Kernel.caller.first}: [DEPRECATION] #{self.class.name}##{__method__} is deprecated. Use Twitter::REST::Request#perform instead."
perform_get(path, options)
end

# Perform an HTTP POST request
def post(path, options = {})
warn "#{Kernel.caller.first}: [DEPRECATION] #{self.class.name}##{__method__} is deprecated. Use Twitter::REST::Request#perform instead."
perform_post(path, options)
end

# @return [Boolean]
def bearer_token?
!!bearer_token
Expand All @@ -87,13 +18,6 @@ def bearer_token?
def credentials?
super || bearer_token?
end

# Returns a Faraday::Connection object
#
# @return [Faraday::Connection]
def connection
@connection ||= Faraday.new(BASE_URL, connection_options)
end
end
end
end
10 changes: 3 additions & 7 deletions lib/twitter/rest/media.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,9 @@ module Media
# @param options [Hash] A customizable set of options.
def upload(media, options = {})
fail(Twitter::Error::UnacceptableIO.new) unless media.respond_to?(:to_io)
base_url = 'https://upload.twitter.com'
path = '/1.1/media/upload.json'
conn = connection.dup
conn.url_prefix = base_url
headers = Twitter::Headers.new(self, :post, base_url + path, options).request_headers
options.merge!(:media => media)
conn.post(path, options) { |request| request.headers.update(headers) }.env.body[:media_id]
url = 'https://upload.twitter.com/1.1/media/upload.json'
headers = Twitter::Headers.new(self, :post, url, options).request_headers
HTTP.with(headers).post(url, :form => options).parse['media_id']
end
end
end
Expand Down
17 changes: 9 additions & 8 deletions lib/twitter/rest/oauth.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
require 'twitter/headers'
require 'twitter/rest/utils'
require 'twitter/rest/response/parse_error_json'
require 'twitter/token'
require 'twitter/utils'

module Twitter
module REST
module OAuth
include Twitter::REST::Utils
include Twitter::Utils

# Allows a registered application to obtain an OAuth 2 Bearer Token, which can be used to make API requests
# on an application's own behalf, without a user context.
Expand All @@ -26,7 +27,9 @@ module OAuth
def token(options = {})
options[:bearer_token_request] = true
options[:grant_type] ||= 'client_credentials'
perform_post_with_object('/oauth2/token', options, Twitter::Token)
headers = Twitter::Headers.new(self, :post, 'https://api.twitter.com/oauth2/token', options).request_headers
response = HTTP.with(headers).post('https://api.twitter.com/oauth2/token', :form => options)
Twitter::Token.new(symbolize_keys!(response.parse))
end
alias_method :bearer_token, :token

Expand All @@ -53,12 +56,10 @@ def invalidate_token(access_token, options = {})
# @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
# @return [String] The token string.
def reverse_token
conn = connection.dup
conn.builder.swap(4, Twitter::REST::Response::ParseErrorJson)
response = conn.post('/oauth/request_token?x_auth_mode=reverse_auth') do |request|
request.headers[:authorization] = Twitter::Headers.new(self, :post, 'https://api.twitter.com/oauth/request_token', :x_auth_mode => 'reverse_auth').oauth_auth_header.to_s
end
response.body
options = {:x_auth_mode => 'reverse_auth'}
url = 'https://api.twitter.com/oauth/request_token'
auth_header = Twitter::Headers.new(self, :post, url, options).oauth_auth_header.to_s
HTTP.with(:authorization => auth_header).post(url, :params => options).to_s
end
end
end
Expand Down
43 changes: 31 additions & 12 deletions lib/twitter/rest/request.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
require 'addressable/uri'
require 'faraday'
require 'json'
require 'timeout'
require 'http'
require 'twitter/error'
require 'twitter/headers'
require 'twitter/rate_limit'
require 'twitter/utils'

module Twitter
module REST
class Request
include Twitter::Utils
BASE_URL = 'https://api.twitter.com'
attr_accessor :client, :headers, :options, :rate_limit, :request_method,
:path, :uri
alias_method :verb, :request_method
Expand All @@ -22,22 +23,40 @@ def initialize(client, request_method, path, options = {})
@client = client
@request_method = request_method.to_sym
@path = path
@uri = Addressable::URI.parse(client.connection.url_prefix + path)
@uri = Addressable::URI.parse(BASE_URL + path)
@options = options
end

# @return [Array, Hash]
def perform
@headers = Twitter::Headers.new(@client, @request_method, @uri.to_s, @options).request_headers
begin
response = @client.connection.send(@request_method, @path, @options) { |request| request.headers.update(@headers) }.env
rescue Faraday::Error::TimeoutError, Timeout::Error => error
raise(Twitter::Error::RequestTimeout.new(error))
rescue Faraday::Error::ClientError, JSON::ParserError => error
raise(Twitter::Error.new(error))
options_key = @request_method == :get ? :params : :form
response = HTTP.with(@headers).send(@request_method, @uri.to_s, options_key => @options)
error = error(response)
fail(error) if error
@rate_limit = Twitter::RateLimit.new(response.headers)
symbolize_keys!(response.parse)
end

private

def error(response)
klass = Twitter::Error.errors[response.code]
if klass == Twitter::Error::Forbidden
forbidden_error(response)
elsif !klass.nil?
klass.from_response(response)
end
end

def forbidden_error(response)
error = Twitter::Error::Forbidden.from_response(response)
klass = Twitter::Error.forbidden_messages[error.message]
if klass
klass.from_response(response)
else
error
end
@rate_limit = Twitter::RateLimit.new(response.response_headers)
response.body
end
end
end
Expand Down
Loading

0 comments on commit 675c01a

Please sign in to comment.