rest-core 0.8.2 → 1.0.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 (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