rest-core 3.1.1 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +59 -0
- data/README.md +28 -1
- data/TODO.md +0 -4
- data/lib/rest-core.rb +5 -0
- data/lib/rest-core/builder.rb +17 -0
- data/lib/rest-core/client/universal.rb +5 -2
- data/lib/rest-core/engine.rb +1 -6
- data/lib/rest-core/middleware/cache.rb +16 -18
- data/lib/rest-core/middleware/clash_response.rb +21 -0
- data/lib/rest-core/middleware/json_response.rb +12 -4
- data/lib/rest-core/middleware/smash_response.rb +21 -0
- data/lib/rest-core/promise.rb +24 -4
- data/lib/rest-core/thread_pool.rb +4 -0
- data/lib/rest-core/timer.rb +5 -1
- data/lib/rest-core/util/clash.rb +50 -0
- data/lib/rest-core/util/parse_link.rb +16 -0
- data/lib/rest-core/util/smash.rb +41 -0
- data/lib/rest-core/version.rb +1 -1
- data/rest-core.gemspec +18 -3
- data/test/test_cache.rb +45 -17
- data/test/test_clash.rb +24 -0
- data/test/test_clash_response.rb +56 -0
- data/test/test_client.rb +8 -3
- data/test/test_json_response.rb +20 -2
- data/test/test_parse_link.rb +43 -0
- data/test/test_promise.rb +11 -1
- data/test/test_smash.rb +24 -0
- data/test/test_smash_response.rb +56 -0
- data/test/test_universal.rb +9 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbe0fe23ff10ec39cf8a2b367616b1c332bd0d42
|
4
|
+
data.tar.gz: 0a7132a6620ecab1c35fd69fdff24733161aafad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d59288134f72ed58c335e743e439479d93de99f6c4278d871733ec845790d65cede5986e276f0a8cd0f0aa39f1cf2ee38325ef468e74cbcb4a08bf3cb01ed2cc
|
7
|
+
data.tar.gz: 73a39d9b95793cc496d05e6c9370e0da5bd7c3ae22374e3ebe4ebc71d3dfd0d2778f885a9b8395fe1c8d38fd143b767df45d57ccbfcb6ae957116a8139ba2c41
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,64 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## rest-core 3.2.0 -- 2014-06-27
|
4
|
+
|
5
|
+
### Enhancements
|
6
|
+
|
7
|
+
* Introduced `RC::JsonResponse::ParseError` which would be a subclass of
|
8
|
+
`RC::Json::ParseError`, and contain the original text before parsing.
|
9
|
+
This should be great for debugging. For example, some servers might
|
10
|
+
return HTML instead JSON, and we would like to read the HTML to learn
|
11
|
+
more why they are doing this.
|
12
|
+
|
13
|
+
* Introduced `RC::ParseLink` utility for parsing web links described in
|
14
|
+
[RFC5988](http://tools.ietf.org/html/rfc5988)
|
15
|
+
|
16
|
+
* Introduced `RC::Clash` which is a hash wrapper protecting you from getting
|
17
|
+
nils from hashes. This is useful whenever we want to access a value deeply
|
18
|
+
inside a hash. For example: `json['a']['b']['c']['d']` would never fail
|
19
|
+
due to nils. Note that `RC::Clash` is recursive.
|
20
|
+
|
21
|
+
* Introduced `RC::Smash` which is a hash wrapper protecting you from getting
|
22
|
+
nils from hashes. This is useful whenever we want to access a value deeply
|
23
|
+
inside a hash. Instead of using multiple layers of subscript operators,
|
24
|
+
we try to use a "path" to specify which value we want. For example:
|
25
|
+
`json['a', 'b', 'c', 'd']` is the same as `json['a']['b']['c']['d']` but
|
26
|
+
with protection from nils in the middle. Note that `RC:Smash` is *not*
|
27
|
+
recursive.
|
28
|
+
|
29
|
+
* Introduced `RC::ClashResponse` which would wrap the response inside
|
30
|
+
`RC::Clash`. This is useful along with `RC::JsonResponse`.
|
31
|
+
|
32
|
+
* Introduced `RC::SmashResponse` which would wrap the response inside
|
33
|
+
`RC::Smash`. This is useful along with `RC::JsonResponse`.
|
34
|
+
|
35
|
+
* Introduced `RC::Client.shutdown` which is essentially the same as
|
36
|
+
`RC::Client.thread_pool.shutdown` and `RC::Client.wait`.
|
37
|
+
|
38
|
+
* `RC::ClashResponse` and `RC::SmashResponse` is added into `RC::Universal`
|
39
|
+
with `{:clash_response => false, :smash_response => false}` by default.
|
40
|
+
|
41
|
+
* Introduced `RC::Promise#future_response` to allow you customize the
|
42
|
+
behaviour of promises more easily.
|
43
|
+
|
44
|
+
* Introduced `RC::Promise.claim` to allow you pre-fill a promise.
|
45
|
+
|
46
|
+
* Introduced `RC::Promise#then` to allow you append a callback whenever
|
47
|
+
the response is ready. The type should be: `Env -> Response`
|
48
|
+
|
49
|
+
* Now `RC::Promise#inspect` would show REQUEST_URI instead of REQUEST_PATH,
|
50
|
+
which should be easier to debug.
|
51
|
+
|
52
|
+
* Introduced `RC::ThreadPool#size` which is a short hand for
|
53
|
+
`RC::ThreadPool#workers.size`.
|
54
|
+
|
55
|
+
### Bugs fixed
|
56
|
+
|
57
|
+
* Inheritance with `RC::Client` now works properly.
|
58
|
+
* Now `RC::Cache` properly return cached headers.
|
59
|
+
* Now `RC::Cache` would work more like `RC::Engine`, wrapping responses
|
60
|
+
inside futures.
|
61
|
+
|
3
62
|
## rest-core 3.1.1 -- 2014-05-13
|
4
63
|
|
5
64
|
### Enhancements
|
data/README.md
CHANGED
@@ -225,6 +225,26 @@ a large number of concurrent calls.
|
|
225
225
|
Also, setting `pool_size` to `-1` would mean we want to make blocking
|
226
226
|
requests, without spawning any threads. This might be useful for debugging.
|
227
227
|
|
228
|
+
### Gracefully shutdown
|
229
|
+
|
230
|
+
To shutdown gracefully, consider shutdown the thread pool (if we're using it),
|
231
|
+
and wait for all requests for a given client. For example, suppose we're using
|
232
|
+
`RC::Universal`, we'll do this when we're shutting down:
|
233
|
+
|
234
|
+
``` ruby
|
235
|
+
RC::Universal.shutdown
|
236
|
+
```
|
237
|
+
|
238
|
+
We could put them in `at_exit` callback like this:
|
239
|
+
|
240
|
+
``` ruby
|
241
|
+
at_exit do
|
242
|
+
RC::Universal.shutdown
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
If you're using unicorn, you probably want to put that in the config.
|
247
|
+
|
228
248
|
### Persistent connections (keep-alive connections)
|
229
249
|
|
230
250
|
Since we're using [httpclient][] by default now, we would reuse connections,
|
@@ -396,7 +416,10 @@ module RestCore
|
|
396
416
|
# cache {} didn't support it
|
397
417
|
use ErrorHandler, nil
|
398
418
|
use ErrorDetectorHttp
|
399
|
-
use
|
419
|
+
use SmashResponse, false
|
420
|
+
use ClashResponse, false
|
421
|
+
use JsonResponse, false
|
422
|
+
use QueryResponse, false
|
400
423
|
end
|
401
424
|
end
|
402
425
|
end
|
@@ -493,6 +516,7 @@ simply ignore `:expires_in`.
|
|
493
516
|
[RC::AuthBasic]: lib/rest-core/middleware/auth_basic.rb
|
494
517
|
[RC::Bypass]: lib/rest-core/middleware/bypass.rb
|
495
518
|
[RC::Cache]: lib/rest-core/middleware/cache.rb
|
519
|
+
[RC::ClashResponse]: lib/rest-core/middleware/clash_response.rb
|
496
520
|
[RC::CommonLogger]: lib/rest-core/middleware/common_logger.rb
|
497
521
|
[RC::DefaultHeaders]: lib/rest-core/middleware/default_headers.rb
|
498
522
|
[RC::DefaultPayload]: lib/rest-core/middleware/default_payload.rb
|
@@ -508,6 +532,7 @@ simply ignore `:expires_in`.
|
|
508
532
|
[RC::Oauth1Header]: lib/rest-core/middleware/oauth1_header.rb
|
509
533
|
[RC::Oauth2Header]: lib/rest-core/middleware/oauth2_header.rb
|
510
534
|
[RC::Oauth2Query]: lib/rest-core/middleware/oauth2_query.rb
|
535
|
+
[RC::SmashResponse]: lib/rest-core/middleware/smash_response.rb
|
511
536
|
[RC::Timeout]: lib/rest-core/middleware/timeout.rb
|
512
537
|
[moneta]: https://github.com/minad/moneta#expiration
|
513
538
|
|
@@ -663,6 +688,7 @@ and complete.
|
|
663
688
|
|
664
689
|
## rest-core users:
|
665
690
|
|
691
|
+
* [rest-firebase](https://github.com/CodementorIO/rest-firebase)
|
666
692
|
* [rest-more][]
|
667
693
|
* [rest-more-yahoo_buy](https://github.com/GoodLife/rest-more-yahoo_buy)
|
668
694
|
* [s2sync](https://github.com/brucehsu/s2sync)
|
@@ -671,6 +697,7 @@ and complete.
|
|
671
697
|
|
672
698
|
## Powered sites:
|
673
699
|
|
700
|
+
* [Codementor](https://www.codementor.io/)
|
674
701
|
* [PicCollage](http://pic-collage.com/)
|
675
702
|
|
676
703
|
## CHANGES:
|
data/TODO.md
CHANGED
data/lib/rest-core.rb
CHANGED
@@ -40,14 +40,19 @@ module RestCore
|
|
40
40
|
# misc utilities
|
41
41
|
autoload :Hmac , 'rest-core/util/hmac'
|
42
42
|
autoload :Json , 'rest-core/util/json'
|
43
|
+
autoload :ParseLink , 'rest-core/util/parse_link'
|
43
44
|
autoload :ParseQuery , 'rest-core/util/parse_query'
|
44
45
|
autoload :Payload , 'rest-core/util/payload'
|
45
46
|
autoload :Config , 'rest-core/util/config'
|
47
|
+
autoload :Clash , 'rest-core/util/clash'
|
48
|
+
autoload :Smash , 'rest-core/util/smash'
|
46
49
|
|
47
50
|
# middlewares
|
48
51
|
autoload :AuthBasic , 'rest-core/middleware/auth_basic'
|
49
52
|
autoload :Bypass , 'rest-core/middleware/bypass'
|
50
53
|
autoload :Cache , 'rest-core/middleware/cache'
|
54
|
+
autoload :ClashResponse , 'rest-core/middleware/clash_response'
|
55
|
+
autoload :SmashResponse , 'rest-core/middleware/smash_response'
|
51
56
|
autoload :CommonLogger , 'rest-core/middleware/common_logger'
|
52
57
|
autoload :DefaultHeaders, 'rest-core/middleware/default_headers'
|
53
58
|
autoload :DefaultQuery , 'rest-core/middleware/default_query'
|
data/lib/rest-core/builder.rb
CHANGED
@@ -45,6 +45,16 @@ class RestCore::Builder
|
|
45
45
|
Module.new do
|
46
46
|
attr_accessor :builder, :pool_size, :pool_idle_time,
|
47
47
|
:event_source_class, :promises, :mutex
|
48
|
+
|
49
|
+
def inherited sub
|
50
|
+
sub.builder = builder
|
51
|
+
sub.pool_size = pool_size
|
52
|
+
sub.pool_idle_time = pool_idle_time
|
53
|
+
sub.event_source_class = event_source_class
|
54
|
+
sub.promises = []
|
55
|
+
sub.mutex = Mutex.new
|
56
|
+
end
|
57
|
+
|
48
58
|
def thread_pool; RestCore::ThreadPool[self]; end
|
49
59
|
|
50
60
|
def give_promise weak_promise, ps=promises, m=mutex
|
@@ -54,6 +64,13 @@ class RestCore::Builder
|
|
54
64
|
end
|
55
65
|
end
|
56
66
|
|
67
|
+
# Shutdown the thread pool for this client and wait for all requests
|
68
|
+
def shutdown
|
69
|
+
thread_pool.shutdown
|
70
|
+
wait
|
71
|
+
end
|
72
|
+
|
73
|
+
# Wait for all the requests to be done for this client
|
57
74
|
def wait ps=promises, m=mutex
|
58
75
|
return self if ps.empty?
|
59
76
|
current_promises = nil
|
@@ -12,10 +12,13 @@ module RestCore
|
|
12
12
|
|
13
13
|
use FollowRedirect, 10
|
14
14
|
use CommonLogger , method(:puts)
|
15
|
-
use Cache , {}, 600 do
|
15
|
+
use Cache , {}, 600 do # default :expires_in 600 but the default
|
16
|
+
# cache {} didn't support it
|
16
17
|
use ErrorHandler, nil
|
17
18
|
use ErrorDetectorHttp
|
18
|
-
use
|
19
|
+
use SmashResponse, false
|
20
|
+
use ClashResponse, false
|
21
|
+
use JsonResponse, false
|
19
22
|
use QueryResponse, false
|
20
23
|
end
|
21
24
|
end
|
data/lib/rest-core/engine.rb
CHANGED
@@ -9,12 +9,7 @@ class RestCore::Engine
|
|
9
9
|
req = env.merge(REQUEST_URI => request_uri(env))
|
10
10
|
promise = Promise.new(req, k, req[ASYNC])
|
11
11
|
promise.defer{ request(promise, req) }
|
12
|
-
|
13
|
-
RESPONSE_STATUS => promise.future_status,
|
14
|
-
RESPONSE_HEADERS => promise.future_headers,
|
15
|
-
RESPONSE_SOCKET => promise.future_socket,
|
16
|
-
FAIL => promise.future_failures,
|
17
|
-
PROMISE => promise)
|
12
|
+
promise.future_response
|
18
13
|
end
|
19
14
|
|
20
15
|
private
|
@@ -22,18 +22,16 @@ class RestCore::Cache
|
|
22
22
|
env
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
cache_get(e){ |cached|
|
26
26
|
e[TIMER].cancel if e[TIMER]
|
27
27
|
wrapped.call(cached, &k)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end)}}
|
36
|
-
end
|
28
|
+
} || app.call(e){ |res|
|
29
|
+
wrapped.call(res){ |res_wrapped|
|
30
|
+
k.call(if (res_wrapped[FAIL] || []).empty?
|
31
|
+
cache_for(res).merge(res_wrapped)
|
32
|
+
else
|
33
|
+
res_wrapped
|
34
|
+
end)}}
|
37
35
|
end
|
38
36
|
|
39
37
|
def cache_key env
|
@@ -41,14 +39,15 @@ class RestCore::Cache
|
|
41
39
|
cache_key_raw(env))}"
|
42
40
|
end
|
43
41
|
|
44
|
-
def cache_get env
|
42
|
+
def cache_get env, &k
|
45
43
|
return unless cache(env)
|
46
44
|
return unless cache_for?(env)
|
47
45
|
|
46
|
+
uri = request_uri(env)
|
48
47
|
start_time = Time.now
|
49
48
|
return unless data = cache(env)[cache_key(env)]
|
50
|
-
log(env, Event::CacheHit.new(Time.now - start_time,
|
51
|
-
merge(data_extract(data))
|
49
|
+
log(env, Event::CacheHit.new(Time.now - start_time, uri)).
|
50
|
+
merge(data_extract(data, env.merge(REQUEST_URI => uri), k))
|
52
51
|
end
|
53
52
|
|
54
53
|
private
|
@@ -96,13 +95,12 @@ class RestCore::Cache
|
|
96
95
|
"#{ res[RESPONSE_BODY]}"
|
97
96
|
end
|
98
97
|
|
99
|
-
def data_extract data
|
98
|
+
def data_extract data, env, k
|
100
99
|
_, status, headers, body =
|
101
|
-
data.match(/\A(\d+)\n([^\n]+\n)
|
100
|
+
data.match(/\A(\d+)\n((?:[^\n]+\n)*)\n\n(.*)\Z/m).to_a
|
102
101
|
|
103
|
-
|
104
|
-
|
105
|
-
RESPONSE_STATUS => status.to_i}
|
102
|
+
Promise.claim(env, k, body,status.to_i,
|
103
|
+
Hash[(headers||'').scan(/([^:]+): ([^\n]+)\n/)]).future_response
|
106
104
|
end
|
107
105
|
|
108
106
|
def cache_for? env
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/middleware'
|
3
|
+
require 'rest-core/util/clash'
|
4
|
+
|
5
|
+
class RestCore::ClashResponse
|
6
|
+
def self.members; [:clash_response]; end
|
7
|
+
include RestCore::Middleware
|
8
|
+
|
9
|
+
def call env, &k
|
10
|
+
return app.call(env, &k) if env[DRY]
|
11
|
+
return app.call(env, &k) unless clash_response(env)
|
12
|
+
|
13
|
+
app.call(env){ |res|
|
14
|
+
if res[RESPONSE_BODY].kind_of?(Hash)
|
15
|
+
yield(res.merge(RESPONSE_BODY => Clash.new(res[RESPONSE_BODY])))
|
16
|
+
else
|
17
|
+
yield(res)
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
@@ -6,6 +6,14 @@ class RestCore::JsonResponse
|
|
6
6
|
def self.members; [:json_response]; end
|
7
7
|
include RestCore::Middleware
|
8
8
|
|
9
|
+
class ParseError < Json.const_get(:ParseError)
|
10
|
+
attr_reader :cause, :body
|
11
|
+
def initialize cause, body
|
12
|
+
super("#{cause.message}\nOriginal text: #{body}")
|
13
|
+
@cause, @body = cause, body
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
9
17
|
JSON_RESPONSE_HEADER = {'Accept' => 'application/json'}.freeze
|
10
18
|
|
11
19
|
def call env, &k
|
@@ -19,10 +27,10 @@ class RestCore::JsonResponse
|
|
19
27
|
end
|
20
28
|
|
21
29
|
def process response
|
22
|
-
response
|
23
|
-
|
24
|
-
|
30
|
+
body = response[RESPONSE_BODY]
|
31
|
+
response.merge(RESPONSE_BODY => Json.decode("[#{body}]").first)
|
32
|
+
# [this].first is not needed for yajl-ruby
|
25
33
|
rescue Json.const_get(:ParseError) => error
|
26
|
-
fail(response, error)
|
34
|
+
fail(response, ParseError.new(error, body))
|
27
35
|
end
|
28
36
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/middleware'
|
3
|
+
require 'rest-core/util/smash'
|
4
|
+
|
5
|
+
class RestCore::SmashResponse
|
6
|
+
def self.members; [:smash_response]; end
|
7
|
+
include RestCore::Middleware
|
8
|
+
|
9
|
+
def call env, &k
|
10
|
+
return app.call(env, &k) if env[DRY]
|
11
|
+
return app.call(env, &k) unless smash_response(env)
|
12
|
+
|
13
|
+
app.call(env){ |res|
|
14
|
+
if res[RESPONSE_BODY].kind_of?(Hash)
|
15
|
+
yield(res.merge(RESPONSE_BODY => Smash.new(res[RESPONSE_BODY])))
|
16
|
+
else
|
17
|
+
yield(res)
|
18
|
+
end
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
data/lib/rest-core/promise.rb
CHANGED
@@ -15,9 +15,15 @@ class RestCore::Promise
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def self.claim env, k=RC.id, body, status, headers
|
19
|
+
promise = new(env, k)
|
20
|
+
promise.fulfill(body, status, headers)
|
21
|
+
promise
|
22
|
+
end
|
23
|
+
|
18
24
|
def initialize env, k=RC.id, immediate=false, &job
|
19
25
|
self.env = env
|
20
|
-
self.k = k
|
26
|
+
self.k = [k]
|
21
27
|
self.immediate = immediate
|
22
28
|
|
23
29
|
self.body, self.status, self.headers,
|
@@ -30,7 +36,7 @@ class RestCore::Promise
|
|
30
36
|
end
|
31
37
|
|
32
38
|
def inspect
|
33
|
-
"<#{self.class.name} for #{env[
|
39
|
+
"<#{self.class.name} for #{env[REQUEST_URI]}>"
|
34
40
|
end
|
35
41
|
|
36
42
|
def future_body ; Future.new(self, RESPONSE_BODY ); end
|
@@ -38,6 +44,14 @@ class RestCore::Promise
|
|
38
44
|
def future_headers ; Future.new(self, RESPONSE_HEADERS); end
|
39
45
|
def future_socket ; Future.new(self, RESPONSE_SOCKET ); end
|
40
46
|
def future_failures; Future.new(self, FAIL) ; end
|
47
|
+
def future_response
|
48
|
+
env.merge(RESPONSE_BODY => future_body,
|
49
|
+
RESPONSE_STATUS => future_status,
|
50
|
+
RESPONSE_HEADERS => future_headers,
|
51
|
+
RESPONSE_SOCKET => future_socket,
|
52
|
+
FAIL => future_failures,
|
53
|
+
PROMISE => self)
|
54
|
+
end
|
41
55
|
|
42
56
|
# called in client thread
|
43
57
|
def defer &job
|
@@ -87,6 +101,12 @@ class RestCore::Promise
|
|
87
101
|
fulfill('', 0, {})
|
88
102
|
end
|
89
103
|
|
104
|
+
# append your actions, which would be called when we're calling back
|
105
|
+
def then &action
|
106
|
+
k << action
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
90
110
|
protected
|
91
111
|
attr_accessor :env, :k, :immediate,
|
92
112
|
:response, :body, :status, :headers, :socket, :error,
|
@@ -107,13 +127,13 @@ class RestCore::Promise
|
|
107
127
|
|
108
128
|
# called in client thread, when yield is called
|
109
129
|
def callback
|
110
|
-
self.response ||= k.
|
130
|
+
self.response ||= k.inject(
|
111
131
|
env.merge(RESPONSE_BODY => body ,
|
112
132
|
RESPONSE_STATUS => status,
|
113
133
|
RESPONSE_HEADERS => headers,
|
114
134
|
RESPONSE_SOCKET => socket,
|
115
135
|
FAIL => ((env[FAIL]||[]) + [error]).compact,
|
116
|
-
LOG => env[LOG] ||[]))
|
136
|
+
LOG => env[LOG] ||[])){ |r, i| i.call(r) }
|
117
137
|
end
|
118
138
|
|
119
139
|
# called in requesting thread, whenever the request is done
|
data/lib/rest-core/timer.rb
CHANGED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
module RestCore; end
|
3
|
+
class RestCore::Clash
|
4
|
+
Empty = Hash.new(&(l = lambda{|_,_|Hash.new(&l).freeze})).freeze
|
5
|
+
|
6
|
+
attr_accessor :data
|
7
|
+
def initialize data
|
8
|
+
self.data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
def [] k
|
12
|
+
if data.key?(k)
|
13
|
+
convert(data[k])
|
14
|
+
else
|
15
|
+
Empty
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def == rhs
|
20
|
+
if rhs.kind_of?(RestCore::Clash)
|
21
|
+
data == rhs.data
|
22
|
+
else
|
23
|
+
data == rhs
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def convert value
|
29
|
+
case value
|
30
|
+
when Hash
|
31
|
+
RestCore::Clash.new(value)
|
32
|
+
when Array
|
33
|
+
value.map{ |ele| convert(ele) }
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def respond_to_missing? msg, include_private=false
|
40
|
+
data.respond_to?(msg, include_private)
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_missing msg, *args, &block
|
44
|
+
if data.respond_to?(msg)
|
45
|
+
data.public_send(msg, *args, &block)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module RestCore; end
|
3
|
+
module RestCore::ParseLink
|
4
|
+
module_function
|
5
|
+
# http://tools.ietf.org/html/rfc5988
|
6
|
+
parname = '"?([^"]+)"?'
|
7
|
+
LINKPARAM = /#{parname}=#{parname}/
|
8
|
+
def parse_link link
|
9
|
+
link.split(',').inject({}) do |r, value|
|
10
|
+
uri, *pairs = value.split(';')
|
11
|
+
params = Hash[pairs.map{ |p| p.strip.match(LINKPARAM)[1..2] }]
|
12
|
+
r[params['rel']] = params.merge('uri' => uri[/<([^>]+)>/, 1])
|
13
|
+
r
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module RestCore; end
|
3
|
+
class RestCore::Smash
|
4
|
+
attr_accessor :data
|
5
|
+
def initialize data
|
6
|
+
self.data = data
|
7
|
+
end
|
8
|
+
|
9
|
+
def [] *keys
|
10
|
+
keys.inject(data) do |r, k|
|
11
|
+
if r.respond_to?(:key) && r.key?(k)
|
12
|
+
r[k]
|
13
|
+
elsif r.respond_to?(:[])
|
14
|
+
r[k]
|
15
|
+
else
|
16
|
+
return nil # stop here
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def == rhs
|
22
|
+
if rhs.kind_of?(RestCore::Smash)
|
23
|
+
data == rhs.data
|
24
|
+
else
|
25
|
+
data == rhs
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def respond_to_missing? msg, include_private=false
|
31
|
+
data.respond_to?(msg, include_private)
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing msg, *args, &block
|
35
|
+
if data.respond_to?(msg)
|
36
|
+
data.public_send(msg, *args, &block)
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/rest-core/version.rb
CHANGED
data/rest-core.gemspec
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
# stub: rest-core 3.
|
2
|
+
# stub: rest-core 3.2.0 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "rest-core"
|
6
|
-
s.version = "3.
|
6
|
+
s.version = "3.2.0"
|
7
7
|
|
8
8
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
9
|
s.require_paths = ["lib"]
|
10
10
|
s.authors = ["Lin Jen-Shin (godfat)"]
|
11
|
-
s.date = "2014-
|
11
|
+
s.date = "2014-06-27"
|
12
12
|
s.description = "Modular Ruby clients interface for REST APIs.\n\nThere has been an explosion in the number of REST APIs available today.\nTo address the need for a way to access these APIs easily and elegantly,\nwe have developed rest-core, which consists of composable middleware\nthat allows you to build a REST client for any REST API. Or in the case of\ncommon APIs such as Facebook, Github, and Twitter, you can simply use the\ndedicated clients provided by [rest-more][].\n\n[rest-more]: https://github.com/godfat/rest-more"
|
13
13
|
s.email = ["godfat (XD) godfat.org"]
|
14
14
|
s.files = [
|
@@ -42,6 +42,7 @@ Gem::Specification.new do |s|
|
|
42
42
|
"lib/rest-core/middleware/auth_basic.rb",
|
43
43
|
"lib/rest-core/middleware/bypass.rb",
|
44
44
|
"lib/rest-core/middleware/cache.rb",
|
45
|
+
"lib/rest-core/middleware/clash_response.rb",
|
45
46
|
"lib/rest-core/middleware/common_logger.rb",
|
46
47
|
"lib/rest-core/middleware/default_headers.rb",
|
47
48
|
"lib/rest-core/middleware/default_payload.rb",
|
@@ -58,6 +59,7 @@ Gem::Specification.new do |s|
|
|
58
59
|
"lib/rest-core/middleware/oauth2_header.rb",
|
59
60
|
"lib/rest-core/middleware/oauth2_query.rb",
|
60
61
|
"lib/rest-core/middleware/query_response.rb",
|
62
|
+
"lib/rest-core/middleware/smash_response.rb",
|
61
63
|
"lib/rest-core/middleware/timeout.rb",
|
62
64
|
"lib/rest-core/patch/multi_json.rb",
|
63
65
|
"lib/rest-core/patch/rest-client.rb",
|
@@ -65,11 +67,14 @@ Gem::Specification.new do |s|
|
|
65
67
|
"lib/rest-core/test.rb",
|
66
68
|
"lib/rest-core/thread_pool.rb",
|
67
69
|
"lib/rest-core/timer.rb",
|
70
|
+
"lib/rest-core/util/clash.rb",
|
68
71
|
"lib/rest-core/util/config.rb",
|
69
72
|
"lib/rest-core/util/hmac.rb",
|
70
73
|
"lib/rest-core/util/json.rb",
|
74
|
+
"lib/rest-core/util/parse_link.rb",
|
71
75
|
"lib/rest-core/util/parse_query.rb",
|
72
76
|
"lib/rest-core/util/payload.rb",
|
77
|
+
"lib/rest-core/util/smash.rb",
|
73
78
|
"lib/rest-core/version.rb",
|
74
79
|
"lib/rest-core/wrapper.rb",
|
75
80
|
"rest-core.gemspec",
|
@@ -79,6 +84,8 @@ Gem::Specification.new do |s|
|
|
79
84
|
"test/test_auth_basic.rb",
|
80
85
|
"test/test_builder.rb",
|
81
86
|
"test/test_cache.rb",
|
87
|
+
"test/test_clash.rb",
|
88
|
+
"test/test_clash_response.rb",
|
82
89
|
"test/test_client.rb",
|
83
90
|
"test/test_client_oauth1.rb",
|
84
91
|
"test/test_config.rb",
|
@@ -94,11 +101,14 @@ Gem::Specification.new do |s|
|
|
94
101
|
"test/test_json_response.rb",
|
95
102
|
"test/test_oauth1_header.rb",
|
96
103
|
"test/test_oauth2_header.rb",
|
104
|
+
"test/test_parse_link.rb",
|
97
105
|
"test/test_payload.rb",
|
98
106
|
"test/test_promise.rb",
|
99
107
|
"test/test_query_response.rb",
|
100
108
|
"test/test_rest-client.rb",
|
101
109
|
"test/test_simple.rb",
|
110
|
+
"test/test_smash.rb",
|
111
|
+
"test/test_smash_response.rb",
|
102
112
|
"test/test_thread_pool.rb",
|
103
113
|
"test/test_timeout.rb",
|
104
114
|
"test/test_universal.rb",
|
@@ -111,6 +121,8 @@ Gem::Specification.new do |s|
|
|
111
121
|
"test/test_auth_basic.rb",
|
112
122
|
"test/test_builder.rb",
|
113
123
|
"test/test_cache.rb",
|
124
|
+
"test/test_clash.rb",
|
125
|
+
"test/test_clash_response.rb",
|
114
126
|
"test/test_client.rb",
|
115
127
|
"test/test_client_oauth1.rb",
|
116
128
|
"test/test_config.rb",
|
@@ -126,11 +138,14 @@ Gem::Specification.new do |s|
|
|
126
138
|
"test/test_json_response.rb",
|
127
139
|
"test/test_oauth1_header.rb",
|
128
140
|
"test/test_oauth2_header.rb",
|
141
|
+
"test/test_parse_link.rb",
|
129
142
|
"test/test_payload.rb",
|
130
143
|
"test/test_promise.rb",
|
131
144
|
"test/test_query_response.rb",
|
132
145
|
"test/test_rest-client.rb",
|
133
146
|
"test/test_simple.rb",
|
147
|
+
"test/test_smash.rb",
|
148
|
+
"test/test_smash_response.rb",
|
134
149
|
"test/test_thread_pool.rb",
|
135
150
|
"test/test_timeout.rb",
|
136
151
|
"test/test_universal.rb",
|
data/test/test_cache.rb
CHANGED
@@ -7,6 +7,18 @@ describe RC::Cache do
|
|
7
7
|
Muack.verify
|
8
8
|
end
|
9
9
|
|
10
|
+
def simple_client
|
11
|
+
RC::Builder.client{ use RC::Cache, {}, nil }.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def json_client
|
15
|
+
RC::Builder.client do
|
16
|
+
use RC::Cache, {}, 3600 do
|
17
|
+
use RC::JsonResponse, true
|
18
|
+
end
|
19
|
+
end.new
|
20
|
+
end
|
21
|
+
|
10
22
|
should 'basic 0' do
|
11
23
|
c = RC::Builder.client do
|
12
24
|
use RC::Cache, {}, 3600
|
@@ -60,17 +72,13 @@ describe RC::Cache do
|
|
60
72
|
should 'not raise error if headers is nil' do
|
61
73
|
path = 'http://a'
|
62
74
|
stub_request(:get , path).to_return(:body => 'OK', :headers => nil)
|
63
|
-
c =
|
64
|
-
use RC::Cache, {}, nil
|
65
|
-
end.new
|
75
|
+
c = simple_client
|
66
76
|
c.get(path).should.eq 'OK'
|
67
77
|
c.get(path).should.eq 'OK'
|
68
78
|
end
|
69
79
|
|
70
80
|
should 'head then get' do
|
71
|
-
c =
|
72
|
-
use RC::Cache, {}, nil
|
73
|
-
end.new
|
81
|
+
c = simple_client
|
74
82
|
path = 'http://example.com'
|
75
83
|
stub_request(:head, path).to_return(:headers => {'A' => 'B'})
|
76
84
|
c.head(path).should.eq('A' => 'B')
|
@@ -98,20 +106,40 @@ describe RC::Cache do
|
|
98
106
|
end
|
99
107
|
|
100
108
|
should 'cache the original response' do
|
101
|
-
c =
|
102
|
-
use RC::Cache, {}, 3600 do
|
103
|
-
use RC::JsonResponse, true
|
104
|
-
end
|
105
|
-
end.new
|
109
|
+
c = json_client
|
106
110
|
stub_request(:get, 'http://me').to_return(:body => body = '{"a":"b"}')
|
107
111
|
c.get('http://me').should.eq 'a' => 'b'
|
108
112
|
c.cache.values.first.should.eq "200\n\n\n#{body}"
|
109
113
|
end
|
110
114
|
|
115
|
+
should 'cache multiple headers' do
|
116
|
+
c = simple_client
|
117
|
+
stub_request(:get, 'http://me').to_return(:headers =>
|
118
|
+
{'Apple' => 'Orange', 'Orange' => 'Apple'})
|
119
|
+
expected = {'APPLE' => 'Orange', 'ORANGE' => 'Apple'}
|
120
|
+
args = ['http://me', {}, {RC::RESPONSE_KEY => RC::RESPONSE_HEADERS}]
|
121
|
+
2.times{ c.get(*args).should.eq expected }
|
122
|
+
end
|
123
|
+
|
124
|
+
should 'preserve promise and REQUEST_URI' do
|
125
|
+
c = simple_client
|
126
|
+
uri = 'http://me?a=b'
|
127
|
+
stub_request(:get, uri)
|
128
|
+
args = ['http://me', {:a => 'b'}, {RC::RESPONSE_KEY => RC::PROMISE}]
|
129
|
+
2.times{ c.get(*args).yield[RC::REQUEST_URI].should.eq uri }
|
130
|
+
end
|
131
|
+
|
132
|
+
should 'preserve promise and preserve wrapped call' do
|
133
|
+
c = json_client
|
134
|
+
stub_request(:get, 'http://me').to_return(:body => body = '{"a":"b"}')
|
135
|
+
args = ['http://me', {}, {RC::RESPONSE_KEY => RC::PROMISE}]
|
136
|
+
2.times do
|
137
|
+
c.get(*args).then{ |r| r[RC::RESPONSE_BODY].should.eq 'a' => 'b' }.yield
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
111
141
|
should 'multiline response' do
|
112
|
-
c =
|
113
|
-
use RC::Cache, {}, 3600
|
114
|
-
end.new
|
142
|
+
c = simple_client
|
115
143
|
stub_request(:get, 'http://html').to_return(:body => body = "a\n\nb")
|
116
144
|
c.get('http://html').should.eq body
|
117
145
|
c.cache.values.first.should.eq "200\n\n\n#{body}"
|
@@ -151,14 +179,14 @@ describe RC::Cache do
|
|
151
179
|
end
|
152
180
|
|
153
181
|
should 'not cache dry run' do
|
154
|
-
c =
|
182
|
+
c = simple_client
|
155
183
|
c.url('test')
|
156
184
|
c.cache.should.eq({})
|
157
185
|
end
|
158
186
|
|
159
187
|
should 'not cache hijacking' do
|
160
188
|
stub_request(:get, 'http://a').to_return(:body => 'ok')
|
161
|
-
c =
|
189
|
+
c = simple_client
|
162
190
|
2.times do
|
163
191
|
c.get('http://a', {}, RC::HIJACK => true,
|
164
192
|
RC::RESPONSE_KEY => RC::RESPONSE_SOCKET).
|
@@ -170,7 +198,7 @@ describe RC::Cache do
|
|
170
198
|
should 'update cache if there is cache option set to false' do
|
171
199
|
url, body = "https://cache", 'ok'
|
172
200
|
stub_request(:get, url).to_return(:body => body)
|
173
|
-
c =
|
201
|
+
c = simple_client
|
174
202
|
|
175
203
|
c.get(url) .should.eq body
|
176
204
|
stub_request(:get, url).to_return(:body => body.reverse).times(2)
|
data/test/test_clash.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/test'
|
3
|
+
|
4
|
+
describe RC::Clash do
|
5
|
+
should 'never give nil for non-existing values' do
|
6
|
+
h = {0 => 1, 2 => {3 => 4, 5 => [6, {7 => 8}]}, 9 => false, 10 => nil}
|
7
|
+
c = RC::Clash.new(h)
|
8
|
+
c[0] .should.eq(1)
|
9
|
+
c[1] .should.eq({})
|
10
|
+
c[1][2] .should.eq({})
|
11
|
+
c[1][2][3] .should.eq({})
|
12
|
+
c[2] .should.eq(3 => 4, 5 => [6, {7 => 8}])
|
13
|
+
c[2][3] .should.eq(4)
|
14
|
+
c[2][4] .should.eq({})
|
15
|
+
c[2][4][5] .should.eq({})
|
16
|
+
c[2][5] .should.eq([6, {7 => 8}])
|
17
|
+
c[2][5][1] .should.eq(7 => 8)
|
18
|
+
c[2][5][1][7] .should.eq(8)
|
19
|
+
c[2][5][1][8] .should.eq({})
|
20
|
+
c[2][5][1][8][9].should.eq({})
|
21
|
+
c[9] .should.eq(false)
|
22
|
+
c[10] .should.eq(nil)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/test'
|
3
|
+
|
4
|
+
describe RC::ClashResponse do
|
5
|
+
describe 'app' do
|
6
|
+
app = RC::ClashResponse.new(RC::Dry.new, true)
|
7
|
+
|
8
|
+
should 'do nothing' do
|
9
|
+
env = {RC::RESPONSE_BODY => []}
|
10
|
+
app.call(env) do |res|
|
11
|
+
res.should.eq(env)
|
12
|
+
res[RC::RESPONSE_BODY].should.kind_of(Array)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'clash' do
|
17
|
+
app.call(RC::RESPONSE_BODY => {}) do |res|
|
18
|
+
body = res[RC::RESPONSE_BODY]
|
19
|
+
body.should.kind_of(RC::Clash)
|
20
|
+
body.should.empty
|
21
|
+
body[0].should.eq({})
|
22
|
+
body[0][0].should.eq({})
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'client' do
|
27
|
+
body = {0 => {1 => 2}}
|
28
|
+
client = RC::Builder.client do
|
29
|
+
use RC::ClashResponse, true
|
30
|
+
run Class.new{
|
31
|
+
define_method(:call) do |env, &block|
|
32
|
+
block.call(env.merge(RC::RESPONSE_BODY => body))
|
33
|
+
end
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'do nothing' do
|
38
|
+
b = client.new(:clash_response => false).get(''){ |res|
|
39
|
+
res.should.eq(body)
|
40
|
+
res.should.kind_of(Hash)
|
41
|
+
}.get('')
|
42
|
+
b.should.eq(body)
|
43
|
+
b.should.kind_of(Hash)
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'clash' do
|
47
|
+
b = client.new.get(''){ |res|
|
48
|
+
res.should.eq(body)
|
49
|
+
res.should.kind_of(RC::Clash)
|
50
|
+
}.get('')
|
51
|
+
b.should.eq(body)
|
52
|
+
b.should.kind_of(RC::Clash)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/test/test_client.rb
CHANGED
@@ -67,13 +67,13 @@ describe RC::Simple do
|
|
67
67
|
stub(any_instance_of(WeakRef)).weakref_alive?{false}
|
68
68
|
client.new.get(url)
|
69
69
|
client.promises.size.should < 6
|
70
|
-
client.
|
70
|
+
client.shutdown
|
71
71
|
client.promises.should.empty
|
72
72
|
end
|
73
73
|
|
74
74
|
should 'have correct to_i' do
|
75
|
-
stub_request(:get,
|
76
|
-
RC::Simple.new.get(
|
75
|
+
stub_request(:get, url).to_return(:body => '123')
|
76
|
+
RC::Simple.new.get(url).to_i.should.eq 123
|
77
77
|
end
|
78
78
|
|
79
79
|
should 'use defaults' do
|
@@ -106,4 +106,9 @@ describe RC::Simple do
|
|
106
106
|
c.timeout = false
|
107
107
|
c.timeout.should.eq false # false would disable default
|
108
108
|
end
|
109
|
+
|
110
|
+
should 'work for inheritance' do
|
111
|
+
stub_request(:get, url).to_return(:body => '123')
|
112
|
+
Class.new(RC::Simple).new.get(url).should.eq '123'
|
113
|
+
end
|
109
114
|
end
|
data/test/test_json_response.rb
CHANGED
@@ -4,6 +4,7 @@ require 'rest-core/test'
|
|
4
4
|
describe RC::JsonResponse do
|
5
5
|
describe 'app' do
|
6
6
|
app = RC::JsonResponse.new(RC::Dry.new, true)
|
7
|
+
bad = 'bad json'
|
7
8
|
|
8
9
|
should 'do nothing' do
|
9
10
|
expected = {RC::RESPONSE_BODY => nil,
|
@@ -14,9 +15,26 @@ describe RC::JsonResponse do
|
|
14
15
|
should 'decode' do
|
15
16
|
expected = {RC::RESPONSE_BODY => {},
|
16
17
|
RC::REQUEST_HEADERS => {'Accept' => 'application/json'}}
|
17
|
-
app.call(RC::RESPONSE_BODY => '{}')
|
18
|
+
app.call(RC::RESPONSE_BODY => '{}') do |response|
|
18
19
|
response.should.eq(expected)
|
19
|
-
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'give proper parse error' do
|
24
|
+
app.call(RC::RESPONSE_BODY => bad) do |response|
|
25
|
+
err = response[RC::FAIL].first
|
26
|
+
err.should.kind_of(RC::Json.const_get(:ParseError))
|
27
|
+
err.should.kind_of(RC::JsonResponse::ParseError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
should 'give me original text' do
|
32
|
+
app.call(RC::RESPONSE_BODY => bad) do |response|
|
33
|
+
err = response[RC::FAIL].first
|
34
|
+
err.message .should.include(bad)
|
35
|
+
err.body .should.eq(bad)
|
36
|
+
err.cause.class.should.eq(RC::Json.const_get(:ParseError))
|
37
|
+
end
|
20
38
|
end
|
21
39
|
end
|
22
40
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/test'
|
3
|
+
|
4
|
+
describe RC::ParseLink do
|
5
|
+
describe 'http://tools.ietf.org/html/rfc5988' do
|
6
|
+
should '5.5 a' do
|
7
|
+
link = '<http://example.com/TheBook/chapter2>; rel="previous"; title="previous chapter"'
|
8
|
+
RC::ParseLink.parse_link(link).should.eq(
|
9
|
+
'previous' => {'uri' => 'http://example.com/TheBook/chapter2',
|
10
|
+
'rel' => 'previous',
|
11
|
+
'title' => 'previous chapter'})
|
12
|
+
end
|
13
|
+
|
14
|
+
should '5.5 b' do
|
15
|
+
link = '</>; rel="http://example.net/foo"'
|
16
|
+
RC::ParseLink.parse_link(link).should.eq(
|
17
|
+
'http://example.net/foo' => {'uri' => '/',
|
18
|
+
'rel' => 'http://example.net/foo'})
|
19
|
+
end
|
20
|
+
|
21
|
+
should '5.5 c (we did not implement * and unescape for now)' do
|
22
|
+
link = <<-LINK
|
23
|
+
</TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel
|
24
|
+
LINK
|
25
|
+
RC::ParseLink.parse_link(link).should.eq(
|
26
|
+
'previous' => {'uri' => '/TheBook/chapter2',
|
27
|
+
'rel' => 'previous',
|
28
|
+
'title*' => "UTF-8'de'letztes%20Kapitel"},
|
29
|
+
'next' => {'uri' => '/TheBook/chapter4',
|
30
|
+
'rel' => 'next',
|
31
|
+
'title*' => "UTF-8'de'n%c3%a4chstes%20Kapitel"})
|
32
|
+
end
|
33
|
+
|
34
|
+
should '5.5 d' do
|
35
|
+
link = '<http://example.org/>; rel="start http://example.net/relation/other"'
|
36
|
+
|
37
|
+
RC::ParseLink.parse_link(link).should.eq(
|
38
|
+
'start http://example.net/relation/other' =>
|
39
|
+
{'uri' => 'http://example.org/',
|
40
|
+
'rel' => 'start http://example.net/relation/other'})
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/test/test_promise.rb
CHANGED
@@ -12,7 +12,8 @@ describe RC::Promise do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
after do
|
15
|
-
@client.
|
15
|
+
@client.shutdown
|
16
|
+
@client.thread_pool.size.should.eq 0
|
16
17
|
Muack.verify
|
17
18
|
end
|
18
19
|
|
@@ -44,6 +45,15 @@ describe RC::Promise do
|
|
44
45
|
@promise.send(:headers).should.eq('K' => 'V')
|
45
46
|
end
|
46
47
|
|
48
|
+
should 'then then then' do
|
49
|
+
plusone = lambda do |r|
|
50
|
+
r.merge(RC::RESPONSE_BODY => r[RC::RESPONSE_BODY] + 1)
|
51
|
+
end
|
52
|
+
2.times{ @promise.then(&plusone).then(&plusone).then(&plusone) }
|
53
|
+
@promise.fulfill(0, 200, {})
|
54
|
+
@promise.future_body.should.eq 6
|
55
|
+
end
|
56
|
+
|
47
57
|
should 'call inline if pool_size < 0' do
|
48
58
|
@client.pool_size = -1
|
49
59
|
current_thread = Thread.current
|
data/test/test_smash.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/test'
|
3
|
+
|
4
|
+
describe RC::Smash do
|
5
|
+
should 'deep access' do
|
6
|
+
h = {0 => 1, 2 => {3 => 4, 5 => [6, {7 => 8}]}, 9 => false, 10 => nil}
|
7
|
+
c = RC::Smash.new(h)
|
8
|
+
c[0] .should.eq(1)
|
9
|
+
c[1] .should.eq(nil)
|
10
|
+
c[1, 2] .should.eq(nil)
|
11
|
+
c[1, 2, 3] .should.eq(nil)
|
12
|
+
c[2] .should.eq(3 => 4, 5 => [6, {7 => 8}])
|
13
|
+
c[2, 3] .should.eq(4)
|
14
|
+
c[2, 4] .should.eq(nil)
|
15
|
+
c[2, 4, 5] .should.eq(nil)
|
16
|
+
c[2, 5] .should.eq([6, {7 => 8}])
|
17
|
+
c[2, 5, 1] .should.eq(7 => 8)
|
18
|
+
c[2, 5, 1, 7] .should.eq(8)
|
19
|
+
c[2, 5, 1, 8] .should.eq(nil)
|
20
|
+
c[2, 5, 1, 8, 9].should.eq(nil)
|
21
|
+
c[9] .should.eq(false)
|
22
|
+
c[10] .should.eq(nil)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/test'
|
3
|
+
|
4
|
+
describe RC::SmashResponse do
|
5
|
+
describe 'app' do
|
6
|
+
app = RC::SmashResponse.new(RC::Dry.new, true)
|
7
|
+
|
8
|
+
should 'do nothing' do
|
9
|
+
env = {RC::RESPONSE_BODY => []}
|
10
|
+
app.call(env) do |res|
|
11
|
+
res.should.eq(env)
|
12
|
+
res[RC::RESPONSE_BODY].should.kind_of(Array)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'smash' do
|
17
|
+
app.call(RC::RESPONSE_BODY => {}) do |res|
|
18
|
+
body = res[RC::RESPONSE_BODY]
|
19
|
+
body.should.kind_of(RC::Smash)
|
20
|
+
body.should.empty
|
21
|
+
body[0].should.eq(nil)
|
22
|
+
body[0, 0].should.eq(nil)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'client' do
|
27
|
+
body = {0 => {1 => 2}}
|
28
|
+
client = RC::Builder.client do
|
29
|
+
use RC::SmashResponse, true
|
30
|
+
run Class.new{
|
31
|
+
define_method(:call) do |env, &block|
|
32
|
+
block.call(env.merge(RC::RESPONSE_BODY => body))
|
33
|
+
end
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'do nothing' do
|
38
|
+
b = client.new(:smash_response => false).get(''){ |res|
|
39
|
+
res.should.eq(body)
|
40
|
+
res.should.kind_of(Hash)
|
41
|
+
}.get('')
|
42
|
+
b.should.eq(body)
|
43
|
+
b.should.kind_of(Hash)
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'clash' do
|
47
|
+
b = client.new.get(''){ |res|
|
48
|
+
res.should.eq(body)
|
49
|
+
res.should.kind_of(RC::Smash)
|
50
|
+
}.get('')
|
51
|
+
b.should.eq(body)
|
52
|
+
b.should.kind_of(RC::Smash)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/test/test_universal.rb
CHANGED
@@ -16,4 +16,13 @@ describe RC::Universal do
|
|
16
16
|
u.request_full(env, u.dry)[RC::REQUEST_HEADERS].should.eq(
|
17
17
|
{'Authorization' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}.merge(acc))
|
18
18
|
end
|
19
|
+
|
20
|
+
should 'clash' do
|
21
|
+
url = 'http://localhost/'
|
22
|
+
stub_request(:get, url).to_return(:body => '{"a":{"b":"c"}}')
|
23
|
+
res = RC::Universal.new(:json_response => true,
|
24
|
+
:clash_response => true,
|
25
|
+
:log_method => false).get(url)
|
26
|
+
res['a']['d'].should.eq({})
|
27
|
+
end
|
19
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lin Jen-Shin (godfat)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|
@@ -99,6 +99,7 @@ files:
|
|
99
99
|
- lib/rest-core/middleware/auth_basic.rb
|
100
100
|
- lib/rest-core/middleware/bypass.rb
|
101
101
|
- lib/rest-core/middleware/cache.rb
|
102
|
+
- lib/rest-core/middleware/clash_response.rb
|
102
103
|
- lib/rest-core/middleware/common_logger.rb
|
103
104
|
- lib/rest-core/middleware/default_headers.rb
|
104
105
|
- lib/rest-core/middleware/default_payload.rb
|
@@ -115,6 +116,7 @@ files:
|
|
115
116
|
- lib/rest-core/middleware/oauth2_header.rb
|
116
117
|
- lib/rest-core/middleware/oauth2_query.rb
|
117
118
|
- lib/rest-core/middleware/query_response.rb
|
119
|
+
- lib/rest-core/middleware/smash_response.rb
|
118
120
|
- lib/rest-core/middleware/timeout.rb
|
119
121
|
- lib/rest-core/patch/multi_json.rb
|
120
122
|
- lib/rest-core/patch/rest-client.rb
|
@@ -122,11 +124,14 @@ files:
|
|
122
124
|
- lib/rest-core/test.rb
|
123
125
|
- lib/rest-core/thread_pool.rb
|
124
126
|
- lib/rest-core/timer.rb
|
127
|
+
- lib/rest-core/util/clash.rb
|
125
128
|
- lib/rest-core/util/config.rb
|
126
129
|
- lib/rest-core/util/hmac.rb
|
127
130
|
- lib/rest-core/util/json.rb
|
131
|
+
- lib/rest-core/util/parse_link.rb
|
128
132
|
- lib/rest-core/util/parse_query.rb
|
129
133
|
- lib/rest-core/util/payload.rb
|
134
|
+
- lib/rest-core/util/smash.rb
|
130
135
|
- lib/rest-core/version.rb
|
131
136
|
- lib/rest-core/wrapper.rb
|
132
137
|
- rest-core.gemspec
|
@@ -136,6 +141,8 @@ files:
|
|
136
141
|
- test/test_auth_basic.rb
|
137
142
|
- test/test_builder.rb
|
138
143
|
- test/test_cache.rb
|
144
|
+
- test/test_clash.rb
|
145
|
+
- test/test_clash_response.rb
|
139
146
|
- test/test_client.rb
|
140
147
|
- test/test_client_oauth1.rb
|
141
148
|
- test/test_config.rb
|
@@ -151,11 +158,14 @@ files:
|
|
151
158
|
- test/test_json_response.rb
|
152
159
|
- test/test_oauth1_header.rb
|
153
160
|
- test/test_oauth2_header.rb
|
161
|
+
- test/test_parse_link.rb
|
154
162
|
- test/test_payload.rb
|
155
163
|
- test/test_promise.rb
|
156
164
|
- test/test_query_response.rb
|
157
165
|
- test/test_rest-client.rb
|
158
166
|
- test/test_simple.rb
|
167
|
+
- test/test_smash.rb
|
168
|
+
- test/test_smash_response.rb
|
159
169
|
- test/test_thread_pool.rb
|
160
170
|
- test/test_timeout.rb
|
161
171
|
- test/test_universal.rb
|
@@ -188,6 +198,8 @@ test_files:
|
|
188
198
|
- test/test_auth_basic.rb
|
189
199
|
- test/test_builder.rb
|
190
200
|
- test/test_cache.rb
|
201
|
+
- test/test_clash.rb
|
202
|
+
- test/test_clash_response.rb
|
191
203
|
- test/test_client.rb
|
192
204
|
- test/test_client_oauth1.rb
|
193
205
|
- test/test_config.rb
|
@@ -203,11 +215,14 @@ test_files:
|
|
203
215
|
- test/test_json_response.rb
|
204
216
|
- test/test_oauth1_header.rb
|
205
217
|
- test/test_oauth2_header.rb
|
218
|
+
- test/test_parse_link.rb
|
206
219
|
- test/test_payload.rb
|
207
220
|
- test/test_promise.rb
|
208
221
|
- test/test_query_response.rb
|
209
222
|
- test/test_rest-client.rb
|
210
223
|
- test/test_simple.rb
|
224
|
+
- test/test_smash.rb
|
225
|
+
- test/test_smash_response.rb
|
211
226
|
- test/test_thread_pool.rb
|
212
227
|
- test/test_timeout.rb
|
213
228
|
- test/test_universal.rb
|