rest-core 0.8.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.travis.yml +4 -2
  2. data/CHANGES.md +130 -19
  3. data/Gemfile +4 -0
  4. data/README.md +147 -13
  5. data/TODO.md +0 -10
  6. data/doc/ToC.md +7 -0
  7. data/doc/dependency.md +4 -0
  8. data/doc/design.md +4 -0
  9. data/example/auto.rb +51 -0
  10. data/example/coolio.rb +21 -0
  11. data/example/eventmachine.rb +28 -0
  12. data/example/multi.rb +33 -0
  13. data/example/rest-client.rb +16 -0
  14. data/lib/rest-core/app/abstract/async_fiber.rb +13 -0
  15. data/lib/rest-core/app/auto.rb +22 -0
  16. data/lib/rest-core/app/coolio-async.rb +32 -0
  17. data/lib/rest-core/app/coolio-fiber.rb +30 -0
  18. data/lib/rest-core/app/coolio.rb +9 -0
  19. data/lib/rest-core/app/dry.rb +1 -0
  20. data/lib/rest-core/app/em-http-request-async.rb +34 -0
  21. data/lib/rest-core/app/em-http-request-fiber.rb +39 -0
  22. data/lib/rest-core/app/em-http-request.rb +9 -0
  23. data/lib/rest-core/app/rest-client.rb +24 -7
  24. data/lib/rest-core/client/universal.rb +3 -4
  25. data/lib/rest-core/client.rb +31 -4
  26. data/lib/rest-core/middleware/cache.rb +23 -5
  27. data/lib/rest-core/middleware/common_logger.rb +13 -3
  28. data/lib/rest-core/middleware/error_detector.rb +10 -1
  29. data/lib/rest-core/middleware/error_handler.rb +7 -1
  30. data/lib/rest-core/middleware/follow_redirect.rb +42 -0
  31. data/lib/rest-core/middleware/json_decode.rb +11 -2
  32. data/lib/rest-core/middleware/timeout/coolio_timer.rb +4 -0
  33. data/lib/rest-core/middleware/timeout/eventmachine_timer.rb +14 -0
  34. data/lib/rest-core/middleware/timeout.rb +73 -1
  35. data/lib/rest-core/middleware.rb +7 -0
  36. data/lib/rest-core/patch/rest-client.rb +35 -0
  37. data/lib/rest-core/version.rb +1 -1
  38. data/lib/rest-core.rb +19 -0
  39. data/rest-core.gemspec +28 -8
  40. data/test/test_client.rb +20 -3
  41. data/test/test_follow_redirect.rb +47 -0
  42. data/test/test_json_decode.rb +24 -0
  43. metadata +36 -11
  44. data/example/facebook.rb +0 -9
  45. data/example/github.rb +0 -4
  46. data/example/linkedin.rb +0 -8
  47. data/example/twitter.rb +0 -11
@@ -0,0 +1,22 @@
1
+
2
+ require 'rest-core/middleware'
3
+
4
+ class RestCore::Auto
5
+ include RestCore::Middleware
6
+ def call env
7
+ client = http_client
8
+ client.call(log(env, "Auto picked: #{client.class}"))
9
+ end
10
+
11
+ def http_client
12
+ if Object.const_defined?(:Coolio) && ::Coolio::Loop.default.
13
+ has_active_watchers?
14
+ @coolio ||= RestCore::Coolio.new
15
+ elsif Object.const_defined?(:EventMachine) && ::EventMachine.
16
+ reactor_running?
17
+ @emhttprequest ||= RestCore::EmHttpRequest.new
18
+ else
19
+ @restclient ||= RestCore::RestClient.new
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+
2
+ require 'rest-core/middleware'
3
+
4
+ require 'cool.io-http'
5
+
6
+ class RestCore::CoolioAsync
7
+ include RestCore::Middleware
8
+ def call env
9
+ client = ::Coolio::Http.request(:method => env[REQUEST_METHOD] ,
10
+ :url => request_uri(env) ,
11
+ :payload => env[REQUEST_PAYLOAD],
12
+ :headers => env[REQUEST_HEADERS]){ |res|
13
+
14
+ env[TIMER].detach if env[TIMER]
15
+ env[ASYNC].call(env.merge(RESPONSE_BODY => res.body ,
16
+ RESPONSE_STATUS => res.status,
17
+ RESPONSE_HEADERS => res.headers)) if
18
+ env[ASYNC]
19
+ }
20
+
21
+ env[TIMER].on_timer{
22
+ detach
23
+ client.detach
24
+ env[ASYNC].call(env.merge(RESPONSE_BODY => env[TIMER].error,
25
+ RESPONSE_STATUS => 0 ,
26
+ RESPONSE_HEADERS => {} )) if
27
+ env[ASYNC]
28
+ } if env[TIMER]
29
+
30
+ env
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+
2
+ require 'rest-core/middleware'
3
+
4
+ require 'cool.io-http'
5
+
6
+ class RestCore::CoolioFiber
7
+ include RestCore::Middleware
8
+ def call env
9
+ process(env,
10
+ ::Coolio::HttpFiber.request(:method => env[REQUEST_METHOD] ,
11
+ :url => request_uri(env) ,
12
+ :payload => env[REQUEST_PAYLOAD],
13
+ :headers => env[REQUEST_HEADERS]))
14
+ end
15
+
16
+ def process env, response
17
+ env[TIMER].detach if env[TIMER]
18
+ Thread.current[:coolio_http_client].detach if
19
+ Thread.current[:coolio_http_client].kind_of?(::Coolio::HttpFiber)
20
+
21
+ raise response if response.kind_of?(::Exception)
22
+
23
+ result = env.merge(RESPONSE_BODY => response.body ,
24
+ RESPONSE_STATUS => response.status,
25
+ RESPONSE_HEADERS => response.headers)
26
+
27
+ result[ASYNC].call(result) if result[ASYNC]
28
+ result
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+
2
+ require 'rest-core/app/abstract/async_fiber'
3
+ require 'rest-core/app/coolio-async'
4
+ require 'rest-core/app/coolio-fiber'
5
+
6
+ class RestCore::Coolio < RestCore::AsyncFiber
7
+ def async; @async ||= CoolioAsync.new; end
8
+ def fiber; @fiber ||= CoolioFiber.new; end
9
+ end
@@ -4,6 +4,7 @@ require 'rest-core/middleware'
4
4
  class RestCore::Dry
5
5
  include RestCore::Middleware
6
6
  def call env
7
+ env[ASYNC].call(env) if env[ASYNC]
7
8
  env
8
9
  end
9
10
  end
@@ -0,0 +1,34 @@
1
+
2
+ require 'rest-core/middleware'
3
+
4
+ require 'rest-client'
5
+ require 'em-http-request'
6
+
7
+ class RestCore::EmHttpRequestAsync
8
+ include RestCore::Middleware
9
+ def call env
10
+ payload = ::RestClient::Payload.generate(env[REQUEST_PAYLOAD])
11
+ client = ::EventMachine::HttpRequest.new(request_uri(env)).send(
12
+ env[REQUEST_METHOD],
13
+ :body => payload.read,
14
+ :head => payload.headers.merge(env[REQUEST_HEADERS]))
15
+
16
+ client.callback{
17
+ env[TIMER].cancel if env[TIMER]
18
+ env[ASYNC].call(env.merge(
19
+ RESPONSE_BODY => client.response,
20
+ RESPONSE_STATUS => client.response_header.status,
21
+ RESPONSE_HEADERS => client.response_header)) if env[ASYNC]
22
+ }
23
+
24
+ env[TIMER].on_timeout{
25
+ client.close
26
+ env[ASYNC].call(env.merge(RESPONSE_BODY => env[TIMER].error,
27
+ RESPONSE_STATUS => 0 ,
28
+ RESPONSE_HEADERS => {} )) if
29
+ env[ASYNC]
30
+ } if env[TIMER]
31
+
32
+ env
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+
2
+ require 'rest-core/middleware'
3
+
4
+ require 'rest-client'
5
+ require 'em-http-request'
6
+ require 'fiber'
7
+
8
+ class RestCore::EmHttpRequestFiber
9
+ include RestCore::Middleware
10
+ def call env
11
+ f = Fiber.current
12
+
13
+ payload = ::RestClient::Payload.generate(env[REQUEST_PAYLOAD])
14
+ client = ::EventMachine::HttpRequest.new(request_uri(env)).send(
15
+ env[REQUEST_METHOD],
16
+ :body => payload.read,
17
+ :head => payload.headers.merge(env[REQUEST_HEADERS]))
18
+
19
+ client.callback{
20
+ env[TIMER].cancel if env[TIMER]
21
+ f.resume(process(env, client)) if f.alive?
22
+ }
23
+
24
+ if (response = Fiber.yield).kind_of?(::Exception)
25
+ client.close
26
+ raise response
27
+ else
28
+ response
29
+ end
30
+ end
31
+
32
+ def process env, client
33
+ result = env.merge(RESPONSE_BODY => client.response,
34
+ RESPONSE_STATUS => client.response_header.status,
35
+ RESPONSE_HEADERS => client.response_header)
36
+ result[ASYNC].call(result) if result[ASYNC]
37
+ result
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+
2
+ require 'rest-core/app/abstract/async_fiber'
3
+ require 'rest-core/app/em-http-request-async'
4
+ require 'rest-core/app/em-http-request-fiber'
5
+
6
+ class RestCore::EmHttpRequest < RestCore::AsyncFiber
7
+ def async; @async ||= EmHttpRequestAsync.new; end
8
+ def fiber; @fiber ||= EmHttpRequestFiber.new; end
9
+ end
@@ -3,22 +3,39 @@ require 'rest-core/middleware'
3
3
 
4
4
  require 'restclient'
5
5
 
6
+ require 'rest-core/patch/rest-client'
7
+
6
8
  class RestCore::RestClient
7
9
  include RestCore::Middleware
8
10
  def call env
9
- respond(env,
11
+ process(env,
10
12
  ::RestClient::Request.execute(:method => env[REQUEST_METHOD ],
11
13
  :url => request_uri(env) ,
12
14
  :payload => env[REQUEST_PAYLOAD],
13
- :headers => env[REQUEST_HEADERS]))
15
+ :headers => env[REQUEST_HEADERS],
16
+ :max_redirects => 0))
14
17
 
15
18
  rescue ::RestClient::Exception => e
16
- respond(env, e.response)
19
+ process(env, e.response)
20
+ end
21
+
22
+ def process env, response
23
+ result = env.merge(RESPONSE_BODY => response.body,
24
+ RESPONSE_STATUS => response.code,
25
+ RESPONSE_HEADERS => normalize_headers(
26
+ response.raw_headers))
27
+ result[ASYNC].call(result) if result[ASYNC]
28
+ result
17
29
  end
18
30
 
19
- def respond env, response
20
- env.merge(RESPONSE_BODY => response.body,
21
- RESPONSE_STATUS => response.code,
22
- RESPONSE_HEADERS => response.raw_headers)
31
+ def normalize_headers raw_headers
32
+ raw_headers.inject({}){ |r, (k, v)|
33
+ r[k.to_s.upcase.tr('-', '_')] = if v.kind_of?(Array) && v.size == 1
34
+ v.first
35
+ else
36
+ v
37
+ end
38
+ r
39
+ }
23
40
  end
24
41
  end
@@ -1,19 +1,18 @@
1
1
 
2
- RestCore::Universal = RestCore::Builder.client(:data) do
2
+ RestCore::Universal = RestCore::Builder.client do
3
3
  s = self.class # this is only for ruby 1.8!
4
4
  use s::Timeout , 0
5
5
 
6
6
  use s::DefaultSite , nil
7
7
  use s::DefaultHeaders, {}
8
8
  use s::DefaultQuery , {}
9
+ use s::AuthBasic , nil, nil
9
10
 
11
+ use s::FollowRedirect, 10
10
12
  use s::CommonLogger , method(:puts)
11
- use s::AuthBasic , nil, nil
12
13
  use s::Cache , {}, 600 do
13
14
  use s::ErrorHandler, nil
14
15
  use s::ErrorDetectorHttp
15
16
  use s::JsonDecode , false
16
17
  end
17
-
18
- use s::Defaults , :data => lambda{{}}
19
18
  end
@@ -60,8 +60,8 @@ module RestCore::Client
60
60
  end
61
61
 
62
62
  def inspect
63
- "#<struct #{self.class.name} #{attributes.map{ |k, v|
64
- "#{k}=#{v.inspect}" }.join(', ')}>"
63
+ fields = attributes.map{ |k, v| "#{k}=#{v.inspect}" }.join(', ')
64
+ "#<struct #{self.class.name}#{if fields.empty? then '' else fields end}>"
65
65
  end
66
66
 
67
67
  def lighten! o={}
@@ -118,6 +118,20 @@ module RestCore::Client
118
118
  REQUEST_QUERY => query }.merge(opts), &cb)
119
119
  end
120
120
 
121
+ def head path, query={}, opts={}, &cb
122
+ request(
123
+ {REQUEST_METHOD => :head ,
124
+ REQUEST_PATH => path ,
125
+ REQUEST_QUERY => query }.merge(opts), &cb)
126
+ end
127
+
128
+ def options path, query={}, opts={}, &cb
129
+ request(
130
+ {REQUEST_METHOD => :options,
131
+ REQUEST_PATH => path ,
132
+ REQUEST_QUERY => query }.merge(opts), &cb)
133
+ end
134
+
121
135
  def post path, payload={}, query={}, opts={}, &cb
122
136
  request(
123
137
  {REQUEST_METHOD => :post ,
@@ -134,6 +148,14 @@ module RestCore::Client
134
148
  REQUEST_PAYLOAD => payload}.merge(opts), &cb)
135
149
  end
136
150
 
151
+ def patch path, payload={}, query={}, opts={}, &cb
152
+ request(
153
+ {REQUEST_METHOD => :patch ,
154
+ REQUEST_PATH => path ,
155
+ REQUEST_QUERY => query ,
156
+ REQUEST_PAYLOAD => payload}.merge(opts), &cb)
157
+ end
158
+
137
159
  def request env, app=app
138
160
  if block_given?
139
161
  request_full(env, app){ |response|
@@ -152,10 +174,15 @@ module RestCore::Client
152
174
  REQUEST_PAYLOAD => {} ,
153
175
  REQUEST_HEADERS => {} ,
154
176
  FAIL => [] ,
155
- LOG => [] }.merge(env)))
177
+ LOG => [] ,
178
+ ASYNC => if block_given?
179
+ lambda{ |response| yield(response) }
180
+ else
181
+ nil
182
+ end}.merge(env)))
156
183
 
157
184
  if block_given?
158
- yield(response)
185
+ self
159
186
  else
160
187
  response
161
188
  end
@@ -25,16 +25,34 @@ class RestCore::Cache
25
25
  if cached = cache_get(e)
26
26
  wrapped.call(cached)
27
27
  else
28
- response = app.call(e)
29
- response_wrapped = wrapped.call(response)
30
- if (response_wrapped[FAIL] || []).empty?
31
- cache_for(e, response).merge(response_wrapped)
28
+ if e[ASYNC]
29
+ app.call(e.merge(ASYNC => lambda{ |response|
30
+ process(e, response)
31
+ }))
32
32
  else
33
- response_wrapped
33
+ process(e, app.call(e))
34
34
  end
35
35
  end
36
36
  end
37
37
 
38
+ def process env, response
39
+ if env[ASYNC]
40
+ wrapped.call(response.merge(ASYNC => lambda{ |response_wrapped|
41
+ env[ASYNC].call(process_wrapped(env, response, response_wrapped))
42
+ }))
43
+ else
44
+ process_wrapped(env, response, wrapped.call(response))
45
+ end
46
+ end
47
+
48
+ def process_wrapped env, response, response_wrapped
49
+ if (response_wrapped[FAIL] || []).empty?
50
+ cache_for(env, response).merge(response_wrapped)
51
+ else
52
+ response_wrapped
53
+ end
54
+ end
55
+
38
56
  protected
39
57
  def cache_key env
40
58
  Digest::MD5.hexdigest(env['cache.key'] || request_uri(env))
@@ -8,13 +8,23 @@ class RestCore::CommonLogger
8
8
 
9
9
  def call env
10
10
  start_time = Time.now
11
- response = app.call(flushed = flush(env))
12
- flush(log(response, log_request(start_time, response)))
11
+ flushed = flush(env)
12
+ if env[ASYNC]
13
+ app.call(flushed.merge(ASYNC => lambda{ |response|
14
+ env[ASYNC].call(process(response, start_time))
15
+ }))
16
+ else
17
+ process(app.call(flushed), start_time)
18
+ end
13
19
  rescue
14
- flush(log(flushed, log_request(start_time, flushed)))
20
+ process(flushed, start_time)
15
21
  raise
16
22
  end
17
23
 
24
+ def process response, start_time
25
+ flush(log(response, log_request(start_time, response)))
26
+ end
27
+
18
28
  def flush env
19
29
  return env if !log_method(env) || env[DRY]
20
30
  (env[LOG] || []).compact.
@@ -6,7 +6,16 @@ class RestCore::ErrorDetector
6
6
  include RestCore::Middleware
7
7
 
