faraday 0.5.7 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +9 -8
  2. data/README.md +99 -20
  3. data/faraday.gemspec +9 -11
  4. data/lib/faraday.rb +1 -1
  5. data/lib/faraday/adapter.rb +11 -86
  6. data/lib/faraday/adapter/action_dispatch.rb +3 -12
  7. data/lib/faraday/adapter/em_synchrony.rb +9 -24
  8. data/lib/faraday/adapter/excon.rb +7 -19
  9. data/lib/faraday/adapter/net_http.rb +37 -35
  10. data/lib/faraday/adapter/patron.rb +16 -23
  11. data/lib/faraday/adapter/test.rb +4 -10
  12. data/lib/faraday/adapter/typhoeus.rb +11 -39
  13. data/lib/faraday/builder.rb +82 -33
  14. data/lib/faraday/connection.rb +3 -10
  15. data/lib/faraday/error.rb +20 -15
  16. data/lib/faraday/middleware.rb +7 -2
  17. data/lib/faraday/request.rb +13 -10
  18. data/lib/faraday/request/json.rb +31 -0
  19. data/lib/faraday/request/multipart.rb +63 -0
  20. data/lib/faraday/request/url_encoded.rb +37 -0
  21. data/lib/faraday/response.rb +72 -30
  22. data/lib/faraday/response/logger.rb +34 -0
  23. data/lib/faraday/response/raise_error.rb +16 -0
  24. data/lib/faraday/upload_io.rb +14 -6
  25. data/lib/faraday/utils.rb +54 -17
  26. data/test/adapters/live_test.rb +36 -14
  27. data/test/adapters/logger_test.rb +1 -1
  28. data/test/adapters/net_http_test.rb +33 -0
  29. data/test/connection_test.rb +0 -39
  30. data/test/env_test.rb +84 -6
  31. data/test/helper.rb +17 -8
  32. data/test/live_server.rb +19 -17
  33. data/test/middleware_stack_test.rb +91 -0
  34. data/test/request_middleware_test.rb +75 -21
  35. data/test/response_middleware_test.rb +34 -31
  36. metadata +21 -17
  37. data/lib/faraday/adapter/logger.rb +0 -32
  38. data/lib/faraday/request/active_support_json.rb +0 -21
  39. data/lib/faraday/request/yajl.rb +0 -18
  40. data/lib/faraday/response/active_support_json.rb +0 -30
  41. data/lib/faraday/response/yajl.rb +0 -26
  42. data/test/adapters/typhoeus_test.rb +0 -31
  43. data/test/connection_app_test.rb +0 -60
  44. data/test/form_post_test.rb +0 -58
  45. data/test/multipart_test.rb +0 -48
data/lib/faraday/error.rb CHANGED
@@ -1,28 +1,33 @@
1
1
  module Faraday
2
2
  module Error
3
3
  class ClientError < StandardError
4
- def initialize(exception)
5
- @inner_exception = exception
6
- end
4
+ attr_reader :response
7
5
 
8
- def message
9
- @inner_exception.respond_to?(:message) ?
10
- @inner_exception.message :
11
- @inner_exception.to_s
12
- end
6
+ def initialize(ex)
7
+ @wrapped_exception = nil
8
+ @response = nil
13
9
 
14
- def backtrace
15
- @inner_exception.backtrace
10
+ if ex.respond_to?(:backtrace)
11
+ super(ex.message)
12
+ @wrapped_exception = ex
13
+ elsif ex.respond_to?(:each_key)
14
+ super("the server responded with status #{ex[:status]}")
15
+ @response = ex
16
+ else
17
+ super(ex.to_s)
18
+ end
16
19
  end
17
20
 
18
- alias to_str message
19
-
20
- def to_s
21
- @inner_exception.to_s
21
+ def backtrace
22
+ if @wrapped_exception
23
+ @wrapped_exception.backtrace
24
+ else
25
+ super
26
+ end
22
27
  end
23
28
 
24
29
  def inspect
