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/utils.rb CHANGED
@@ -6,22 +6,64 @@ module Faraday
6
6
  extend Rack::Utils
7
7
  extend self
8
8
 
9
- HEADERS = Hash.new do |h, k|
10
- if k.respond_to?(:to_str)
11
- k
12
- else
13
- k.to_s.split('_'). # :user_agent => %w(user agent)
14
- each { |w| w.capitalize! }. # => %w(User Agent)
15
- join('-') # => "User-Agent"
9
+ class Headers < HeaderHash
10
+ # symbol -> string mapper + cache
11
+ KeyMap = Hash.new do |map, key|
12
+ map[key] = if key.respond_to?(:to_str) then key
13
+ else
14
+ key.to_s.split('_'). # :user_agent => %w(user agent)
15
+ each { |w| w.capitalize! }. # => %w(User Agent)
16
+ join('-') # => "User-Agent"
17
+ end
18
+ end
19
+ KeyMap[:etag] = "ETag"
20
+
21
+ def [](k)
22
+ super(KeyMap[k])
16
23
  end
17
- end
18
24
 
19
- HEADERS.merge! :etag => "ETag"
20
- HEADERS.values.each { |v| v.freeze }
25
+ def []=(k, v)
26
+ # join multiple values with a comma
27
+ v = v.to_ary.join(', ') if v.respond_to? :to_ary
28
+ super(KeyMap[k], v)
29
+ end
30
+
31
+ alias_method :update, :merge!
32
+
33
+ def parse(header_string)
34
+ return unless header_string && !header_string.empty?
35
+ header_string.split(/\r\n/).
36
+ tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
37
+ map { |h| h.split(/:\s+/, 2) }.reject { |(k, v)| k.nil? }. # split key and value, ignore blank lines
38
+ each { |key, value|
39
+ # join multiple values with a comma
40
+ if self[key] then self[key] << ', ' << value
41
+ else self[key] = value
42
+ end
43
+ }
44
+ end
45
+ end
21
46
 
22
47
  # Make Rack::Utils build_query method public.
23
48
  public :build_query
24
49
 
50
+ # Override Rack's version since it doesn't handle non-String values
51
+ def build_nested_query(value, prefix = nil)
52
+ case value
53
+ when Array
54
+ value.map { |v| build_nested_query(v, "#{prefix}[]") }.join("&")
55
+ when Hash
56
+ value.map { |k, v|
57
+ build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
58
+ }.join("&")
59
+ when NilClass
60
+ prefix
61
+ else
62
+ raise ArgumentError, "value must be a Hash" if prefix.nil?
63
+ "#{prefix}=#{escape(value)}"
64
+ end
65
+ end
66
+
25
67
  # Be sure to URI escape '+' symbols to %2B. Otherwise, they get interpreted
26
68
  # as spaces.
27
69
  def escape(s)
@@ -37,15 +79,10 @@ module Faraday
37
79
  end
38
80
  end
39
81
 
40
- # Turns headers keys and values into strings. Look up symbol keys in the
41
- # the HEADERS hash.
42
- #
43
- # h = merge_headers(HeaderHash.new, :content_type => 'text/plain')
44
- # h['Content-Type'] # = 'text/plain'
45
- #
82
+ # Turns headers keys and values into strings
46
83
  def merge_headers(existing_headers, new_headers)
47
84
  new_headers.each do |key, value|
48
- existing_headers[HEADERS[key]] = value.to_s
85
+ existing_headers[key] = value.to_s
49
86
  end
50
87
  end
51
88
 
@@ -1,12 +1,19 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
2
2
 
3
- if Faraday::TestCase::LIVE_SERVER
3
+ if !Faraday::TestCase::LIVE_SERVER
4
+ warn "warning: test server is not up; skipping live server tests"
5
+ else
4
6
  module Adapters
5
7
  class LiveTest < Faraday::TestCase
6
- loaded_adapters = Faraday::Adapter.all_loaded_constants
7
- loaded_adapters -= [Faraday::Adapter::ActionDispatch]
8
- loaded_adapters << :default
9
- loaded_adapters.each do |adapter|
8
+ adapters = if ENV['ADAPTER']
9
+ ENV['ADAPTER'].split(':').map { |name| Faraday::Adapter.lookup_module name.to_sym }
10
+ else
11
+ loaded_adapters = Faraday::Adapter.all_loaded_constants
12
+ loaded_adapters -= [Faraday::Adapter::ActionDispatch]
13
+ loaded_adapters << :default
14
+ end
15
+
16
+ adapters.each do |adapter|
10
17
  define_method "test_#{adapter}_GET_retrieves_the_response_body" do
