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 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