25
- @inner_exception.inspect
30
+ %(#<#{self.class}>)
26
31
  end
27
32
  end
28
33
 
@@ -1,7 +1,5 @@
1
1
  module Faraday
2
2
  class Middleware
3
- include Rack::Utils
4
-
5
3
  class << self
6
4
  attr_accessor :load_error, :supports_parallel_requests
7
5
  alias supports_parallel_requests? supports_parallel_requests
@@ -13,6 +11,13 @@ module Faraday
13
11
  end
14
12
  end
15
13
 
14
+ # Executes a block which should try to require and reference dependent libraries
15
+ def self.dependency(lib = nil)
16
+ lib ? require(lib) : yield
17
+ rescue LoadError, NameError => error
18
+ self.load_error = error
19
+ end
20
+
16
21
  def self.loaded?
17
22
  @load_error.nil?
18
23
  end
@@ -11,15 +11,16 @@ module Faraday
11
11
  #
12
12
  class Request < Struct.new(:path, :params, :headers, :body)
13
13
  extend AutoloadHelper
14
+
14
15
  autoload_all 'faraday/request',
15
- :Yajl => 'yajl',
16
- :ActiveSupportJson => 'active_support_json'
16
+ :JSON => 'json',
17
+ :UrlEncoded => 'url_encoded',
18
+ :Multipart => 'multipart'
17
19
 
18
20
  register_lookup_modules \
19
- :yajl => :Yajl,
20
- :activesupport_json => :ActiveSupportJson,
21
- :rails_json => :ActiveSupportJson,
22
- :active_support_json => :ActiveSupportJson
21
+ :json => :JSON,
22
+ :url_encoded => :UrlEncoded,
23
+ :multipart => :Multipart
23
24
 
24
25
  def self.run(connection, request_method)
25
26
  req = create
@@ -54,7 +55,6 @@ module Faraday
54
55
  # :request_headers - hash of HTTP Headers to be sent to the server
55
56
  # :response_headers - Hash of HTTP headers from the server
56
57
  # :parallel_manager - sent if the connection is in parallel mode
57
- # :response - the actual response object that stores the rack response
58
58
  # :request - Hash of options for configuring the request.
59
59
  # :timeout - open/read timeout Integer in seconds
60
60
  # :open_timeout - read timeout Integer in seconds
@@ -74,15 +74,18 @@ module Faraday
74
74
  :url => connection.build_url(path, env_params),
75
75
  :request_headers => env_headers,
76
76
  :parallel_manager => connection.parallel_manager,
77
- :response => Response.new,
78
77
  :request => connection.options.merge(:proxy => connection.proxy),
79
78
  :ssl => connection.ssl}
80
79
  end
81
80
 
82
81
  def run(connection, request_method)
83
- app = connection.to_app
82
+ app = lambda { |env|
83
+ response = Response.new
84
+ response.finish(env) unless env[:parallel_manager]
85
+ env[:response] = response
86
+ }
84
87
  env = to_env_hash(connection, request_method)
85
- app.call(env)
88
+ connection.builder.to_app(app).call(env)
86
89
  end
87
90
  end
88
91
  end