11
18
  assert_equal 'hello world', create_connection(adapter).get('hello_world').body
12
19
  end
@@ -19,7 +26,17 @@ if Faraday::TestCase::LIVE_SERVER
19
26
  end
20
27
 
21
28
  define_method "test_#{adapter}_GET_retrieves_the_response_headers" do
22
- assert_match /text\/html/, create_connection(adapter).get('hello_world').headers['content-type']
29
+ response = create_connection(adapter).get('hello_world')
30
+ assert_match /text\/html/, response.headers['Content-Type'], 'original case fail'
31
+ assert_match /text\/html/, response.headers['content-type'], 'lowercase fail'
32
+ end
33
+
34
+ # https://github.com/geemus/excon/issues/10
35
+ unless %[Faraday::Adapter::Excon] == adapter.to_s
36
+ define_method "test_#{adapter}_GET_handles_headers_with_multiple_values" do
37
+ response = create_connection(adapter).get('multi')
38
+ assert_equal 'one, two', response.headers['set-cookie']
39
+ end
23
40
  end
24
41
 
25
42
  define_method "test_#{adapter}_POST_send_url_encoded_params" do
@@ -43,13 +60,12 @@ if Faraday::TestCase::LIVE_SERVER
43
60
  end
44
61
 
45
62
  define_method "test_#{adapter}_POST_sends_files" do
46
- name = File.join(File.dirname(__FILE__), '..', 'live_server.rb')
47
63
  resp = create_connection(adapter).post do |req|
48
64
  req.url 'file'
49
- req.body = {'uploaded_file' => Faraday::UploadIO.new(name, 'text/x-ruby')}
65
+ req.body = {'uploaded_file' => Faraday::UploadIO.new(__FILE__, 'text/x-ruby')}
50
66
  end
51
- assert_equal "file live_server.rb text/x-ruby", resp.body
52
- end
67
+ assert_equal "file live_test.rb text/x-ruby", resp.body
68
+ end unless :default == adapter # isn't configured for multipart
53
69
 
54
70
  # http://github.com/toland/patron/issues/#issue/9
55
71
  if ENV['FORCE'] || adapter != Faraday::Adapter::Patron
@@ -142,13 +158,19 @@ if Faraday::TestCase::LIVE_SERVER
142
158
 
143
159
  def create_connection(adapter)
144
160
  if adapter == :default
145
- conn = Faraday.default_connection
146
- conn.url_prefix = LIVE_SERVER
147
- conn
161
+ Faraday.default_connection.tap do |conn|
162
+ conn.url_prefix = LIVE_SERVER
163
+ conn.headers['X-Faraday-Adapter'] = adapter.to_s
164
+ end
148
165
  else
149
- Faraday::Connection.new LIVE_SERVER do |b|
166
+ Faraday::Connection.new LIVE_SERVER, :headers => {'X-Faraday-Adapter' => adapter.to_s} do |b|
167
+ b.request :multipart
168
+ b.request :url_encoded
150
169
  b.use adapter
151
170
  end
171
+ end.tap do |conn|
172
+ target = conn.builder.handlers.last
173
+ conn.builder.insert_before target, Faraday::Response::RaiseError
152
174
  end
153
175
  end
154
176
 
@@ -10,7 +10,7 @@ module Adapters
10
10
  @logger.level = Logger::DEBUG
11
11
 
12
12
  @conn = Faraday.new do |b|
13
- b.adapter :logger, @logger
13
+ b.response :logger, @logger
14
14
  b.adapter :test do |stubs|
15
15
  stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] }
16
16
  end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
2
+
3
+ module Adapters
4
+ class NetHttpTest < Faraday::TestCase
5
+ def setup
6
+ @connection = Faraday.new('http://disney.com') do |b|
7
+ b.adapter :net_http
8
+ end
9
+ end
10
+
11
+ def test_handles_compression_transparently_on_get
12
+ stub_request(:get, 'disney.com/hello').with { |request|
13
+ accept_encoding = request.headers['Accept-Encoding']
14
+ if RUBY_VERSION.index('1.8') == 0
15
+ # ruby 1.8 doesn't do any gzip/deflate automatically
16
+ accept_encoding == nil
17
+ else
18
+ # test for a value such as "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
19
+ accept_encoding =~ /gzip;.+\bdeflate\b/
20
+ end
21
+ }
22
+ @connection.get('/hello')
23
+ end
24
+
25
+ def test_connect_error_gets_wrapped
26
+ stub_request(:get, 'disney.com/hello').to_raise(Errno::ECONNREFUSED)
27
+
28
+ assert_raise Faraday::Error::ConnectionFailed do
29
+ @connection.get('/hello')
30
+ end
31
+ end
32
+ end
33
+ end
@@ -242,43 +242,4 @@ class TestConnection < Faraday::TestCase
242
242
  assert_not_equal conn.send(attr).object_id, duped.send(attr).object_id
