rest-core 3.1.1 → 3.2.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.
- 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
|