rest-core 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +21 -11
- data/README.md +60 -21
- data/lib/rest-core/builder.rb +49 -15
- data/lib/rest-core/client.rb +6 -14
- data/lib/rest-core/version.rb +1 -1
- data/rest-core.gemspec +3 -3
- data/test/test_client.rb +29 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 530a9ed8d2e0b7cc07703113eaf17950cc611d12
|
4
|
+
data.tar.gz: ad5eb2846acf18bfacdd88fff480e91bccb4f89e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e43365d45547c82c1093963a677c34c2f38ab7c637e80c1219462b7a75313aa30ed8ce817ec94d55d9400a1d993b93bf2e4e203b7a6f4334818086dfa5e8ea9
|
7
|
+
data.tar.gz: a7c962c1876c5b67480708d2dda7e5df1299432cb16aea00afd7173efffc7e22c3596f9d06da640511d2de64df649cc4c927d9bd23e3e1492be502534543f287
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## rest-core 3.1.1 -- 2014-05-13
|
4
|
+
|
5
|
+
### Enhancements
|
6
|
+
|
7
|
+
* Introduced `RC::Client.wait` along with `RC::Client#wait`. It would collect
|
8
|
+
all the promises from all instances of the client, so we could wait on all
|
9
|
+
promises we're waiting. This would make writing graceful shutdown much
|
10
|
+
easier. For example, we could have: `at_exit{ RC::Universal.wait }` to
|
11
|
+
wait on all requests from the universal client before exiting the process.
|
12
|
+
|
3
13
|
## rest-core 3.1.0 -- 2014-05-09
|
4
14
|
|
5
15
|
### Incompatible changes
|
@@ -10,7 +20,7 @@
|
|
10
20
|
* Now RC::EventSource#onmessage would receive the event in the first argument,
|
11
21
|
and the data in the second argument instead of a hash in the first argument.
|
12
22
|
|
13
|
-
###
|
23
|
+
### Enhancements
|
14
24
|
|
15
25
|
* Added RC::REQUEST_URI in the env so that we could access the requesting
|
16
26
|
URI easily.
|
@@ -59,7 +69,7 @@ Highlights:
|
|
59
69
|
* `RC::Future` is renamed to `RC::Promise`, and `RC::Future::Proxy` is
|
60
70
|
renamed to `RC::Promise::Future`.
|
61
71
|
|
62
|
-
###
|
72
|
+
### Enhancements
|
63
73
|
|
64
74
|
* HIJACK support, which is similar to Rack's HIJACK feature. If you're
|
65
75
|
passing `{RC::HIJACK => true}` whenever making a request, rest-core would
|
@@ -107,7 +117,7 @@ Highlights:
|
|
107
117
|
|
108
118
|
* Fixed em-http-request support.
|
109
119
|
|
110
|
-
###
|
120
|
+
### Enhancements
|
111
121
|
|
112
122
|
* [`Payload`] Now it is a class rather than a module.
|
113
123
|
* [`Paylaod`] Introduced `Payload.generate_with_headers`.
|
@@ -132,7 +142,7 @@ Highlights:
|
|
132
142
|
* [`Payload`] Now we could correctly support payload with "foo=1&foo=2".
|
133
143
|
* [`Client`] Fix inspect spacing.
|
134
144
|
|
135
|
-
###
|
145
|
+
### Enhancements
|
136
146
|
|
137
147
|
* [`Payload`] With this class introduced, replacing rest-client's own
|
138
148
|
payload implementation, we could pass StringIO or other sockets as the
|
@@ -172,7 +182,7 @@ Highlights:
|
|
172
182
|
* Don't walk into parent's constants in `RC.eagerload`.
|
173
183
|
* Also rescue `NameError` in `RC.eagerload`.
|
174
184
|
|
175
|
-
###
|
185
|
+
### Enhancements
|
176
186
|
|
177
187
|
* Remove unnecessary `future.wrap` in `EmHttpRequest`.
|
178
188
|
* Introduce Future#callback_in_async.
|
@@ -269,7 +279,7 @@ It's a bit outdated, but you can also checkout my blog post.
|
|
269
279
|
* You must provide a block to `app.call(env){ ... }`.
|
270
280
|
* Rename Wrapper#default_app to Wrapper#default_engine
|
271
281
|
|
272
|
-
###
|
282
|
+
### Enhancements
|
273
283
|
|
274
284
|
* The default engine is changed from `RestClient` to `Auto`, which would
|
275
285
|
be using `EmHttpRequest` under the context of a event loop, while
|
@@ -317,7 +327,7 @@ It's a bit outdated, but you can also checkout my blog post.
|
|
317
327
|
|
318
328
|
## rest-core 1.0.3 -- 2012-08-15
|
319
329
|
|
320
|
-
###
|
330
|
+
### Enhancements
|
321
331
|
|
322
332
|
* [Client] `client.head` now returns the headers instead of response body.
|
323
333
|
It doesn't make sense to return the response body, because there's no
|
@@ -334,7 +344,7 @@ It's a bit outdated, but you can also checkout my blog post.
|
|
334
344
|
|
335
345
|
## rest-core 1.0.2 -- 2012-06-05
|
336
346
|
|
337
|
-
###
|
347
|
+
### Enhancements
|
338
348
|
|
339
349
|
* Some internal refactoring.
|
340
350
|
|
@@ -459,7 +469,7 @@ for more detail.
|
|
459
469
|
|
460
470
|
## rest-core 0.8.2 -- 2012-02-18
|
461
471
|
|
462
|
-
###
|
472
|
+
### Enhancements
|
463
473
|
|
464
474
|
* [`DefaultPayload`] This middleware allows you to have default payload for
|
465
475
|
POST and PUT requests.
|
@@ -474,7 +484,7 @@ for more detail.
|
|
474
484
|
|
475
485
|
## rest-core 0.8.1 -- 2012-02-09
|
476
486
|
|
477
|
-
###
|
487
|
+
### Enhancements
|
478
488
|
|
479
489
|
* [`Wrapper`] Introducing `Wrapper.default_app` (also `Builder.default_app`)
|
480
490
|
which you can change the default app from `RestClient` to other HTTP
|
@@ -513,7 +523,7 @@ Changes are mostly related to OAuth.
|
|
513
523
|
|
514
524
|
[OAuth 2.0 spec]: http://tools.ietf.org/html/draft-ietf-oauth-v2-22
|
515
525
|
|
516
|
-
###
|
526
|
+
### Enhancements
|
517
527
|
|
518
528
|
* [`AuthBasic`] Added a new middleware which could do
|
519
529
|
[basic authentication][].
|
data/README.md
CHANGED
@@ -87,7 +87,7 @@ YourClient = RC::Builder.client do
|
|
87
87
|
use RC::DefaultSite , 'https://api.github.com/users/'
|
88
88
|
use RC::JsonResponse, true
|
89
89
|
use RC::CommonLogger, method(:puts)
|
90
|
-
use RC::Cache , nil, 3600
|
90
|
+
use RC::Cache , nil, 3600 # :expires_in if cache store supports
|
91
91
|
end
|
92
92
|
```
|
93
93
|
|
@@ -392,7 +392,8 @@ module RestCore
|
|
392
392
|
|
393
393
|
use FollowRedirect, 10
|
394
394
|
use CommonLogger , method(:puts)
|
395
|
-
use Cache , {}, 600 do
|
395
|
+
use Cache , {}, 600 do # default :expires_in 600 but the default
|
396
|
+
# cache {} didn't support it
|
396
397
|
use ErrorHandler, nil
|
397
398
|
use ErrorDetectorHttp
|
398
399
|
use JsonResponse, false
|
@@ -432,25 +433,62 @@ installed before trying this.
|
|
432
433
|
|
433
434
|
## List of built-in Middleware:
|
434
435
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
* [
|
450
|
-
* [
|
451
|
-
*
|
452
|
-
|
453
|
-
|
436
|
+
### [RC::AuthBasic][]
|
437
|
+
|
438
|
+
### [RC::Bypass][]
|
439
|
+
|
440
|
+
### [RC::Cache][]
|
441
|
+
|
442
|
+
use RC::Cache, cache, expires_in
|
443
|
+
|
444
|
+
where `cache` is the cache store which the cache data would be storing to.
|
445
|
+
`expires_in` would be passed to
|
446
|
+
`cache.store(key, value :expires_in => expires_in)` if `store` method is
|
447
|
+
available and its arity should be at least 3. The interface to the cache
|
448
|
+
could be referenced from [moneta][], namely:
|
449
|
+
|
450
|
+
* (required) `[](key)`
|
451
|
+
* (required) `[]=(key, value)`
|
452
|
+
* (optional, required if :expires_in is needed) `store(key, value, options)`
|
453
|
+
|
454
|
+
Note that `{:expires_in => seconds}` would be passed as the options in
|
455
|
+
`store(key, value, options)`, and a plain old Ruby hash `{}` satisfies
|
456
|
+
the mandatory requirements: `[](key)` and `[]=(key, value)`, but not the
|
457
|
+
last one for `:expires_in` because the `store` method for Hash did not take
|
458
|
+
the third argument. That means we could use `{}` as the cache but it would
|
459
|
+
simply ignore `:expires_in`.
|
460
|
+
|
461
|
+
### [RC::CommonLogger][]
|
462
|
+
|
463
|
+
### [RC::DefaultHeaders][]
|
464
|
+
|
465
|
+
### [RC::DefaultPayload][]
|
466
|
+
|
467
|
+
### [RC::DefaultQuery][]
|
468
|
+
|
469
|
+
### [RC::DefaultSite][]
|
470
|
+
|
471
|
+
### [RC::Defaults][]
|
472
|
+
|
473
|
+
### [RC::ErrorDetector][]
|
474
|
+
|
475
|
+
### [RC::ErrorDetectorHttp][]
|
476
|
+
|
477
|
+
### [RC::ErrorHandler][]
|
478
|
+
|
479
|
+
### [RC::FollowRedirect][]
|
480
|
+
|
481
|
+
### [RC::JsonRequest][]
|
482
|
+
|
483
|
+
### [RC::JsonResponse][]
|
484
|
+
|
485
|
+
### [RC::Oauth1Header][]
|
486
|
+
|
487
|
+
### [RC::Oauth2Header][]
|
488
|
+
|
489
|
+
### [RC::Oauth2Query][]
|
490
|
+
|
491
|
+
### [RC::Timeout][]
|
454
492
|
|
455
493
|
[RC::AuthBasic]: lib/rest-core/middleware/auth_basic.rb
|
456
494
|
[RC::Bypass]: lib/rest-core/middleware/bypass.rb
|
@@ -471,6 +509,7 @@ installed before trying this.
|
|
471
509
|
[RC::Oauth2Header]: lib/rest-core/middleware/oauth2_header.rb
|
472
510
|
[RC::Oauth2Query]: lib/rest-core/middleware/oauth2_query.rb
|
473
511
|
[RC::Timeout]: lib/rest-core/middleware/timeout.rb
|
512
|
+
[moneta]: https://github.com/minad/moneta#expiration
|
474
513
|
|
475
514
|
## Build Your Own Middleware:
|
476
515
|
|
data/lib/rest-core/builder.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
|
2
|
+
require 'thread'
|
2
3
|
require 'rest-core/client'
|
3
4
|
require 'rest-core/wrapper'
|
4
5
|
|
@@ -16,27 +17,60 @@ class RestCore::Builder
|
|
16
17
|
|
17
18
|
def to_client *attrs
|
18
19
|
fields = members + attrs
|
19
|
-
struct =
|
20
|
-
Struct.new(nil)
|
21
|
-
else
|
22
|
-
Struct.new(*fields.uniq)
|
23
|
-
end
|
20
|
+
struct = build_struct(fields)
|
24
21
|
client = Class.new(struct)
|
25
22
|
client.const_set('Struct', struct)
|
23
|
+
class_methods = build_class_methods
|
24
|
+
client.const_set('ClassMethods', class_methods)
|
25
|
+
client.singleton_class.send(:include, class_methods)
|
26
26
|
client.send(:include, Client)
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
client.builder = self
|
28
|
+
client.pool_size = 0 # default to no pool
|
29
|
+
client.pool_idle_time = 60 # default to 60 seconds
|
30
|
+
client.event_source_class = EventSource
|
31
|
+
client.promises = []
|
32
|
+
client.mutex = Mutex.new
|
33
|
+
client
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_struct fields
|
37
|
+
if fields.empty?
|
38
|
+
Struct.new(nil)
|
39
|
+
else
|
40
|
+
Struct.new(*fields.uniq)
|
41
|
+
end
|
42
|
+
end
|
30
43
|
|
31
|
-
|
32
|
-
|
44
|
+
def build_class_methods
|
45
|
+
Module.new do
|
46
|
+
attr_accessor :builder, :pool_size, :pool_idle_time,
|
47
|
+
:event_source_class, :promises, :mutex
|
48
|
+
def thread_pool; RestCore::ThreadPool[self]; end
|
49
|
+
|
50
|
+
def give_promise weak_promise, ps=promises, m=mutex
|
51
|
+
m.synchronize do
|
52
|
+
ps << weak_promise
|
53
|
+
ps.keep_if(&:weakref_alive?)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def wait ps=promises, m=mutex
|
58
|
+
return self if ps.empty?
|
59
|
+
current_promises = nil
|
60
|
+
m.synchronize do
|
61
|
+
current_promises = ps.dup
|
62
|
+
ps.clear
|
63
|
+
end
|
64
|
+
current_promises.each do |p|
|
65
|
+
next unless p.weakref_alive?
|
66
|
+
begin
|
67
|
+
p.wait
|
68
|
+
rescue WeakRef::RefError # it's gc'ed after we think it's alive
|
69
|
+
end
|
70
|
+
end
|
71
|
+
wait(ps, m)
|
33
72
|
end
|
34
73
|
end
|
35
|
-
client.instance_variable_set(:@builder, self)
|
36
|
-
client.instance_variable_set(:@pool_size, 0) # default to no pool
|
37
|
-
client.instance_variable_set(:@pool_idle_time, 60) # default to 60 seconds
|
38
|
-
client.instance_variable_set(:@event_source_class, EventSource)
|
39
|
-
client
|
40
74
|
end
|
41
75
|
|
42
76
|
def initialize &block
|
data/lib/rest-core/client.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
|
2
|
+
require 'thread'
|
2
3
|
require 'weakref'
|
3
4
|
require 'rest-core'
|
4
5
|
|
@@ -85,19 +86,8 @@ module RestCore::Client
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def wait
|
88
|
-
|
89
|
-
|
90
|
-
mutex.synchronize{
|
91
|
-
current_promises = promises.dup
|
92
|
-
promises.clear
|
93
|
-
}
|
94
|
-
current_promises.each{ |f|
|
95
|
-
begin
|
96
|
-
f.wait
|
97
|
-
rescue WeakRef::RefError # it's gc'ed after we think it's alive
|
98
|
-
end if f.weakref_alive?
|
99
|
-
}
|
100
|
-
wait
|
89
|
+
self.class.wait(promises, mutex)
|
90
|
+
self
|
101
91
|
end
|
102
92
|
|
103
93
|
def url path, query={}, opts={}
|
@@ -181,7 +171,9 @@ module RestCore::Client
|
|
181
171
|
# in that case (maybe in a user created engine), Client#wait
|
182
172
|
# won't work because we have no way to track the promise.
|
183
173
|
if response.kind_of?(Hash) && response[PROMISE].kind_of?(Promise)
|
184
|
-
|
174
|
+
weak_promise = WeakRef.new(response[PROMISE])
|
175
|
+
self.class.give_promise(weak_promise)
|
176
|
+
self.class.give_promise(weak_promise, promises, mutex)
|
185
177
|
end
|
186
178
|
|
187
179
|
if block_given?
|
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.1.
|
2
|
+
# stub: rest-core 3.1.1 ruby lib
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "rest-core"
|
6
|
-
s.version = "3.1.
|
6
|
+
s.version = "3.1.1"
|
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-05-
|
11
|
+
s.date = "2014-05-13"
|
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 = [
|
data/test/test_client.rb
CHANGED
@@ -7,9 +7,10 @@ describe RC::Simple do
|
|
7
7
|
Muack.verify
|
8
8
|
end
|
9
9
|
|
10
|
+
url = 'http://localhost/'
|
11
|
+
|
10
12
|
should 'do simple request' do
|
11
13
|
c = RC::Simple.new
|
12
|
-
url = 'http://localhost/'
|
13
14
|
[:get, :post, :delete, :put, :patch].each do |method|
|
14
15
|
stub_request(method, url).to_return(:body => '[]')
|
15
16
|
c.send(method, url).should.eq '[]'
|
@@ -23,7 +24,6 @@ describe RC::Simple do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
should 'call the callback' do
|
26
|
-
url = 'http://localhost/'
|
27
27
|
[:get, :post, :delete, :put, :patch].each do |method|
|
28
28
|
stub_request(method, url).to_return(:body => '123')
|
29
29
|
(client = RC::Simple.new).send(method, url){ |res|
|
@@ -44,6 +44,33 @@ describe RC::Simple do
|
|
44
44
|
client.wait
|
45
45
|
end
|
46
46
|
|
47
|
+
should 'wait for all the requests' do
|
48
|
+
t, i, m = 5, 0, Mutex.new
|
49
|
+
stub_request(:get, url).to_return do
|
50
|
+
m.synchronize{ i += 1 }
|
51
|
+
Thread.pass
|
52
|
+
end
|
53
|
+
|
54
|
+
client = RC::Builder.client
|
55
|
+
t.times{ client.new.get(url) }
|
56
|
+
client.wait
|
57
|
+
client.promises.should.empty
|
58
|
+
i.should.eq t
|
59
|
+
end
|
60
|
+
|
61
|
+
should 'cleanup promises' do
|
62
|
+
stub_request(:get, url)
|
63
|
+
client = RC::Builder.client
|
64
|
+
5.times{ client.new.get(url) }
|
65
|
+
Thread.pass
|
66
|
+
GC.start # can only force GC run on MRI, so we mock for jruby and rubinius
|
67
|
+
stub(any_instance_of(WeakRef)).weakref_alive?{false}
|
68
|
+
client.new.get(url)
|
69
|
+
client.promises.size.should < 6
|
70
|
+
client.wait
|
71
|
+
client.promises.should.empty
|
72
|
+
end
|
73
|
+
|
47
74
|
should 'have correct to_i' do
|
48
75
|
stub_request(:get, 'http://localhost/').to_return(:body => '123')
|
49
76
|
RC::Simple.new.get('http://localhost/').to_i.should.eq 123
|
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.1.
|
4
|
+
version: 3.1.1
|
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-05-
|
11
|
+
date: 2014-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|