243
243
  end
244
244
  end
245
-
246
- def test_allows_rebuilding_of_connection_handlers
247
- conn = Faraday::Connection.new
248
- conn.to_app
249
- inner = conn.builder.handlers[0]
250
- mware = conn.builder.handlers[1].call({})
251
- assert_kind_of Faraday::Adapter::NetHttp, mware
252
-
253
- conn.build do |b|
254
- b.adapter :test
255
- end
256
- mware = conn.builder.handlers[1].call({})
257
- assert_kind_of Faraday::Adapter::Test, mware
258
- assert_equal inner, conn.builder.handlers[0]
259
- end
260
-
261
- def test_allows_extending_of_existing_connection_handlers
262
- conn = Faraday::Connection.new
263
- conn.to_app
264
- mware = conn.builder.handlers[1].call({})
265
- assert_kind_of Faraday::Adapter::NetHttp, mware
266
- assert_equal 2, conn.builder.handlers.size
267
-
268
- conn.build :keep => true do |b|
269
- b.adapter :test
270
- end
271
- mware = conn.builder.handlers[1].call({})
272
- assert_kind_of Faraday::Adapter::Test, mware
273
- assert_equal 3, conn.builder.handlers.size
274
- end
275
-
276
- def test_sets_default_adapter_if_none_set
277
- conn = Faraday::Connection.new
278
- assert_equal 0, conn.builder.handlers.size
279
-
280
- app = conn.to_app
281
- mware = conn.builder.handlers[1].call({})
282
- assert_kind_of Faraday::Adapter::NetHttp, mware
283
- end
284
245
  end
data/test/env_test.rb CHANGED
@@ -1,15 +1,13 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
2
 
3
- class TestEnv < Faraday::TestCase
3
+ class EnvTest < Faraday::TestCase
4
4
  def setup
5
5
  @conn = Faraday.new :url => 'http://sushi.com/api', :headers => {'Mime-Version' => '1.0'}
6
6
  @conn.options[:timeout] = 3
7
7
  @conn.options[:open_timeout] = 5
8
8
  @conn.ssl[:verify] = false
9
9
  @conn.proxy 'http://proxy.com'
10
- @input = {
11
- :body => 'abc',
12
- :headers => {'Server' => 'Faraday'}}
10
+ @input = { :body => 'abc' }
13
11
  @env = env_for @conn do |req|
14
12
  req.url 'foo.json', 'a' => 1
15
13
  req['Server'] = 'Faraday'
@@ -26,8 +24,9 @@ class TestEnv < Faraday::TestCase
26
24
  end
27
25
 
28
26
  def test_request_create_stores_headers
29
- assert_kind_of Rack::Utils::HeaderHash, @env[:request_headers]
30
- assert_equal @input[:headers].merge('Mime-Version' => '1.0'), @env[:request_headers]
27
+ headers = @env[:request_headers]
28
+ assert_equal '1.0', headers['mime-version']
29
+ assert_equal 'Faraday', headers['server']
31
30
  end
32
31
 
33
32
  def test_request_create_stores_body
@@ -54,3 +53,82 @@ class TestEnv < Faraday::TestCase
54
53
  env_setup.to_env_hash(connection, :get)
55
54
  end
56
55
  end