8
8
  def call env
9
- response = app.call(env)
9
+ if env[ASYNC]
10
+ app.call(env.merge(ASYNC => lambda{ |response|
11
+ env[ASYNC].call(process(env, response))
12
+ }))
13
+ else
14
+ process(env, app.call(env))
15
+ end
16
+ end
17
+
18
+ def process env, response
10
19
  detector = error_detector(env)
11
20
  if error = (detector && detector.call(response))
12
21
  fail(response, error)
@@ -6,7 +6,13 @@ class RestCore::ErrorHandler
6
6
  include RestCore::Middleware
7
7
 
8
8
  def call env
9
- handle(app.call(handle(env)))
9
+ if env[ASYNC]
10
+ app.call(handle(env).merge(ASYNC => lambda{ |response|
11
+ env[ASYNC].call(handle(response))
12
+ }))
13
+ else
14
+ handle(app.call(handle(env)))
15
+ end
10
16
  end
11
17
 
12
18
  def handle env
@@ -0,0 +1,42 @@
1
+
2
+ require 'rest-core/middleware'
3
+
4
+ class RestCore::FollowRedirect
5
+ def self.members; [:max_redirects]; end
6
+ include RestCore::Middleware
7
+
8
+ def call env
9
+ e = env.merge('follow_redirect.max_redirects' =>
10
+ env['follow_redirect.max_redirects'] ||
11
+ max_redirects(env))
12
+
13
+ return app.call(e) if e[DRY]
14
+ if e[ASYNC]
15
+ app.call(e.merge(ASYNC => lambda{ |response|
16
+ e[ASYNC].call(process(response))
17
+ }))
18
+ else
19
+ process(app.call(e))
20
+ end
21
+ end
22
+
23
+ def process res
24
+ return res if res['follow_redirect.max_redirects'] <= 0
25
+ return res if ![301,302,303,307].include?(res[RESPONSE_STATUS])
26
+ return res if [301,302 ,307].include?(res[RESPONSE_STATUS]) &&
27
+ ![:get, :head ].include?(res[REQUEST_METHOD])
28
+
29
+ location = [res[RESPONSE_HEADERS]['LOCATION']].flatten.first
30
+ meth = if res[RESPONSE_STATUS] == 303
31
+ :get
32
+ else
33
+ res[REQUEST_METHOD]
34
+ end
35
+
36
+ call(res.merge(REQUEST_PATH => location,
37
+ REQUEST_METHOD => meth ,
38
+ REQUEST_PAYLOAD => nil ,
39
+ 'follow_redirect.max_redirects' =>
40
+ res['follow_redirect.max_redirects'] - 1))
41
+ end
42
+ end
@@ -7,8 +7,17 @@ class RestCore::JsonDecode
7
7
 
8
8
  def call env
9
9
  return app.call(env) if env[DRY]
10
- response = app.call(env)
11
- if json_decode(env)
10
+ if env[ASYNC]
11
+ app.call(env.merge(ASYNC => lambda{ |response|
12
+ env[ASYNC].call(process(response))
13
+ }))
14
+ else
15
+ process(app.call(env))
16
+ end
17
+ end
18
+
19
+ def process response
20
+ if json_decode(response)
12
21
  response.merge(RESPONSE_BODY =>
13
22
  self.class.json_decode("[#{response[RESPONSE_BODY]}]").first)
14
23
  # [this].first is not needed for yajl-ruby
@@ -0,0 +1,4 @@
1
+
2
+ class RestCore::Timeout::CoolioTimer < ::Coolio::TimerWatcher
3
+ attr_accessor :error
4
+ end
@@ -0,0 +1,14 @@
1
+
2
+ class RestCore::Timeout::EventMachineTimer < ::EventMachine::Timer
3
+ attr_accessor :timeout, :error
4
+
5
+ def initialize timeout, error, &block
6
+ super(timeout, &block) if block_given?
7
+ self.timeout = timeout
8
+ self.error = error
9
+ end
10
+
11
+ def on_timeout &block
12
+ send(:initialize, timeout, error, &block)
13
+ end
14
+ end
@@ -8,6 +8,78 @@ class RestCore::Timeout
8
8
  include RestCore::Middleware
9
9
 
10
10
  def call env
11
- ::Timeout.timeout(timeout(env)){ app.call(env) }
11
+ return app.call(env) if env[DRY]
12
+ monitor(env){ |e| app.call(e) }
12
13
  end