@@ -0,0 +1,31 @@
1
+ module Faraday
2
+ class Request::JSON < Request::UrlEncoded
3
+ self.mime_type = 'application/json'.freeze
4
+
5
+ class << self
6
+ attr_accessor :adapter
7
+ end
8
+
9
+ # loads the JSON encoder either from yajl-ruby or activesupport
10
+ dependency do
11
+ begin
12
+ require 'yajl'
13
+ self.adapter = Yajl::Encoder
14
+ rescue LoadError, NameError
15
+ require 'active_support/core_ext/module/attribute_accessors' # AS 2.3.11
16
+ require 'active_support/core_ext/kernel/reporting' # AS 2.3.11
17
+ require 'active_support/json/encoding'
18
+ require 'active_support/ordered_hash' # AS 3.0.4
19
+ self.adapter = ActiveSupport::JSON
20
+ end
21
+ end
22
+
23
+ def call(env)
24
+ match_content_type(env) do |data|
25
+ # encode with the first successfully loaded adapter
26
+ env[:body] = self.class.adapter.encode data
27
+ end
28
+ @app.call env
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ module Faraday
2
+ class Request::Multipart < Request::UrlEncoded
3
+ self.mime_type = 'multipart/form-data'.freeze
4
+ DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze
5
+
6
+ def call(env)
7
+ match_content_type(env) do |params|
8
+ env[:request] ||= {}
9
+ env[:request][:boundary] ||= DEFAULT_BOUNDARY
10
+ env[:request_headers][CONTENT_TYPE] += ";boundary=#{env[:request][:boundary]}"
11
+ env[:body] = create_multipart(env, params)
12
+ end
13
+ @app.call env
14
+ end
15
+
16
+ def process_request?(env)
17
+ type = request_type(env)
18
+ env[:body].respond_to?(:each_key) and !env[:body].empty? and (
19
+ (type.empty? and has_multipart?(env[:body])) or
20
+ type == self.class.mime_type
21
+ )
22
+ end
23
+
24
+ def has_multipart?(body)
25
+ body.values.each do |val|
26
+ if val.respond_to?(:content_type)
27
+ return true
28
+ elsif val.respond_to?(:values)
29
+ return true if has_multipart?(val)
30
+ end
31
+ end
32
+ false
33
+ end
34
+
35
+ def create_multipart(env, params)
36
+ boundary = env[:request][:boundary]
37
+ parts = process_params(params) do |key, value|
38
+ Faraday::Parts::Part.new(boundary, key, value)
39
+ end
40
+ parts << Faraday::Parts::EpiloguePart.new(boundary)
41
+
42
+ body = Faraday::CompositeReadIO.new(parts)
43
+ env[:request_headers]['Content-Length'] = body.length.to_s
44
+ return body
45
+ end
46
+
47
+ def process_params(params, prefix = nil, pieces = nil, &block)
48
+ params.inject(pieces || []) do |all, (key, value)|
49
+ key = "#{prefix}[#{key}]" if prefix
50
+
51
+ case value
52
+ when Array
53
+ values = value.inject([]) { |a,v| a << [nil, v] }
54
+ process_params(values, key, all, &block)
55
+ when Hash
56
+ process_params(value, key, all, &block)
57
+ else
58
+ all << block.call(key, value)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,37 @@
1
+ module Faraday
2
+ class Request::UrlEncoded < Faraday::Middleware
3
+ CONTENT_TYPE = 'Content-Type'.freeze
4
+
5
+ class << self
6
+ attr_accessor :mime_type
7
+ end
8
+ self.mime_type = 'application/x-www-form-urlencoded'.freeze
9
+
10
+ def call(env)
11
+ match_content_type(env) do |data|
12
+ env[:body] = Faraday::Utils.build_nested_query data
13
+ end
14
+ @app.call env
15
+ end
16
+
17
+ def match_content_type(env)
18
+ type = request_type(env)
19
+
20
+ if process_request?(env)
21
+ env[:request_headers][CONTENT_TYPE] ||= self.class.mime_type
22
+ yield env[:body] unless env[:body].respond_to?(:to_str)
23
+ end
24
+ end
25
+
26
+ def process_request?(env)
27
+ type = request_type(env)
28
+ env[:body] and (type.empty? or type == self.class.mime_type)
29
+ end
30
+
31
+ def request_type(env)
32
+ type = env[:request_headers][CONTENT_TYPE].to_s
33
+ type = type.split(';', 2).first if type.index(';')
34
+ type
35
+ end
36
+ end
37
+ end
@@ -1,56 +1,98 @@
1
+ require 'forwardable'
2
+
1
3
  module Faraday
2
4
  class Response
5
+ # Used for simple response middleware.
3
6
  class Middleware < Faraday::Middleware
4
- self.load_error = :abstract
5
-
6
- # Use a response callback in case the request is parallelized.
7
- #
8
- # env[:response].on_complete do |finished_env|
9
- # finished_env[:body] = do_stuff_to(finished_env[:body])
10
- # end
11
- #
12
- def self.register_on_complete(env)
7
+ def call(env)
8
+ @app.call(env).on_complete do |env|
9
+ on_complete(env)
10
+ end
13
11
  end
14
12
 
15
- def call(env)
16
- self.class.register_on_complete(env)
17
- @app.call env
13
+ # Override this to modify the environment after the response has finished.
14
+ # Calls the `parse` method if defined
15
+ def on_complete(env)
16
+ if respond_to? :parse
17
+ env[:body] = parse(env[:body])
18
+ end
18
19
  end
19
20
  end
20
21
 
22
+ extend Forwardable
21
23
  extend AutoloadHelper
22
24
 
23
25
  autoload_all 'faraday/response',
24
- :Yajl => 'yajl',
25
- :ActiveSupportJson => 'active_support_json'
26
+ :RaiseError => 'raise_error',
27
+ :Logger => 'logger'
26
28
 
27
29
  register_lookup_modules \
28
- :yajl => :Yajl,
29
- :activesupport_json => :ActiveSupportJson,
30
- :rails_json => :ActiveSupportJson,
31
- :active_support_json => :ActiveSupportJson
32
- attr_accessor :status, :headers, :body
33
-
34
- def initialize
35
- @status, @headers, @body = nil, nil, nil
30
+ :raise_error => :RaiseError,
31
+ :logger => :Logger
32
+
33
+ def initialize(env = nil)
34
+ @env = env
36
35
  @on_complete_callbacks = []
37
36
  end
38
37
 
39
- def on_complete(&block)
40
- @on_complete_callbacks << block
38
+ attr_reader :env
39
+ alias_method :to_hash, :env
40
+
41
+ def status
42
+ finished? ? env[:status] : nil
43
+ end
44
+
45
+ def headers
46
+ finished? ? env[:response_headers] : {}
47
+ end
48
+ def_delegator :headers, :[]
49
+
50
+ def body
51
+ finished? ? env[:body] : nil
52
+ end
53
+
54
+ def finished?
55
+ !!env
56
+ end
57
+
58
+ def on_complete
59
+ if not finished?
60
+ @on_complete_callbacks << Proc.new
61
+ else
62
+ yield env
63
+ end
64
+ return self
41
65
  end
42
66
 
43
67
  def finish(env)
44
- return self if @status
45
- env[:body] ||= ''
46
- env[:response_headers] ||= {}
47
- @on_complete_callbacks.each { |c| c.call(env) }
48
- @status, @headers, @body = env[:status], env[:response_headers], env[:body]
49
- self
68
+ raise "response already finished" if finished?
69
+ @env = env
70
+ @on_complete_callbacks.each { |callback| callback.call(env) }
71
+ return self
50
72
  end
51
73
 
52
74
  def success?
53
75
  status == 200
54
76
  end
77
+
78
+ # because @on_complete_callbacks cannot be marshalled
79
+ def marshal_dump
80
+ !finished? ? nil : {
81
+ :status => @env[:status], :body => @env[:body],
82
+ :response_headers => @env[:response_headers]
83
+ }
84
+ end
85
+
86
+ def marshal_load(env)
87
+ @env = env
88
+ end
89
+
90
+ # Expand the env with more properties, without overriding existing ones.
91
+ # Useful for applying request params after restoring a marshalled Response.
92
+ def apply_request(request_env)
93
+ raise "response didn't finish yet" unless finished?
94
+ @env = request_env.merge @env
95
+ return self
96
+ end
55
97
  end
56
98
  end
@@ -0,0 +1,34 @@
1
+ require 'forwardable'
2
+
3
+ module Faraday
4
+ class Response::Logger < Response::Middleware
5
+ extend Forwardable
6
+
7
+ def initialize(app, logger = nil)
8
+ super(app)
9
+ @logger = logger || begin
10
+ require 'logger'
11
+ ::Logger.new(STDOUT)
12
+ end
13
+ end
14
+
15
+ def_delegators :@logger, :debug, :info, :warn, :error, :fatal
16
+
17
+ def call(env)
18
+ info "#{env[:method]} #{env[:url].to_s}"
19
+ debug('request') { dump_headers env[:request_headers] }
20
+ super
21
+ end
22
+
23
+ def on_complete(env)
24
+ info('Status') { env[:status].to_s }
25
+ debug('response') { dump_headers env[:response_headers] }
26
+ end
27
+
28
+ private
29
+
30
+ def dump_headers(headers)
31
+ headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module Faraday
2
+ class Response::RaiseError < Response::Middleware
3
+ def on_complete(env)
4
+ case env[:status]
5
+ when 404
6
+ raise Faraday::Error::ResourceNotFound, response_values(env)
7
+ when 400...600
8
+ raise Faraday::Error::ClientError, response_values(env)
9
+ end
10
+ end
11
+
12
+ def response_values(env)
13
+ {:status => env[:status], :headers => env[:response_headers], :body => env[:body]}
14
+ end
15
+ end
16
+ end
@@ -3,13 +3,21 @@ begin
3
3
  require 'parts'
4
4
  require 'stringio'
5
5
  rescue LoadError
6
- puts "Install the multipart-post gem."
6
+ $stderr.puts "Install the multipart-post gem."
7
7
  raise
8
8
  end
9
9
 
10
- # Auto-load multipart-post gem on first request.
11
10
  module Faraday
12
- CompositeReadIO = ::CompositeReadIO
13
- UploadIO = ::UploadIO
14
- Parts = ::Parts
15
- end
11
+ class CompositeReadIO < ::CompositeReadIO
12
+ attr_reader :length
13
+
14
+ def initialize(parts)
15
+ @length = parts.inject(0) { |sum, part| sum + part.length }
16
+ ios = parts.map{ |part| part.to_io }
17
+ super(*ios)
18
+ end
19
+ end
20
+
21
+ UploadIO = ::UploadIO
22
+ Parts = ::Parts
23
+ end