faraday 0.7.6 → 0.8.0.rc2

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.
@@ -1,47 +1,22 @@
1
- ## This is the rakegem gemspec template. Make sure you read and understand
2
- ## all of the comments. Some sections require modification, and others can
3
- ## be deleted if you don't need them. Once you understand the contents of
4
- ## this file, feel free to delete any comments that begin with two hash marks.
5
- ## You can find comprehensive Gem::Specification documentation, at
6
- ## http://docs.rubygems.org/read/chapter/20
7
1
  Gem::Specification.new do |s|
8
2
  s.specification_version = 2 if s.respond_to? :specification_version=
9
3
  s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if s.respond_to? :required_rubygems_version=
10
4
 
11
- ## Leave these as is they will be modified for you by the rake gemspec task.
12
- ## If your rubyforge_project name is different, then edit it and comment out
13
- ## the sub! line in the Rakefile
14
- s.name = 'faraday'
15
- s.version = '0.7.6'
16
- s.date = '2012-01-21'
17
- s.rubyforge_project = 'faraday'
5
+ s.name = 'faraday'
6
+ s.version = '0.8.0.rc2'
18
7
 
19
- ## Make sure your summary is short. The description may be as long
20
- ## as you like.
21
8
  s.summary = "HTTP/REST API client library."
22
- s.description = "HTTP/REST API client library."
9
+ # TODO: s.description
23
10
 
24
- ## List the primary authors. If there are a bunch of authors, it's probably
25
- ## better to set the email to an email list or something. If you don't have
26
- ## a custom homepage, consider using your GitHub URL or the like.
27
11
  s.authors = ["Rick Olson"]
28
12
  s.email = 'technoweenie@gmail.com'
29
- s.homepage = 'http://github.com/technoweenie/faraday'
13
+ s.homepage = 'https://github.com/technoweenie/faraday'
30
14
 
31
- ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
32
- ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
33
- s.require_paths = %w[lib]
34
-
35
- s.add_dependency 'addressable', '~> 2.2'
36
15
  s.add_dependency 'multipart-post', '~> 1.1'
37
- s.add_dependency 'rack', '~> 1.1'
38
16
  s.add_development_dependency 'rake'
39
17
  s.add_development_dependency 'test-unit'
40
18
  s.add_development_dependency 'webmock'
41
19
 
42
- ## Leave this section as-is. It will be automatically generated from the
43
- ## contents of your Git repository via the gemspec task. DO NOT REMOVE
44
- ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
45
20
  # = MANIFEST =
46
21
  s.files = %w[
47
22
  Gemfile
@@ -53,7 +28,9 @@ Gem::Specification.new do |s|
53
28
  lib/faraday.rb
54
29
  lib/faraday/adapter.rb
55
30
  lib/faraday/adapter/action_dispatch.rb
31
+ lib/faraday/adapter/em_http.rb
56
32
  lib/faraday/adapter/em_synchrony.rb
33
+ lib/faraday/adapter/em_synchrony/parallel_manager.rb
57
34
  lib/faraday/adapter/excon.rb
58
35
  lib/faraday/adapter/net_http.rb
59
36
  lib/faraday/adapter/patron.rb
@@ -64,8 +41,10 @@ Gem::Specification.new do |s|
64
41
  lib/faraday/error.rb
65
42
  lib/faraday/middleware.rb
66
43
  lib/faraday/request.rb
67
- lib/faraday/request/json.rb
44
+ lib/faraday/request/basic_authentication.rb
68
45
  lib/faraday/request/multipart.rb
46
+ lib/faraday/request/retry.rb
47
+ lib/faraday/request/token_authentication.rb
69
48
  lib/faraday/request/url_encoded.rb
70
49
  lib/faraday/response.rb
71
50
  lib/faraday/response/logger.rb
@@ -77,17 +56,17 @@ Gem::Specification.new do |s|
77
56
  test/adapters/net_http_test.rb
78
57
  test/adapters/test_middleware_test.rb
79
58
  test/adapters/typhoeus_test.rb
59
+ test/authentication_middleware_test.rb
80
60
  test/connection_test.rb
81
61
  test/env_test.rb
82
62
  test/helper.rb
83
63
  test/live_server.rb
64
+ test/middleware/retry_test.rb
84
65
  test/middleware_stack_test.rb
85
66
  test/request_middleware_test.rb
86
67
  test/response_middleware_test.rb
87
68
  ]
