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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 070bdd0392158ac265903b7e713245a18933aaa8
4
- data.tar.gz: 015edd1a6221a2a0b55dc04958dbed0df1729ecf
3
+ metadata.gz: 530a9ed8d2e0b7cc07703113eaf17950cc611d12
4
+ data.tar.gz: ad5eb2846acf18bfacdd88fff480e91bccb4f89e
5
5
  SHA512:
6
- metadata.gz: 30bcbb75a17c797b3ebf823d5e46837209bd628df0abc0190f9d67823f4367b1556a1f11461befb176400aba3863ec920561f503da32dd1edc00682e9e2b44a9
7
- data.tar.gz: fd18d5ecd9cb318c9b8ce3616c3f7432bd55ea43b6c517ea0bbc21a8d33670b0fca238678b08faf25b6c80b95f3c9d6be449a69a6b74ecb929ea90995b49b4ce
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- ### Enhancement
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
- * [RC::AuthBasic][]
436
- * [RC::Bypass][]
437
- * [RC::Cache][]
438
- * [RC::CommonLogger][]
439
- * [RC::DefaultHeaders][]
440
- * [RC::DefaultPayload][]
441
- * [RC::DefaultQuery][]
442
- * [RC::DefaultSite][]
443
- * [RC::Defaults][]
444
- * [RC::ErrorDetector][]
445
- * [RC::ErrorDetectorHttp][]
446
- * [RC::ErrorHandler][]
447
- * [RC::FollowRedirect][]
448
- * [RC::JsonRequest][]
449
- * [RC::JsonResponse][]
450
- * [RC::Oauth1Header][]
451
- * [RC::Oauth2Header][]
452
- * [RC::Oauth2Query][]
453
- * [RC::Timeout][]
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
 
@@ -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 = if fields.empty?
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
- class << client
28
- attr_reader :builder
29
- attr_accessor :pool_size, :pool_idle_time, :event_source_class
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
- def thread_pool
32
- RestCore::ThreadPool[self]
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
@@ -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
- return self if promises.empty?
89
- current_promises = nil
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
- mutex.synchronize{ promises << WeakRef.new(response[PROMISE]) }
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?
@@ -1,4 +1,4 @@
1
1
 
2
2
  module RestCore
3
- VERSION = '3.1.0'
3
+ VERSION = '3.1.1'
4
4
  end
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: rest-core 3.1.0 ruby lib
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.0"
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-09"
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 = [
@@ -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.0
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-09 00:00:00.000000000 Z
11
+ date: 2014-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpclient