rest-core 3.1.0 → 3.1.1
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 +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
|