rest-core 3.3.2 → 3.3.3

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: 791ce9f1c9627673f6bb3c4708c6077948c2f049
4
- data.tar.gz: 364ccb0bcdb0788c64e28221b333408c401395f2
3
+ metadata.gz: 07acac398a096c63464e9dca43b964dbe0d6359a
4
+ data.tar.gz: 887c818f4a83591b316511c709fbbf10e2d45c35
5
5
  SHA512:
6
- metadata.gz: 2efd353f7e2c8058beeb5926427ffed27efdf3216b16bdf6c8fed1c7c57ba60636dc89be1b19db07aeac23c8ed954c4d106f179b755607589d6f63f93d2fdf7d
7
- data.tar.gz: 0be08aada1f1a659d006a253809340e73d5590aa788be0d5d89780634f64be3c211715b5c8fd3c296e80db42cf66ed95d353deb1a51ad1d1f01b7922693b520a
6
+ metadata.gz: 24e9ebd41021150461013695d6e7cf4d2e6c628ea5156787c7dd957d4cd72e9df3452529518cfc9a48d1c837379aebee3cc6485bbe6422ba196fb3951f02da19
7
+ data.tar.gz: 6a0da34cf3b1221b9b791fb63eee8419c2ce93b6766e395019453f5dc65a8859f24a73a48ef46728ef7e9262186ff0d032334d82ddb777cfcad4b08ec920e4ad
data/.travis.yml CHANGED
@@ -2,8 +2,8 @@ before_install: 'git submodule update --init'
2
2
  script: 'ruby -r bundler/setup -S rake test'
3
3
 
4
4
  rvm:
5
- - 1.9.3
6
- - 2.0.0
7
- - ruby
5
+ - 1.9
6
+ - 2.0
7
+ - 2.1
8
8
  - rbx-2
9
9
  - jruby
data/CHANGES.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # CHANGES
2
2
 
3
+ ## rest-core 3.3.3 -- 2014-11-07
4
+
5
+ ### Bugs fixed
6
+
7
+ * `RC::EventSource` would now properly reconnect for SystemCallError such as
8
+ `Errno::ECONNRESET`.
9
+
10
+ * It would now always emit a warning whenever there's an exception raised
11
+ asynchronously.
12
+
13
+ * All exceptions raised from a thread or thread pool would now have a
14
+ proper backtrace. This was fixed by introducing `RC::Promise.backtrace`
15
+
16
+ ### Enhancements
17
+
18
+ * Introduced `RC::Promise.backtrace`. Using this in a callback could give you
19
+ proper backtrace, comparing to `caller` would only give you the backtrace
20
+ for current thread.
21
+
22
+ * Introduced `RC::Promise.set_backtrace`. Using this we could set exceptions
23
+ with proper backtrace.
24
+
3
25
  ## rest-core 3.3.2 -- 2014-10-11
4
26
 
5
27
  * Just use `File.join` for `RC::DefaultSite` as `File::SEPARATOR` is
data/README.md CHANGED
@@ -14,6 +14,9 @@ talk is in Mandarin.
14
14
  * [github](https://github.com/godfat/rest-core)
15
15
  * [rubygems](https://rubygems.org/gems/rest-core)
16
16
  * [rdoc](http://rdoc.info/projects/godfat/rest-core)
17
+ * [mailing list](http://www.freelists.org/list/rest-core)
18
+ Send your questions to: <rest-core@freelists.org> and you could read
19
+ through [archives](http://www.freelists.org/archives/rest-core)
17
20
 
18
21
  ## DESCRIPTION:
19
22
 
@@ -73,8 +76,6 @@ gem 'rest-core', :git => 'git://github.com/godfat/rest-core.git',
73
76
  If you just want to use Facebook or Twitter clients, please take a look at
74
77
  [rest-more][] which has a lot of clients built with rest-core.
75
78
 
76
- [rest-more]: http://github.com/godfat/rest-more
77
-
78
79
  ## Build Your Own Clients:
79
80
 
80
81
  You can use `RestCore::Builder` to build your own dedicated clients.
@@ -374,12 +375,10 @@ client.request_full(RC::REQUEST_PATH => 'godfat',
374
375
 
375
376
  Runnable example is at: [example/simple.rb][]. Please see [rest-more][]
376
377
  for more complex examples to build clients, and [slides][] from
377
- [rubyconf.tw/2011][rubyconf.tw] for concepts.
378
+ [rubyconf.tw/2011][talk] for concepts.
378
379
 
379
380
  [example/simple.rb]: example/simple.rb
380
- [rest-more]: https://github.com/godfat/rest-more
381
381
  [slides]: http://www.godfat.org/slide/2011-08-27-rest-core.html
382
- [rubyconf.tw]: http://rubyconf.tw/2011/#6
383
382
 
384
383
  ## Playing Around:
385
384
 
@@ -452,7 +451,6 @@ This is mostly for fun and experimenting, so it's only included in
452
451
  installed before trying this.
453
452
 
454
453
  [rib]: https://github.com/godfat/rib
455
- [rest-more]: https://github.com/godfat/rest-more
456
454
 
457
455
  ## List of built-in Middleware:
458
456
 
@@ -683,7 +681,6 @@ all the possible use cases, you can also see: [example/use-cases.rb][]. It's
683
681
  also served as a test for each possible combinations, so it's quite complex
684
682
  and complete.
685
683
 
686
- [example/simple.rb]: example/simple.rb
687
684
  [example/use-cases.rb]: example/use-cases.rb
688
685
 
689
686
  ## rest-core users:
@@ -16,8 +16,6 @@ class RestCore::HttpClient < RestCore::Engine
16
16
  else
17
17
  request_sync(client, payload, headers, promise, env)
18
18
  end
19
- rescue Exception => e
20
- promise.reject(e)
21
19
  end
22
20
 
23
21
  def request_sync client, payload, headers, promise, env
@@ -103,7 +103,7 @@ class RestCore::EventSource < Struct.new(:client, :path, :query, :opts,
103
103
  end
104
104
  sock.close
105
105
  onerror(EOFError.new, sock)
106
- rescue IOError => e
106
+ rescue IOError, SystemCallError => e
107
107
  onerror(e, sock)
108
108
  end
109
109
 
@@ -24,6 +24,6 @@ class RestCore::AuthBasic
24
24
 
25
25
  def auth_basic_header env
26
26
  {'Authorization' =>
27
- "Basic #{["#{username(env)}:#{password(env)}"].pack('m').tr("\n",'')}"}
27
+ "Basic #{["#{username(env)}:#{password(env)}"].pack('m0')}"}
28
28
  end
29
29
  end
@@ -30,6 +30,7 @@ class RestCore::ErrorHandler
30
30
  end
31
31
 
32
32
  def process res, err
33
+ RC::Promise.set_backtrace(err)
33
34
  if res[ASYNC]
34
35
  res.merge(RESPONSE_BODY => err)
35
36
  else
@@ -47,7 +47,7 @@ class RestCore::Oauth1Header
47
47
 
48
48
  def signature env, params
49
49
  [Hmac.sha1("#{consumer_secret(env)}&#{oauth_token_secret(env)}",
50
- base_string(env, params))].pack('m').tr("\n", '')
50
+ base_string(env, params))].pack('m0')
51
51
  end
52
52
 
53
53
  def base_string env, oauth_params
@@ -63,7 +63,7 @@ class RestCore::Oauth1Header
63
63
  end
64
64
 
65
65
  def nonce
66
- [OpenSSL::Random.random_bytes(32)].pack('m').tr("+/=\n", '')
66
+ [OpenSSL::Random.random_bytes(32)].pack('m0').tr("+/=", '')
67
67
  end
68
68
 
69
69
  # according to OAuth 1.0a spec, only:
@@ -21,6 +21,14 @@ class RestCore::Promise
21
21
  promise
22
22
  end
23
23
 
24
+ def self.backtrace
25
+ Thread.current[:backtrace] || []
26
+ end
27
+
28
+ def self.set_backtrace e
29
+ e.set_backtrace((e.backtrace || caller) + backtrace)
30
+ end
31
+
24
32
  def initialize env, k=RC.id, immediate=false, &job
25
33
  self.env = env
26
34
  self.k = [k]
@@ -58,11 +66,13 @@ class RestCore::Promise
58
66
  if pool_size < 0 # negative number for blocking call
59
67
  job.call
60
68
  elsif pool_size > 0
69
+ backtrace = caller + self.class.backtrace
61
70
  self.task = client_class.thread_pool.defer do
62
- synchronized_yield{ job.call }
71
+ synchronized_yield(backtrace){ job.call }
63
72
  end
64
73
  else
65
- Thread.new{ synchronized_yield{ job.call } }
74
+ backtrace = caller + self.class.backtrace
75
+ Thread.new{ synchronized_yield(backtrace){ job.call } }
66
76
  end
67
77
  env[TIMER].on_timeout{ reject(env[TIMER].error) } if env[TIMER]
68
78
  end
@@ -85,7 +95,8 @@ class RestCore::Promise
85
95
  self.body, self.status, self.headers, self.socket =
86
96
  body, status, headers, socket
87
97
  # under ASYNC callback, should call immediately
88
- callback_in_async if immediate
98
+ callback if immediate
99
+ ensure
89
100
  condv.broadcast # client or response might be waiting
90
101
  end
91
102
 
@@ -112,7 +123,7 @@ class RestCore::Promise
112
123
  # For synchronous mode, since we're waiting for the callback anyway,
113
124
  # we don't really have to check if it's called.
114
125
  def done?
115
- !!status && !(immediate && !called)
126
+ !!status && (!immediate || called)
116
127
  end
117
128
 
118
129
  protected
@@ -124,14 +135,16 @@ class RestCore::Promise
124
135
  private
125
136
  # called in a new thread if pool_size == 0, otherwise from the pool
126
137
  # i.e. requesting thread
127
- def synchronized_yield
138
+ def synchronized_yield backtrace
139
+ Thread.current[:backtrace] = backtrace
128
140
  mutex.synchronize{ yield }
129
141
  rescue Exception => e
142
+ self.class.set_backtrace(e)
130
143
  # nothing we can do here for an asynchronous exception,
131
144
  # so we just log the error
132
145
  # TODO: add error_log_method
133
146
  warn "RestCore: ERROR: #{e}\n from #{e.backtrace.inspect}"
134
- reject(e) # should never deadlock someone
147
+ reject(e) unless done? # not done: i/o error; done: callback error
135
148
  end
136
149
 
137
150
  # called in client thread, when yield is called
@@ -144,18 +157,8 @@ class RestCore::Promise
144
157
  RESPONSE_SOCKET => socket,
145
158
  FAIL => ((env[FAIL]||[]) + [error]).compact,
146
159
  LOG => env[LOG] ||[])){ |r, i| i.call(r) }
160
+ ensure
147
161
  self.called = true
148
- response
149
- end
150
-
151
- # called in requesting thread, whenever the request is done
152
- def callback_in_async
153
- callback
154
- rescue Exception => e
155
- # nothing we can do here for an asynchronous exception,
156
- # so we just log the error
157
- # TODO: add error_log_method
158
- warn "RestCore: ERROR: #{e}\n from #{e.backtrace.inspect}"
159
162
  end
160
163
 
161
164
  def client_class; env[CLIENT].class; end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module RestCore
3
- VERSION = '3.3.2'
3
+ VERSION = '3.3.3'
4
4
  end
data/rest-core.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: rest-core 3.3.2 ruby lib
2
+ # stub: rest-core 3.3.3 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "rest-core"
6
- s.version = "3.3.2"
6
+ s.version = "3.3.3"
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-10-11"
11
+ s.date = "2014-11-07"
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
@@ -49,6 +49,7 @@ describe RC::Simple do
49
49
  stub_request(:get, url).to_return do
50
50
  m.synchronize{ i += 1 }
51
51
  Thread.pass
52
+ {}
52
53
  end
53
54
 
54
55
  client = RC::Builder.client
@@ -131,4 +132,44 @@ describe RC::Simple do
131
132
  stub_request(:get, url).to_return(:body => '123')
132
133
  Class.new(RC::Simple).new.get(url).should.eq '123'
133
134
  end
135
+
136
+ would 'not deadlock when exception was raised in the callback' do
137
+ client = Class.new(RC::Simple).new
138
+ stub_request(:get, url).to_return(:body => 'nnf')
139
+
140
+ (0..1).each do |size|
141
+ mock(any_instance_of(RC::Promise)).warn(is_a(String)) do |msg|
142
+ msg.should.include?('nnf')
143
+ end
144
+ client.class.pool_size = size
145
+ client.get(url) do |body|
146
+ raise body
147
+ end
148
+ client.class.shutdown
149
+ end
150
+
151
+ client.class.pool_size = -1
152
+ should.raise do
153
+ client.get(url) do |body|
154
+ raise body
155
+ end
156
+ end.message.should.eq 'nnf'
157
+ client.class.shutdown
158
+ end
159
+
160
+ would 'be able to access caller outside the callback' do
161
+ client = RC::Simple.new
162
+ stub_request(:get, url).to_return(:body => 'nnf')
163
+ client.get(url) do
164
+ current_file = /^#{__FILE__}/
165
+ caller.grep(current_file).should.empty?
166
+ RC::Promise.backtrace.grep(current_file).should.not.empty?
167
+ client.get(url) do
168
+ RC::Promise.backtrace.last.should.not =~ /promise\.rb:\d+:in/
169
+ client = nil
170
+ end
171
+ end
172
+ client.wait
173
+ client.should.nil? # to make sure the inner most block did run
174
+ end
134
175
  end
@@ -41,4 +41,19 @@ describe RC::ErrorHandler do
41
41
  client.new(:error_handler => lambda{ |res| 1 }).
42
42
  request(RC::FAIL => [0], RC::RESPONSE_KEY => RC::FAIL).should.eq [0, 1]
43
43
  end
44
+
45
+ would 'set full backtrace' do
46
+ url = 'http://example.com/'
47
+ client = RC::Builder.client do
48
+ use RC::ErrorHandler, lambda{ |env|
49
+ RuntimeError.new(env[RC::RESPONSE_BODY]) }
50
+ use RC::ErrorDetectorHttp
51
+ end.new
52
+ stub_request(:get, url).to_return(:status => 404, :body => 'nnf')
53
+ client.get(url) do |error|
54
+ error.message.should.eq 'nnf'
55
+ error.backtrace.grep(/^#{__FILE__}/).should.not.empty?
56
+ end
57
+ client.wait
58
+ end
44
59
  end
data/test/test_simple.rb CHANGED
@@ -33,5 +33,6 @@ describe RC::Simple do
33
33
  stub_request(:get, "#{path}?a=b").to_return(:body => 'OK')
34
34
  RC::Simple.new.get(path, {:a => 'b'},
35
35
  RC::RESPONSE_KEY => RC::REQUEST_URI).should.eq "#{path}?a=b"
36
+ RC::Simple.wait
36
37
  end
37
38
  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.3.2
4
+ version: 3.3.3
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-10-11 00:00:00.000000000 Z
11
+ date: 2014-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpclient