faraday 0.5.7 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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