88
69
  # = MANIFEST =
89
70
 
90
- ## Test files will be grabbed from the file list. Make sure the path glob
91
- ## matches what you actually use.
92
71
  s.test_files = s.files.select { |path| path =~ %r{^test/*/.+\.rb} }
93
72
  end
@@ -1,7 +1,8 @@
1
1
  module Faraday
2
- VERSION = "0.7.6"
2
+ VERSION = "0.8.0.rc2"
3
3
 
4
4
  class << self
5
+ attr_accessor :root_path, :lib_path
5
6
  attr_accessor :default_adapter
6
7
  attr_writer :default_connection
7
8
 
@@ -10,29 +11,54 @@ module Faraday
10
11
  Faraday::Connection.new(url, options, &block)
11
12
  end
12
13
 
14
+ def require_libs(*libs)
15
+ libs.each do |lib|
16
+ require "#{lib_path}/#{lib}"
17
+ end
18
+ end
19
+
20
+ alias require_lib require_libs
21
+
13
22
  private
14
23
  def method_missing(name, *args, &block)
15
24
  default_connection.send(name, *args, &block)
16
25
  end
17
26
  end
18
27
 
28
+ self.root_path = File.expand_path "..", __FILE__
29
+ self.lib_path = File.expand_path "../faraday", __FILE__
19
30
  self.default_adapter = :net_http
20
31
 
21
32
  def self.default_connection
22
33
  @default_connection ||= Connection.new
23
34
  end
24
35
 
25
- module AutoloadHelper
26
- def register_lookup_modules(mods)
27
- (@lookup_module_index ||= {}).update(mods)
36
+ module MiddlewareRegistry
37
+ # Internal: Register middleware class(es) on the current module.
38
+ #
39
+ # mapping - A Hash mapping Symbol keys to classes. See
40
+ # Faraday.register_middleware for more details.
41
+ def register_middleware(mapping)
42
+ (@registered_middleware ||= {}).update(mapping)
28
43
  end
29
44
 
30
- def lookup_module(key)
31
- return if !@lookup_module_index
32
- const_get @lookup_module_index[key] || key
45
+ # Internal: Lookup middleware class with a registered Symbol shortcut.
46
+ #
47
+ # Returns a middleware Class.
48
+ def lookup_middleware(key)
49
+ unless defined? @registered_middleware and found = @registered_middleware[key]
50
+ raise "#{key.inspect} is not registered on #{self}"
51
+ end
52
+ found = @registered_middleware[key] = found.call if found.is_a? Proc
53
+ found.is_a?(Module) ? found : const_get(found)
33
54
  end
55
+ end
34
56
 
57
+ module AutoloadHelper
35
58
  def autoload_all(prefix, options)
59
+ if prefix =~ /^faraday(\/|$)/i
60
+ prefix = File.join(Faraday.root_path, prefix)
61
+ end
36
62
  options.each do |const_name, path|
37
63
  autoload const_name, File.join(prefix, path)
38
64
  end
@@ -54,7 +80,30 @@ module Faraday
54
80
 
55
81
  extend AutoloadHelper
56
82
 
57
- autoload_all 'faraday',
83
+ # Public: register middleware classes under a short name.
84
+ #
85
+ # type - A Symbol specifying the kind of middleware (default: :middleware)
86
+ # mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
87
+ # as fully qualified constant, or a Proc that will be lazily called
88
+ # to return the former.
89
+ #
90
+ # Examples
91
+ #
92
+ # Faraday.register_middleware :aloha => MyModule::Aloha
93
+ # Faraday.register_middleware :response, :boom => MyModule::Boom
94
+ #
95
+ # # shortcuts are now available in Builder:
96
+ # builder.use :aloha
97
+ # builder.response :boom
98
+ #
99
+ # Returns nothing.
100
+ def self.register_middleware type, mapping = nil
101
+ type, mapping = :middleware, type if mapping.nil?
102
+ component = self.const_get(type.to_s.capitalize)
103
+ component.register_middleware(mapping)
104
+ end
105
+
106
+ autoload_all "faraday",
58
107
  :Middleware => 'middleware',
59
108
  :Builder => 'builder',
60
109
  :Request => 'request',
@@ -62,12 +111,10 @@ module Faraday
62
111
  :CompositeReadIO => 'upload_io',
63
112
  :UploadIO => 'upload_io',
64
113
  :Parts => 'upload_io'
114
+
115
+ require_libs "utils", "connection", "adapter", "error"
65
116
  end
66
117
 
67
- require 'faraday/utils'
68
- require 'faraday/connection'
69
- require 'faraday/adapter'
70
- require 'faraday/error'
71
118
 
72
119
  # not pulling in active-support JUST for this method.
73
120
  class Object
@@ -3,25 +3,41 @@ module Faraday
3
3
  CONTENT_LENGTH = 'Content-Length'.freeze
4
4
 
5
5
  extend AutoloadHelper
6
+ extend MiddlewareRegistry
6
7
 
7
8
  autoload_all 'faraday/adapter',
8
9
  :ActionDispatch => 'action_dispatch',
9
10
  :NetHttp => 'net_http',
10
11
  :Typhoeus => 'typhoeus',
11
12
  :EMSynchrony => 'em_synchrony',
13
+ :EMHttp => 'em_http',
12
14
  :Patron => 'patron',
13
15
  :Excon => 'excon',
14
16
  :Test => 'test'
15
17
 
16
- register_lookup_modules \
18
+ register_middleware \
17
19
  :action_dispatch => :ActionDispatch,
18
20
  :test => :Test,
19
21
  :net_http => :NetHttp,
20
22
  :typhoeus => :Typhoeus,
21
23
  :patron => :Patron,
22
24
  :em_synchrony => :EMSynchrony,
25
+ :em_http => :EMHttp,
23
26
  :excon => :Excon
24
27
 
28
+ module Parallelism
29
+ attr_writer :supports_parallel
30
+ def supports_parallel?() @supports_parallel end
31
+
32
+ def inherited(subclass)
33
+ super
34
+ subclass.supports_parallel = self.supports_parallel?
35
+ end
36
+ end
37
+
38
+ extend Parallelism
39
+ self.supports_parallel = false
40
+
25
41
  def call(env)
26
42
  if !env[:body] and Connection::METHODS_WITH_BODIES.include? env[:method]
27
43
  # play nice and indicate we're sending an empty body
@@ -0,0 +1,204 @@
1
+ module Faraday
2
+ class Adapter
3
+ # EventMachine adapter is useful for either asynchronous requests
4
+ # when in EM reactor loop or for making parallel requests in
5
+ # synchronous code.
6
+ class EMHttp < Faraday::Adapter
7
+
8
+ dependency 'em-http'
9
+
10
+ self.supports_parallel = true
11
+
12
+ def self.setup_parallel_manager(options = nil)
13
+ Manager.new
14
+ end
15
+
16
+ def call(env)
17
+ super
18
+ perform_request env
19
+ @app.call env
20
+ end
21
+
22
+ def perform_request(env)
23
+ if parallel?(env)
24
+ manager = env[:parallel_manager]
25
+ manager.add {
26
+ perform_single_request(env).
27
+ callback { env[:response].finish(env) }
28
+ }
29
+ else
30
+ unless EventMachine.reactor_running?
31
+ error = nil
32
+ # start EM, block until request is completed
33
+ EventMachine.run do
34
+ perform_single_request(env).
35
+ callback { EventMachine.stop }.
36
+ errback { |client|
37
+ error = error_message(client)
38
+ EventMachine.stop
39
+ }
40
+ end
41
+ raise_error(error) if error
42
+ else
43
+ # EM is running: instruct upstream that this is an async request
44
+ env[:parallel_manager] = true
45
+ perform_single_request(env).
46
+ callback { env[:response].finish(env) }.
47
+ errback {
48
+ # TODO: no way to communicate the error in async mode
49
+ raise NotImplementedError
50
+ }
51
+ end
52
+ end
53
+ end
54
+
55
+ # TODO: reuse the connection to support pipelining
56
+ def perform_single_request(env)
57
+ req = EventMachine::HttpRequest.new(env[:url], connection_config(env))
58
+ req.setup_request(env[:method], request_config(env)).callback { |client|
59
+ save_response(env, client.response_header.status, client.response) do |resp_headers|
60
+ client.response_header.each do |name, value|
61
+ resp_headers[name.to_sym] = value
62
+ end
63
+ end
64
+ }
65
+ end
66
+
67
+ def error_message(client)
68
+ client.error or "request failed"
69
+ end
70
+
71
+ def raise_error(msg)
72
+ errklass = Faraday::Error::ClientError
73
+ if msg == Errno::ETIMEDOUT
74
+ errklass = Faraday::Error::TimeoutError
75
+ msg = "request timed out"
76
+ elsif msg == Errno::ECONNREFUSED
77
+ errklass = Faraday::Error::ConnectionFailed
78
+ msg = "connection refused"
79
+ end
80
+ raise errklass, msg
81
+ end
82
+
83
+ def connection_config(env)
84
+ options = {}
85
+ configure_ssl(options, env)
86
+ configure_proxy(options, env)
87
+ configure_timeout(options, env)
88
+ options
89
+ end
90
+
91
+ def request_config(env)
92
+ options = {
93
+ :body => read_body(env),
94
+ :head => env[:request_headers],
95
+ # :keepalive => true,
96
+ # :file => 'path/to/file', # stream data off disk
97
+ }
98
+ configure_compression(options, env)
99
+ # configure_proxy_auth
100
+ # :proxy => {:authorization => [user, pass]}
101
+ # proxy[:username] && proxy[:password]
102
+ options
103
+ end
104
+
105
+ def read_body(env)
106
+ body = env[:body]
107
+ body.respond_to?(:read) ? body.read : body
108
+ end
109
+
110
+ def configure_ssl(options, env)
111
+ if ssl = env[:ssl]
112
+ # :ssl => {
113
+ # :private_key_file => '/tmp/server.key',
114
+ # :cert_chain_file => '/tmp/server.crt',
115
+ # :verify_peer => false
116
+ end
117
+ end
118
+
119
+ def configure_proxy(options, env)
120
+ if proxy = request_options(env)[:proxy]
121
+ options[:proxy] = {
122
+ :host => proxy[:uri].host,
123
+ :port => proxy[:uri].port
124
+ }
125
+ end
126
+ end
127
+
128
+ def configure_timeout(options, env)
129
+ timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
130
+ options[:connect_timeout] = options[:inactivity_timeout] = timeout
131
+ options[:connect_timeout] = open_timeout if open_timeout
132
+ end
133
+
134
+ def configure_compression(options, env)
135
+ if env[:method] == :get and not options[:head].key? 'accept-encoding'
136
+ options[:head]['accept-encoding'] = 'gzip, compressed'
137
+ end
138
+ end
139
+
140
+ def request_options(env)
141
+ env[:request]
142
+ end
143
+
144
+ def parallel?(env)
145
+ !!env[:parallel_manager]
146
+ end
147
+
148
+ # The parallel manager is designed to start an EventMachine loop
149
+ # and block until all registered requests have been completed.
150
+ class Manager
151
+ def initialize
152
+ reset
153
+ end
154
+
155
+ def reset
156
+ @registered_procs = []
157
+ @num_registered = 0
158
+ @num_succeeded = 0
159
+ @errors = []
160
+ @running = false
161
+ end
162
+
163
+ def running?() @running end
164
+
165
+ def add
166
+ if running?
167
+ perform_request { yield }
168
+ else
169
+ @registered_procs << Proc.new
170
+ end
171
+ @num_registered += 1
172
+ end
173
+
174
+ def run
175
+ if @num_registered > 0
176
+ @running = true
177
+ EventMachine.run do
178
+ @registered_procs.each do |proc|
179
+ perform_request(&proc)
180
+ end
181
+ end
182
+ if @errors.size > 0
183
+ raise Faraday::Error::ClientError, @errors.first || "connection failed"
184
+ end
185
+ end
186
+ ensure
187
+ reset
188
+ end
189
+
190
+ def perform_request
191
+ client = yield
192
+ client.callback { @num_succeeded += 1; check_finished }
193
+ client.errback { @errors << client.error; check_finished }
194
+ end
195
+
196
+ def check_finished
197
+ if @num_succeeded + @errors.size == @num_registered
198
+ EventMachine.stop
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end