14
+
15
+ def monitor env
16
+ class_name = case name = run.class.to_s
17
+ when /Auto/
18
+ run.http_client.class.to_s
19
+ else
20
+ name
21
+ end
22
+
23
+ case class_name
24
+ when /Coolio|EmHttpRequest/
25
+ if root_fiber? && env[ASYNC]
26
+ yield(env.merge(TIMER => timeout_with_callback(env, class_name)))
27
+ else
28
+ yield(env.merge(TIMER => timeout_with_resume( env, class_name)))
29
+ end
30
+ else
31
+ ::Timeout.timeout(timeout(env)){ yield(env) }
32
+ end
33
+ end
34
+
35
+ def root_fiber?
36
+ if RestCore.const_defined?(:RootFiber)
37
+ RootFiber == Fiber.current
38
+ else
39
+ true
40
+ end
41
+ end
42
+
43
+ def timeout_with_callback env, class_name
44
+ case class_name
45
+ when /Coolio/
46
+ timer = CoolioTimer.new(timeout(env))
47
+ timer.error = timeout_error
48
+ timer.attach(::Coolio::Loop.default)
49
+ timer
50
+ when /EmHttpRequest/
51
+ EventMachineTimer.new(timeout(env), timeout_error)
52
+ else
53
+ raise "BUG: #{run} is not supported"
54
+ end
55
+ end
56
+
57
+ def timeout_with_resume env, class_name
58
+ case class_name
59
+ when /Coolio/
60
+ f = Fiber.current
61
+ timer = CoolioTimer.new(timeout(env))
62
+ error = timer.error = timeout_error
63
+ timer.on_timer{ f.resume(error) if f.alive? }
64
+ timer.attach(::Coolio::Loop.default)
65
+ timer
66
+
67
+ when /EmHttpRequest/
68
+ f = Fiber.current
69
+ EventMachineTimer.new(timeout(env), error = timeout_error){
70
+ f.resume(error) if f.alive?
71
+ }
72
+ else
73
+ raise "BUG: #{run} is not supported"
74
+ end
75
+ end
76
+
77
+ def timeout_error
78
+ ::Timeout::Error.new('execution expired')
79
+ end
80
+
81
+ autoload :CoolioTimer,
82
+ 'rest-core/middleware/timeout/coolio_timer'
83
+ autoload :EventMachineTimer,
84
+ 'rest-core/middleware/timeout/eventmachine_timer'
13
85
  end
@@ -37,6 +37,13 @@ module RestCore::Middleware
37
37
  def call env ; app.call(env) ; end
38
38
  def fail env, obj; env.merge(FAIL => (env[FAIL] || []) + [obj]); end
39
39
  def log env, obj; env.merge(LOG => (env[LOG] || []) + [obj]); end
40
+ def run app=app
41
+ if app.respond_to?(:app) && app.app
42
+ run(app.app)
43
+ else
44
+ app
45
+ end
46
+ end
40
47
 
41
48
  module_function
42
49
  def request_uri env
@@ -0,0 +1,35 @@
1
+
2
+ module RestClient
3
+ module AbstractResponse
4
+ # begin patch
5
+ # https://github.com/archiloque/rest-client/pull/103
6
+ remove_method :to_i if method_defined?(:to_i)
7
+ # end patch
8
+
9
+ # Follow a redirection
10
+ def follow_redirection request = nil, result = nil, & block
11
+ url = headers[:location]
12
+ if url !~ /^http/
13
+ url = URI.parse(args[:url]).merge(url).to_s
14
+ end
15
+ args[:url] = url
16
+ if request
17
+ if request.max_redirects == 0
18
+ # begin patch
19
+ # https://github.com/archiloque/rest-client/pull/118
20
+ raise MaxRedirectsReached.new(self, code)
21
+ # end patch
22
+ end
23
+ args[:password] = request.password
24
+ args[:user] = request.user
25
+ args[:headers] = request.headers
26
+ args[:max_redirects] = request.max_redirects - 1
27
+ # pass any cookie set in the result
28
+ if result && result['set-cookie']
29
+ args[:headers][:cookies] = (args[:headers][:cookies] || {}).merge(parse_cookie(result['set-cookie']))
30
+ end
31
+ end
32
+ Request.execute args, &block
33
+ end
34
+ end
35
+ end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module RestCore
3
- VERSION = '0.8.2'
3
+ VERSION = '1.0.0'
4
4
  end