rest-core 3.3.2 → 3.3.3

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