rest-core 1.0.3 → 2.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 (87) hide show
  1. data/.travis.yml +6 -7
  2. data/CHANGES.md +137 -0
  3. data/Gemfile +1 -1
  4. data/README.md +183 -191
  5. data/TODO.md +5 -8
  6. data/example/multi.rb +31 -24
  7. data/example/simple.rb +28 -0
  8. data/example/use-cases.rb +194 -0
  9. data/lib/rest-core.rb +26 -19
  10. data/lib/rest-core/builder.rb +2 -2
  11. data/lib/rest-core/client.rb +40 -27
  12. data/lib/rest-core/client/universal.rb +16 -13
  13. data/lib/rest-core/client_oauth1.rb +5 -5
  14. data/lib/rest-core/engine/auto.rb +25 -0
  15. data/lib/rest-core/{app → engine}/dry.rb +1 -2
  16. data/lib/rest-core/engine/em-http-request.rb +39 -0
  17. data/lib/rest-core/engine/future/future.rb +106 -0
  18. data/lib/rest-core/engine/future/future_fiber.rb +39 -0
  19. data/lib/rest-core/engine/future/future_thread.rb +29 -0
  20. data/lib/rest-core/engine/rest-client.rb +56 -0
  21. data/lib/rest-core/middleware.rb +27 -5
  22. data/lib/rest-core/middleware/auth_basic.rb +5 -5
  23. data/lib/rest-core/middleware/bypass.rb +2 -2
  24. data/lib/rest-core/middleware/cache.rb +67 -54
  25. data/lib/rest-core/middleware/common_logger.rb +5 -8
  26. data/lib/rest-core/middleware/default_headers.rb +2 -2
  27. data/lib/rest-core/middleware/default_payload.rb +26 -2
  28. data/lib/rest-core/middleware/default_query.rb +4 -2
  29. data/lib/rest-core/middleware/default_site.rb +8 -6
  30. data/lib/rest-core/middleware/error_detector.rb +9 -16
  31. data/lib/rest-core/middleware/error_handler.rb +25 -11
  32. data/lib/rest-core/middleware/follow_redirect.rb +11 -14
  33. data/lib/rest-core/middleware/json_request.rb +19 -0
  34. data/lib/rest-core/middleware/json_response.rb +28 -0
  35. data/lib/rest-core/middleware/oauth1_header.rb +2 -7
  36. data/lib/rest-core/middleware/oauth2_header.rb +4 -7
  37. data/lib/rest-core/middleware/oauth2_query.rb +2 -2
  38. data/lib/rest-core/middleware/timeout.rb +21 -65
  39. data/lib/rest-core/middleware/timeout/{eventmachine_timer.rb → timer_em.rb} +3 -1
  40. data/lib/rest-core/middleware/timeout/timer_thread.rb +36 -0
  41. data/lib/rest-core/patch/multi_json.rb +8 -0
  42. data/lib/rest-core/test.rb +3 -12
  43. data/lib/rest-core/util/json.rb +65 -0
  44. data/lib/rest-core/util/parse_query.rb +2 -2
  45. data/lib/rest-core/version.rb +1 -1
  46. data/lib/rest-core/wrapper.rb +16 -16
  47. data/rest-core.gemspec +28 -27
  48. data/test/test_auth_basic.rb +14 -10
  49. data/test/test_builder.rb +7 -7
  50. data/test/test_cache.rb +126 -37
  51. data/test/test_client.rb +3 -1
  52. data/test/test_client_oauth1.rb +2 -3
  53. data/test/test_default_query.rb +17 -23
  54. data/test/test_em_http_request.rb +146 -0
  55. data/test/test_error_detector.rb +0 -1
  56. data/test/test_error_handler.rb +44 -0
  57. data/test/test_follow_redirect.rb +17 -19
  58. data/test/test_json_request.rb +28 -0
  59. data/test/test_json_response.rb +51 -0
  60. data/test/test_oauth1_header.rb +4 -4
  61. data/test/test_payload.rb +20 -12
  62. data/test/test_simple.rb +14 -0
  63. data/test/test_timeout.rb +11 -19
  64. data/test/test_universal.rb +5 -5
  65. data/test/test_wrapper.rb +19 -13
  66. metadata +28 -29
  67. data/doc/ToC.md +0 -7
  68. data/doc/dependency.md +0 -4
  69. data/doc/design.md +0 -4
  70. data/example/auto.rb +0 -51
  71. data/example/coolio.rb +0 -21
  72. data/example/eventmachine.rb +0 -30
  73. data/example/rest-client.rb +0 -16
  74. data/lib/rest-core/app/abstract/async_fiber.rb +0 -13
  75. data/lib/rest-core/app/auto.rb +0 -23
  76. data/lib/rest-core/app/coolio-async.rb +0 -32
  77. data/lib/rest-core/app/coolio-fiber.rb +0 -30
  78. data/lib/rest-core/app/coolio.rb +0 -9
  79. data/lib/rest-core/app/em-http-request-async.rb +0 -37
  80. data/lib/rest-core/app/em-http-request-fiber.rb +0 -45
  81. data/lib/rest-core/app/em-http-request.rb +0 -9
  82. data/lib/rest-core/app/rest-client.rb +0 -41
  83. data/lib/rest-core/middleware/json_decode.rb +0 -93
  84. data/lib/rest-core/middleware/timeout/coolio_timer.rb +0 -10
  85. data/pending/test_multi.rb +0 -123
  86. data/pending/test_test_util.rb +0 -86
  87. data/test/test_json_decode.rb +0 -24
@@ -1,36 +1,40 @@
1
1
 
2
2
  require 'rest-core/test'
3
3
 
4
- describe RestCore::AuthBasic do
4
+ describe RC::AuthBasic do
5
5
  before do
6
- @auth = RestCore::AuthBasic.new(RestCore::Dry.new, nil, nil)
6
+ @auth = RC::AuthBasic.new(RC::Dry.new, nil, nil)
7
7
  end
8
8
 
9
9
  should 'do nothing' do
10
- @auth.call({}).should.eq({})
10
+ @auth.call({}){ |res| res.should.eq({}) }
11
11
  end
12
12
 
13
13
  should 'send Authorization header' do
14
14
  @auth.instance_eval{@username = 'Aladdin'}
15
15
  @auth.instance_eval{@password = 'open sesame'}
16
16
 
17
- @auth.call({}).should.eq({RestCore::REQUEST_HEADERS =>
18
- {'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}})
17
+ @auth.call({}){ |res|
18
+ res.should.eq({RC::REQUEST_HEADERS =>
19
+ {'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}})
20
+ }
19
21
 
20
22
  acc = {'Accept' => 'text/plain'}
21
- env = {RestCore::REQUEST_HEADERS => acc}
23
+ env = {RC::REQUEST_HEADERS => acc}
22
24
 
23
- @auth.call(env).should.eq({RestCore::REQUEST_HEADERS =>
24
- {'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}.merge(acc)})
25
+ @auth.call(env){ |res|
26
+ res.should.eq({RC::REQUEST_HEADERS =>
27
+ {'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}.merge(acc)})
28
+ }
25
29
  end
26
30
 
27
31
  should 'leave a log if username are not both provided' do
28
32
  @auth.instance_eval{@username = 'Aladdin'}
29
- @auth.call({})[RestCore::LOG].size.should.eq 1
33
+ @auth.call({}){ |res| res[RC::LOG].size.should.eq 1 }
30
34
  end
31
35
 
32
36
  should 'leave a log if password are not both provided' do
33
37
  @auth.instance_eval{@password = 'open sesame'}
34
- @auth.call({})[RestCore::LOG].size.should.eq 1
38
+ @auth.call({}){ |res| res[RC::LOG].size.should.eq 1 }
35
39
  end
36
40
  end
@@ -1,14 +1,14 @@
1
1
 
2
2
  require 'rest-core/test'
3
3
 
4
- describe RestCore::Builder do
5
- should 'default app is RestCore::RestClient' do
6
- RestCore::Builder.client.new.app.class.should.eq RestCore::RestClient
4
+ describe RC::Builder do
5
+ should 'default app is RestCore::Auto' do
6
+ RC::Builder.client.new.app.class.should.eq RC::Auto
7
7
  end
8
8
 
9
- should 'switch default_app to RestCore::Dry' do
10
- builder = RestCore::Builder.dup
11
- builder.default_app = RestCore::Dry
12
- builder.client.new.app.class.should.eq RestCore::Dry
9
+ should 'switch default_engine to RestCore::Dry' do
10
+ builder = RC::Builder.dup
11
+ builder.default_engine = RC::Dry
12
+ builder.client.new.app.class.should.eq RC::Dry
13
13
  end
14
14
  end
@@ -7,7 +7,7 @@ describe RC::Cache do
7
7
  RR.verify
8
8
  end
9
9
 
10
- should 'basic' do
10
+ should 'basic 0' do
11
11
  c = RC::Builder.client do
12
12
  use RC::Cache, {}, 3600
13
13
  run Class.new{
@@ -17,71 +17,160 @@ describe RC::Cache do
17
17
  end
18
18
  def call env
19
19
  self.tick +=1
20
- env.merge(RC::RESPONSE_BODY => 'response')
20
+ yield(env.merge(RC::RESPONSE_BODY => 'response',
21
+ RC::RESPONSE_HEADERS => {'A' => 'B'},
22
+ RC::RESPONSE_STATUS => 200))
21
23
  end
22
24
  }
23
25
  end.new
24
26
  c.get('/')
25
- c.cache.should.eq({Digest::MD5.hexdigest('/') => 'response'})
27
+ key = Digest::MD5.hexdigest('get:/:')
28
+ c.cache.should.eq("rest-core:cache:#{key}" => "200\nA: B\n\nresponse")
26
29
  c.app.app.tick.should.eq 1
27
30
  c.get('/')
28
31
  c.app.app.tick.should.eq 1
29
32
  c.cache.clear
30
33
  c.get('/')
31
34
  c.app.app.tick.should.eq 2
35
+ c.head('/').should.eq('A' => 'B')
36
+ c.get('/').should.eq 'response'
37
+ c.request({RC::REQUEST_PATH => '/'}, RC::RESPONSE_STATUS).should.eq 200
32
38
  end
33
39
 
34
- should 'cancel timeout for fiber' do
35
- any_instance_of(RC::Timeout::EventMachineTimer) do |timer|
36
- proxy.mock(timer).cancel.times(2)
40
+ should 'basic 1' do
41
+ path = 'http://a'
42
+ stub_request(:get , path).to_return(:body => 'OK')
43
+ stub_request(:post, path).to_return(:body => 'OK')
44
+ c = RC::Builder.client do
45
+ use RC::Cache, nil, nil
37
46
  end
38
- path = 'http://example.com/'
39
- stub_request(:get, path).to_return(:body => 'response')
47
+
48
+ c.new . get(path).should.eq('OK')
49
+ c.new(:cache => (h={})).post(path).should.eq('OK')
50
+ h.should.eq({})
51
+ c.new(:cache => (h={})). get(path).should.eq('OK')
52
+ h.size.should.eq 1
53
+ c.new(:cache => (h={})). get(path, {}, :cache => false).should.eq('OK')
54
+ h.should.eq({})
55
+ c.new . get(path, {}, 'cache.update' => true).
56
+ should.eq('OK')
57
+ end
58
+
59
+ should 'not raise error if headers is nil' do
60
+ path = 'http://a'
61
+ stub_request(:get , path).to_return(:body => 'OK', :headers => nil)
40
62
  c = RC::Builder.client do
41
- use RC::Timeout, 10
42
- use RC::Cache, {}, 3600
43
- run RC::EmHttpRequestFiber
63
+ use RC::Cache, {}, nil
44
64
  end.new
45
- EM.run{ Fiber.new{
46
- c.request_full(RC::REQUEST_PATH => path)
47
- c.request_full(RC::REQUEST_PATH => path)
48
- EM.stop }.resume }
49
- c.cache.size.should.eq 1
50
- end if defined?(Fiber)
51
-
52
- should 'cancel timeout for async' do
53
- path = 'http://example.com/'
54
- any_instance_of(RC::Timeout::EventMachineTimer) do |timer|
55
- mock(timer).cancel.times(2)
56
- end
57
- stub_request(:get, path).to_return(:body => 'response')
65
+ c.get(path).should.eq 'OK'
66
+ c.get(path).should.eq 'OK'
67
+ end
68
+
69
+ should 'head then get' do
58
70
  c = RC::Builder.client do
59
- use RC::Timeout, 10
60
- use RC::Cache, {}, 3600
61
- run RC::EmHttpRequestAsync
71
+ use RC::Cache, {}, nil
62
72
  end.new
63
- EM.run{
64
- c.request_full(RC::REQUEST_PATH => path){
65
- c.request_full(RC::REQUEST_PATH => path){
66
- EM.stop }}}
67
- c.cache.size.should.eq 1
73
+ path = 'http://example.com'
74
+ stub_request(:head, path).to_return(:headers => {'A' => 'B'})
75
+ c.head(path).should.eq('A' => 'B')
76
+ stub_request(:get , path).to_return(:body => 'body')
77
+ c.get(path).should.eq('body')
68
78
  end
69
79
 
70
80
  should 'only [] and []= should be implemented' do
71
81
  cache = Class.new do
72
- def initialize ; @h = {} ; end
73
- def [] key ; @h[key] ; end
74
- def []= key, value; @h[key] = value * 2; end
82
+ def initialize ; @h = {} ; end
83
+ def [] key ; @h[key] ; end
84
+ def []= key, value; @h[key] = value.sub('4', '5'); end
75
85
  end.new
76
86
  c = RC::Builder.client do
77
87
  use RC::Cache, cache, 0
78
88
  run Class.new{
79
89
  def call env
80
- env.merge(RC::RESPONSE_BODY => env[RC::REQUEST_PATH])
90
+ yield(env.merge(RC::RESPONSE_BODY => env[RC::REQUEST_PATH],
91
+ RC::RESPONSE_STATUS => 200))
81
92
  end
82
93
  }
83
94
  end.new
84
95
  c.get('4')
85
- c.get('4').should.eq '44'
96
+ c.get('4').should.eq '5'
97
+ end
98
+
99
+ should 'cache the original response' do
100
+ c = RC::Builder.client do
101
+ use RC::Cache, {}, 3600 do
102
+ use RC::JsonResponse, true
103
+ end
104
+ end.new
105
+ stub_request(:get, 'me').to_return(:body => body = '{"a":"b"}')
106
+ c.get('me').should.eq 'a' => 'b'
107
+ c.cache.values.first.should.eq "200\n\n\n#{body}"
108
+ end
109
+
110
+ should "follow redirect with cache.update correctly" do
111
+ c = RC::Builder.client do
112
+ use RC::FollowRedirect, 10
113
+ use RC::Cache, {}, nil
114
+ end.new
115
+ x, y, z = 'http://X', 'http://Y', 'http://Z'
116
+ stub_request(:get, x).to_return(:headers => {'Location' => y},
117
+ :status => 301)
118
+ stub_request(:get, y).to_return(:headers => {'Location' => z},
119
+ :status => 302)
120
+ stub_request(:get, z).to_return(:body => 'OK')
121
+ c.get(x, {}, 'cache.update' => true).should.eq 'OK'
122
+ end
123
+
124
+ should 'not cache post/put/delete' do
125
+ [:put, :post, :delete].each{ |meth|
126
+ url, body = "https://cache", 'ok'
127
+ stub_request(meth, url).to_return(:body => body).times(3)
128
+
129
+ cache = {}
130
+ c = RC::Builder.client{use RC::Cache, cache, nil}.new
131
+ 3.times{
132
+ if meth == :delete
133
+ c.send(meth, url).should.eq(body)
134
+ else
135
+ c.send(meth, url, 'payload').should.eq(body)
136
+ end
137
+ }
138
+ cache.should.eq({})
139
+ }
140
+ end
141
+
142
+ should 'update cache if there is cache option set to false' do
143
+ url, body = "https://cache", 'ok'
144
+ stub_request(:get, url).to_return(:body => body)
145
+ c = RC::Builder.client{use RC::Cache, {}, nil}.new
146
+
147
+ c.get(url) .should.eq body
148
+ stub_request(:get, url).to_return(:body => body.reverse).times(2)
149
+ c.get(url) .should.eq body
150
+ c.get(url, {}, 'cache.update' => true).should.eq body.reverse
151
+ c.get(url) .should.eq body.reverse
152
+ c.cache = nil
153
+ c.get(url, {}, 'cache.update' => true).should.eq body.reverse
154
+ end
155
+
156
+ describe 'expires_in' do
157
+ before do
158
+ @url, @body = "https://cache", 'ok'
159
+ stub_request(:get, @url).to_return(:body => @body)
160
+ @cache = {}
161
+ mock(@cache).method(:store){ mock!.arity{ -3 } }
162
+ mock(@cache).store(is_a(String), is_a(String), :expires_in => 3)
163
+ @cache
164
+ end
165
+
166
+ should 'respect in options' do
167
+ c = RC::Builder.client{use RC::Cache, nil, nil}.new
168
+ c.get(@url, {}, :cache => @cache, :expires_in => 3).should.eq @body
169
+ end
170
+
171
+ should 'respect in middleware' do
172
+ c = RC::Builder.client{use RC::Cache, nil, 3}.new(:cache => @cache)
173
+ c.get(@url).should.eq @body
174
+ end
86
175
  end
87
176
  end
@@ -24,12 +24,14 @@ describe RC::Simple do
24
24
  stub_request(method, url).to_return(:body => '123')
25
25
  (client = RC::Simple.new).send(method, url){ |res|
26
26
  res.should.eq '123' }.should.eq client
27
+ client.wait
27
28
  end
28
29
 
29
30
  stub_request(:head, url).to_return(:headers => {'A' => 'B'})
30
31
  (client = RC::Simple.new).head(url){ |res|
31
32
  res.should.eq({'A' => 'B'})
32
33
  }.should.eq client
34
+ client.wait
33
35
  end
34
36
 
35
37
  should 'have correct to_i' do
@@ -38,7 +40,7 @@ describe RC::Simple do
38
40
  end
39
41
 
40
42
  should 'use defaults' do
41
- client = RestCore::Builder.client do
43
+ client = RC::Builder.client do
42
44
  use RC::Timeout, 4
43
45
  end
44
46
  c = client.new
@@ -17,7 +17,7 @@ describe RC::ClientOauth1 do
17
17
  data = {'a' => 'b', 'c' => 'd'}
18
18
  sig = Digest::MD5.hexdigest('e&a=b&c=d')
19
19
  data_sig = data.merge('sig' => sig)
20
- data_json = RC::JsonDecode.json_encode(data_sig)
20
+ data_json = RC::Json.encode(data_sig)
21
21
  @client = client.new(:data => data, :consumer_secret => 'e')
22
22
 
23
23
  @client.send(:calculate_sig).should.eq sig
@@ -26,8 +26,7 @@ describe RC::ClientOauth1 do
26
26
  @client.data_json = data_json
27
27
  @client.data.should.eq data_sig
28
28
 
29
- @client.data_json = RC::JsonDecode.json_encode(
30
- data_sig.merge('sig' => 'wrong'))
29
+ @client.data_json = RC::Json.encode(data_sig.merge('sig' => 'wrong'))
31
30
  @client.data.should.eq({})
32
31
 
33
32
  @client.data_json = data_json
@@ -2,44 +2,38 @@
2
2
  require 'rest-core/test'
3
3
 
4
4
  describe RC::DefaultQuery do
5
- describe 'when given query' do
6
- before do
7
- @app = RC::DefaultQuery.new(RC::Dry.new, {})
8
- end
9
-
10
- def app
11
- @app
12
- end
5
+ before do
6
+ @app = RC::DefaultQuery.new(RC::Dry.new, {})
7
+ end
13
8
 
9
+ describe 'when given query' do
14
10
  should 'do nothing' do
15
- app.call({})[RC::REQUEST_QUERY].should.eq({})
11
+ @app.call({}){ |r| r[RC::REQUEST_QUERY].should.eq({}) }
16
12
  end
17
13
 
18
14
  should 'merge query' do
19
- app.instance_eval{@query = {'q' => 'uery'}}
15
+ @app.instance_eval{@query = {'q' => 'uery'}}
20
16
 
21
- app.call({}).should.eq({RC::REQUEST_QUERY =>
22
- {'q' => 'uery'}})
17
+ @app.call({}){ |r| r.should.eq({RC::REQUEST_QUERY => {'q' => 'uery'}}) }
23
18
 
24
19
  format = {'format' => 'json'}
25
20
  env = {RC::REQUEST_QUERY => format}
26
21
 
27
- app.call(env).should.eq({RC::REQUEST_QUERY =>
28
- {'q' => 'uery'}.merge(format)})
29
- end
30
- end
31
-
32
- describe 'when not given query' do
33
- before do
34
- @app = RC::DefaultQuery.new(RC::Dry.new)
22
+ @app.call(env){ |r|
23
+ r.should.eq({RC::REQUEST_QUERY => {'q' => 'uery'}.merge(format)}) }
35
24
  end
36
25
 
37
- def app
38
- @app
26
+ should 'string_keys in query' do
27
+ env = {'query' => {:symbol => 'value'}}
28
+ @app.call(env){ |r|
29
+ r.should.eq({RC::REQUEST_QUERY => {'symbol' => 'value'}}.merge(env))
30
+ }
39
31
  end
32
+ end
40
33
 
34
+ describe 'when not given query' do
41
35
  should 'merge query with {}' do
42
- app.call({}).should.eq({RC::REQUEST_QUERY => {}})
36
+ @app.call({}){ |r| r.should.eq({RC::REQUEST_QUERY => {}}) }
43
37
  end
44
38
  end
45
39
  end
@@ -0,0 +1,146 @@
1
+
2
+ require 'rest-core/test'
3
+
4
+ describe RC::EmHttpRequest do
5
+ should 'raise RC::Error' do
6
+ EM.run{Fiber.new{
7
+ lambda{
8
+ RC::Universal.new.get('http://localhost:1').tap{}
9
+ }.should.raise(RC::Error)
10
+ EM.stop
11
+ }.resume}
12
+ end
13
+
14
+ should 'never crash EM!' do
15
+ EM.error_handler{ |e| e.should.kind_of?(NoMethodError); EM.stop}
16
+ EM.run{Fiber.new{
17
+ RC::Simple.new.get('http://localhost:1').no_such_method
18
+ }.resume}
19
+ end
20
+
21
+ # ----------------------------------------------------------------------
22
+
23
+ describe RC::Simple do
24
+ before do
25
+ @path = 'http://example.com'
26
+ stub_request(:get, @path).to_return(:body => 'OK')
27
+ end
28
+
29
+ should 'work with EM' do
30
+ EM.run{Fiber.new{
31
+ RC::Simple.new.get(@path).should.eq 'OK'; EM.stop}.resume}
32
+ end
33
+ end
34
+
35
+ # ----------------------------------------------------------------------
36
+
37
+ describe RC::Timeout do
38
+ after do
39
+ WebMock.reset!
40
+ RR.verify
41
+ end
42
+
43
+ should 'cancel timeout for fiber' do
44
+ any_instance_of(RC::Timeout::TimerEm) do |timer|
45
+ proxy.mock(timer).cancel.times(2)
46
+ end
47
+ path = 'http://example.com/'
48
+ stub_request(:get, path).to_return(:body => 'response')
49
+ c = RC::Builder.client do
50
+ use RC::Timeout, 10
51
+ use RC::Cache, {}, 3600
52
+ run RC::EmHttpRequest
53
+ end.new
54
+ EM.run{ Fiber.new{
55
+ c.request(RC::REQUEST_PATH => path).should.eq 'response'
56
+ c.request(RC::REQUEST_PATH => path).should.eq 'response'
57
+ EM.stop }.resume }
58
+ c.cache.size.should.eq 1
59
+ end
60
+
61
+ should 'cancel timeout for async' do
62
+ path = 'http://example.com/'
63
+ any_instance_of(RC::Timeout::TimerEm) do |timer|
64
+ mock(timer).cancel.times(2)
65
+ end
66
+ stub_request(:get, path).to_return(:body => 'response')
67
+ c = RC::Builder.client do
68
+ use RC::Timeout, 10
69
+ use RC::Cache, {}, 3600
70
+ run RC::EmHttpRequest
71
+ end.new
72
+ EM.run{
73
+ c.request_full(RC::REQUEST_PATH => path){
74
+ c.request_full(RC::REQUEST_PATH => path){
75
+ EM.stop }}}
76
+ c.cache.size.should.eq 1
77
+ end
78
+
79
+ should 'return correct result for futures' do
80
+ path = 'http://example.com/'
81
+ stub_request(:get, path).to_return(:body => 'response')
82
+
83
+ c = RC::Builder.client do
84
+ use RC::Timeout, 10
85
+ run RC::EmHttpRequest
86
+ end.new
87
+ EM.run{Fiber.new{c.get(path).should.eq('response');EM.stop}.resume}
88
+ end
89
+
90
+ describe 'raise exception' do
91
+ should 'default timeout' do
92
+ c = RC::Builder.client do
93
+ use RC::Timeout, 0.00001
94
+ run Class.new{
95
+ def call env
96
+ sleep 1
97
+ yield(env)
98
+ end
99
+ }
100
+ end.new
101
+ lambda{ c.get('') }.should.raise ::Timeout::Error
102
+ end
103
+
104
+ should 'future timeout' do
105
+ port = 35795
106
+ path = "http://localhost:#{port}/"
107
+
108
+ c = RC::Builder.client do
109
+ use RC::Timeout, 0.00001
110
+ run RC::EmHttpRequest
111
+ end.new
112
+
113
+ EM.run{
114
+ EM.start_server '127.0.0.1', port, Module.new{
115
+ def receive_data data; end
116
+ }
117
+ Fiber.new{
118
+ begin
119
+ c.get(path).tap{}
120
+ rescue => e
121
+ e.should.kind_of ::Timeout::Error
122
+ EM.stop
123
+ end
124
+ }.resume}
125
+ end
126
+
127
+ should 'async timeout' do
128
+ port = 35795
129
+ path = "http://localhost:#{port}/"
130
+
131
+ c = RC::Builder.client do
132
+ use RC::Timeout, 0.00001
133
+ use RC::ErrorHandler
134
+ run RC::EmHttpRequest
135
+ end.new
136
+
137
+ EM.run{
138
+ EM.start_server '127.0.0.1', port, Module.new{
139
+ def receive_data data; end
140
+ }
141
+ c.get(path){ |e| e.should.kind_of ::Timeout::Error; EM.stop }
142
+ }
143
+ end
144
+ end
145
+ end
146
+ end unless RUBY_ENGINE == 'jruby'