56
+
57
+ class HeadersTest < Faraday::TestCase
58
+ def setup
59
+ @headers = Faraday::Utils::Headers.new
60
+ end
61
+
62
+ def test_parse_response_headers_leaves_http_status_line_out
63
+ @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
64
+ assert_equal %w(Content-Type), @headers.keys
65
+ end
66
+
67
+ def test_parse_response_headers_parses_lower_cased_header_name_and_value
68
+ @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
69
+ assert_equal 'text/html', @headers['content-type']
70
+ end
71
+
72
+ def test_parse_response_headers_parses_lower_cased_header_name_and_value_with_colon
73
+ @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n")
74
+ assert_equal 'http://sushi.com/', @headers['location']
75
+ end
76
+
77
+ def test_parse_response_headers_parses_blank_lines
78
+ @headers.parse("HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n")
79
+ assert_equal 'text/html', @headers['content-type']
80
+ end
81
+ end
82
+
83
+ class ResponseTest < Faraday::TestCase
84
+ def setup
85
+ @env = {
86
+ :status => 404, :body => 'yikes',
87
+ :response_headers => Faraday::Utils::Headers.new('Content-Type' => 'text/plain')
88
+ }
89
+ @response = Faraday::Response.new @env
90
+ end
91
+
92
+ def test_finished
93
+ assert @response.finished?
94
+ end
95
+
96
+ def test_error_on_finish
97
+ assert_raises RuntimeError do
98
+ @response.finish({})
99
+ end
100
+ end
101
+
102
+ def test_not_success
103
+ assert !@response.success?
104
+ end
105
+
106
+ def test_status
107
+ assert_equal 404, @response.status
108
+ end
109
+
110
+ def test_body
111
+ assert_equal 'yikes', @response.body
112
+ end
113
+
114
+ def test_headers
115
+ assert_equal 'text/plain', @response.headers['Content-Type']
116
+ assert_equal 'text/plain', @response['content-type']
117
+ end
118
+
119
+ def test_apply_request
120
+ @response.apply_request :body => 'a=b', :method => :post
121
+ assert_equal 'yikes', @response.body
122
+ assert_equal :post, @response.env[:method]
123
+ end
124
+
125
+ def test_marshal
126
+ @response = Faraday::Response.new
127
+ @response.on_complete { }
128
+ @response.finish @env.merge(:custom => 'moo')
129
+
130
+ loaded = Marshal.load Marshal.dump(@response)
131
+ assert_nil loaded.env[:custom]
132
+ assert_equal %w[body response_headers status], loaded.env.keys.map { |k| k.to_s }.sort
133
+ end
134
+ end
data/test/helper.rb CHANGED
@@ -1,8 +1,13 @@
1
- require 'rubygems'
2
- gem 'rack', '>= 1.2.1'
3
- gem 'addressable', '>= 2.2.2'
4
-
5
1
  require 'test/unit'
2
+ unless ENV['BUNDLE_GEMFILE']
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ Bundler.setup
6
+ end
7
+
8
+ require 'webmock/test_unit'
9
+ WebMock.disable_net_connect!(:allow_localhost => true)
10
+
6
11
  if ENV['LEFTRIGHT']
7
12
  begin
8
13
  require 'leftright'
@@ -11,14 +16,18 @@ if ENV['LEFTRIGHT']
11
16
  end
12
17
  end
13
18
 
14
- $LOAD_PATH.unshift(File.dirname(__FILE__))
15
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
19
+ unless $LOAD_PATH.include? 'lib'
20
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
21
+ $LOAD_PATH.unshift(File.join($LOAD_PATH.first, '..', 'lib'))
22
+ end
16
23
  require 'faraday'
17
24
 
18
25
  begin
19
26
  require 'ruby-debug'
20
- Debugger.start
21
27
  rescue LoadError
28
+ # ignore
29
+ else
30
+ Debugger.start
22
31
  end
23
32
 
24
33
  module Faraday
@@ -31,6 +40,6 @@ module Faraday
31
40
 
32
41
  def test_default
33
42
  assert true
34
- end
43
+ end unless defined? ::MiniTest
35
44
  end
36
45
  end
data/test/live_server.rb CHANGED
@@ -1,39 +1,41 @@
1
1
  require 'sinatra'
2
+ set :logging, false
2
3
 
3
4
  get '/hello_world' do
4
5
  'hello world'
5
6
  end
6
7
 
7
8
  get '/json' do
9
+ content_type 'application/json'
8
10
  "[1,2,3]"
9
11
  end
10
12
 
11
13
  post '/file' do
12
- "file %s %s" % [
13
- params[:uploaded_file][:filename],
14
- params[:uploaded_file][:type]]
14
+ if params[:uploaded_file].respond_to? :each_key
15
+ "file %s %s" % [
16
+ params[:uploaded_file][:filename],
17
+ params[:uploaded_file][:type]]
18
+ else
19
+ status 400
20
+ end
15
21
  end
16
22
 
17
- post '/hello' do
18
- "hello #{params[:name]}"
23
+ %w[get post].each do |method|
24
+ send(method, '/hello') do
25
+ "hello #{params[:name]}"
26
+ end
19
27
  end
20
28
 
21
- get '/hello' do
22
- "hello #{params[:name]}"
23
- end
24
-
25
- post '/echo_name' do
26
- params[:name].inspect
27
- end
28
-
29
- put '/echo_name' do
30
- params[:name].inspect
29
+ %w[post put].each do |method|
30
+ send(method, '/echo_name') do
31
+ params[:name].inspect
32
+ end
31
33
  end
32
34
 
33
35
  delete '/delete_with_json' do
34
36
  %/{"deleted":true}/
35
37
  end
36
38
 
37
- delete '/delete_with_params' do
38
- params[:deleted]
39
+ get '/multi' do
40
+ [200, { 'Set-Cookie' => %w[ one two ] }, '']
39
41
  end