rest-core 1.0.0 → 1.0.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.
- data/CHANGES.md +7 -1
- data/README.md +9 -4
- data/TODO.md +18 -0
- data/example/eventmachine.rb +3 -1
- data/example/multi.rb +2 -0
- data/lib/rest-core/app/auto.rb +6 -5
- data/lib/rest-core/app/em-http-request-async.rb +10 -7
- data/lib/rest-core/app/em-http-request-fiber.rb +7 -4
- data/lib/rest-core/middleware/default_query.rb +6 -0
- data/lib/rest-core/middleware/timeout.rb +12 -11
- data/lib/rest-core/version.rb +1 -1
- data/rest-core.gemspec +7 -3
- data/test/test_default_query.rb +45 -0
- data/test/test_timeout.rb +19 -0
- metadata +7 -3
data/CHANGES.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## rest-core 1.0.1 -- 2012-05-14
|
4
|
+
|
5
|
+
* [`Auto`] Check for eventmachine first instead of cool.io
|
6
|
+
* [`EmHttpRequestFiber`] Also pass callback for errback
|
7
|
+
* [`DefaultQuery`] Make default query to {} instead of nil
|
8
|
+
|
3
9
|
## rest-core 1.0.0 -- 2012-03-17
|
4
10
|
|
5
11
|
This is a very significant release. The most important change is now we
|
@@ -7,7 +13,7 @@ support asynchronous requests, by either passing a callback block or using
|
|
7
13
|
fibers in Ruby 1.9 to make the whole program still look synchronous.
|
8
14
|
|
9
15
|
Please read [README.md](https://github.com/cardinalblue/rest-core/blob/master/README.md)
|
10
|
-
or [example](https://github.com/cardinalblue/rest-core/
|
16
|
+
or [example](https://github.com/cardinalblue/rest-core/tree/master/example)
|
11
17
|
for more detail.
|
12
18
|
|
13
19
|
* [`Client`] Client#inspect is fixed for clients which do not have any
|
data/README.md
CHANGED
@@ -49,14 +49,16 @@ dedicated clients provided by [rest-more][].
|
|
49
49
|
* Fibers only work on Ruby 1.9+
|
50
50
|
* gem [em-http-request][] (if using eventmachine)
|
51
51
|
* gem [cool.io-http][] (if using cool.io)
|
52
|
-
* gem json or yajl-ruby (if using JsonDecode middleware)
|
52
|
+
* gem json or yajl-ruby (if using `JsonDecode` middleware)
|
53
53
|
|
54
54
|
[em-http-request]: https://github.com/igrigorik/em-http-request
|
55
55
|
[cool.io-http]: https://github.com/godfat/cool.io-http
|
56
56
|
|
57
57
|
## INSTALLATION:
|
58
58
|
|
59
|
+
``` shell
|
59
60
|
gem install rest-core
|
61
|
+
```
|
60
62
|
|
61
63
|
Or if you want development version, put this in Gemfile:
|
62
64
|
|
@@ -106,14 +108,14 @@ for concepts.
|
|
106
108
|
|
107
109
|
[example/rest-client.rb]: https://github.com/cardinalblue/rest-core/blob/master/example/rest-client.rb
|
108
110
|
[rest-more]: https://github.com/cardinalblue/rest-more
|
109
|
-
[rubyconf.tw]: http://rubyconf.tw/2011/#6
|
110
111
|
[slides]: http://www.godfat.org/slide/2011-08-27-rest-core.html
|
112
|
+
[rubyconf.tw]: http://rubyconf.tw/2011/#6
|
111
113
|
|
112
114
|
## Asynchronous HTTP Requests:
|
113
115
|
|
114
116
|
I/O bound operations shouldn't be blocking the CPU! If you have a reactor,
|
115
117
|
i.e. event loop, you should take the advantage of that to make HTTP requests
|
116
|
-
|
118
|
+
not block the whole process/thread. For now, we support eventmachine and
|
117
119
|
cool.io. Below is an example for eventmachine:
|
118
120
|
|
119
121
|
``` ruby
|
@@ -133,12 +135,13 @@ If you're passing a block, the block is called after the response is
|
|
133
135
|
available. That is the block is the callback for the request.
|
134
136
|
|
135
137
|
``` ruby
|
136
|
-
client = AsynchronousClient.new
|
138
|
+
client = AsynchronousClient.new
|
137
139
|
EM.run{
|
138
140
|
client.get('cardinalblue'){ |response|
|
139
141
|
p response
|
140
142
|
EM.stop
|
141
143
|
}
|
144
|
+
puts "It's not blocking..."
|
142
145
|
}
|
143
146
|
```
|
144
147
|
|
@@ -154,6 +157,7 @@ If you don't understand what does this mean, you can take a look at
|
|
154
157
|
p client.get('cardinalblue')
|
155
158
|
EM.stop
|
156
159
|
}.resume
|
160
|
+
puts "It's not blocking..."
|
157
161
|
}
|
158
162
|
```
|
159
163
|
|
@@ -178,6 +182,7 @@ You can also make multi-requests synchronously like this:
|
|
178
182
|
p Fiber.yield
|
179
183
|
EM.stop
|
180
184
|
}.resume
|
185
|
+
puts "It's not blocking..."
|
181
186
|
}
|
182
187
|
```
|
183
188
|
|
data/TODO.md
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
+
* Auto should also pick RestClient if it's not inside a fiber
|
4
|
+
* HTTP method in Requested log
|
3
5
|
* middleware revisit (how to initialize?)
|
4
6
|
* test utility
|
7
|
+
|
8
|
+
# BUG
|
9
|
+
|
10
|
+
* inheritance should work; assign builder?
|
11
|
+
* no error handling in cool.io
|
12
|
+
|
13
|
+
# FEATURE
|
14
|
+
|
15
|
+
* middleware composer
|
16
|
+
* RC::Payload which can handle Content-Type: application/json
|
17
|
+
* how to pass a arbitrary payload generator?
|
18
|
+
* RC::JsonRequest
|
19
|
+
* RC::FakeGetWithPayload
|
20
|
+
|
21
|
+
* ResponseThunk and EmHttpRequestThunk
|
22
|
+
* options for apps
|
data/example/eventmachine.rb
CHANGED
@@ -10,12 +10,13 @@ AsynchronousClient = RestCore::Builder.client do
|
|
10
10
|
run s::EmHttpRequest
|
11
11
|
end
|
12
12
|
|
13
|
-
client = AsynchronousClient.new
|
13
|
+
client = AsynchronousClient.new
|
14
14
|
EM.run{
|
15
15
|
client.get('cardinalblue'){ |response|
|
16
16
|
p response
|
17
17
|
EM.stop
|
18
18
|
}
|
19
|
+
puts "It's not blocking..."
|
19
20
|
}
|
20
21
|
|
21
22
|
puts
|
@@ -25,4 +26,5 @@ EM.run{
|
|
25
26
|
p client.get('cardinalblue')
|
26
27
|
EM.stop
|
27
28
|
}.resume
|
29
|
+
puts "It's not blocking..."
|
28
30
|
}
|
data/example/multi.rb
CHANGED
@@ -23,6 +23,7 @@ EM.run{
|
|
23
23
|
result[0] = response
|
24
24
|
fiber.resume(result) if result.size == 2
|
25
25
|
}
|
26
|
+
puts "It's not blocking..."
|
26
27
|
client.get('cardinalblue'){ |response|
|
27
28
|
result[1] = response
|
28
29
|
fiber.resume(result) if result.size == 2
|
@@ -30,4 +31,5 @@ EM.run{
|
|
30
31
|
p Fiber.yield
|
31
32
|
EM.stop
|
32
33
|
}.resume
|
34
|
+
puts "It's not blocking..."
|
33
35
|
}
|
data/lib/rest-core/app/auto.rb
CHANGED
@@ -9,12 +9,13 @@ class RestCore::Auto
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def http_client
|
12
|
-
if Object.const_defined?(:
|
13
|
-
has_active_watchers?
|
14
|
-
@coolio ||= RestCore::Coolio.new
|
15
|
-
elsif Object.const_defined?(:EventMachine) && ::EventMachine.
|
16
|
-
reactor_running?
|
12
|
+
if Object.const_defined?(:EventMachine) && ::EventMachine.reactor_running?
|
17
13
|
@emhttprequest ||= RestCore::EmHttpRequest.new
|
14
|
+
|
15
|
+
elsif Object.const_defined?(:Coolio) && ::Coolio::Loop.default.
|
16
|
+
has_active_watchers?
|
17
|
+
@coolio ||= RestCore::Coolio.new
|
18
|
+
|
18
19
|
else
|
19
20
|
@restclient ||= RestCore::RestClient.new
|
20
21
|
end
|
@@ -13,13 +13,8 @@ class RestCore::EmHttpRequestAsync
|
|
13
13
|
:body => payload.read,
|
14
14
|
:head => payload.headers.merge(env[REQUEST_HEADERS]))
|
15
15
|
|
16
|
-
client.callback{
|
17
|
-
|
18
|
-
env[ASYNC].call(env.merge(
|
19
|
-
RESPONSE_BODY => client.response,
|
20
|
-
RESPONSE_STATUS => client.response_header.status,
|
21
|
-
RESPONSE_HEADERS => client.response_header)) if env[ASYNC]
|
22
|
-
}
|
16
|
+
client.callback{ respond(env, client) }
|
17
|
+
client. errback{ respond(env, client) }
|
23
18
|
|
24
19
|
env[TIMER].on_timeout{
|
25
20
|
client.close
|
@@ -31,4 +26,12 @@ class RestCore::EmHttpRequestAsync
|
|
31
26
|
|
32
27
|
env
|
33
28
|
end
|
29
|
+
|
30
|
+
def respond env, client
|
31
|
+
env[TIMER].cancel if env[TIMER]
|
32
|
+
env[ASYNC].call(env.merge(
|
33
|
+
RESPONSE_BODY => client.response,
|
34
|
+
RESPONSE_STATUS => client.response_header.status,
|
35
|
+
RESPONSE_HEADERS => client.response_header)) if env[ASYNC]
|
36
|
+
end
|
34
37
|
end
|
@@ -16,10 +16,8 @@ class RestCore::EmHttpRequestFiber
|
|
16
16
|
:body => payload.read,
|
17
17
|
:head => payload.headers.merge(env[REQUEST_HEADERS]))
|
18
18
|
|
19
|
-
client.callback{
|
20
|
-
|
21
|
-
f.resume(process(env, client)) if f.alive?
|
22
|
-
}
|
19
|
+
client.callback{ respond(f, env, client) }
|
20
|
+
client. errback{ respond(f, env, client) }
|
23
21
|
|
24
22
|
if (response = Fiber.yield).kind_of?(::Exception)
|
25
23
|
client.close
|
@@ -29,6 +27,11 @@ class RestCore::EmHttpRequestFiber
|
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
30
|
+
def respond f, env, client
|
31
|
+
env[TIMER].cancel if env[TIMER]
|
32
|
+
f.resume(process(env, client)) if f.alive?
|
33
|
+
end
|
34
|
+
|
32
35
|
def process env, client
|
33
36
|
result = env.merge(RESPONSE_BODY => client.response,
|
34
37
|
RESPONSE_STATUS => client.response_header.status,
|
@@ -4,6 +4,12 @@ require 'rest-core/middleware'
|
|
4
4
|
class RestCore::DefaultQuery
|
5
5
|
def self.members; [:query]; end
|
6
6
|
include RestCore::Middleware
|
7
|
+
|
8
|
+
def initialize *args
|
9
|
+
super
|
10
|
+
@query ||= {}
|
11
|
+
end
|
12
|
+
|
7
13
|
def call env
|
8
14
|
app.call(env.merge(REQUEST_QUERY =>
|
9
15
|
@query.merge(query(env)).merge(env[REQUEST_QUERY] || {})))
|
@@ -8,7 +8,7 @@ class RestCore::Timeout
|
|
8
8
|
include RestCore::Middleware
|
9
9
|
|
10
10
|
def call env
|
11
|
-
return app.call(env) if env[DRY]
|
11
|
+
return app.call(env) if env[DRY] || timeout(env) == 0
|
12
12
|
monitor(env){ |e| app.call(e) }
|
13
13
|
end
|
14
14
|
|
@@ -21,7 +21,7 @@ class RestCore::Timeout
|
|
21
21
|
end
|
22
22
|
|
23
23
|
case class_name
|
24
|
-
when /Coolio
|
24
|
+
when /EmHttpRequest|Coolio/
|
25
25
|
if root_fiber? && env[ASYNC]
|
26
26
|
yield(env.merge(TIMER => timeout_with_callback(env, class_name)))
|
27
27
|
else
|
@@ -42,13 +42,13 @@ class RestCore::Timeout
|
|
42
42
|
|
43
43
|
def timeout_with_callback env, class_name
|
44
44
|
case class_name
|
45
|
+
when /EmHttpRequest/
|
46
|
+
EventMachineTimer.new(timeout(env), timeout_error)
|
45
47
|
when /Coolio/
|
46
48
|
timer = CoolioTimer.new(timeout(env))
|
47
49
|
timer.error = timeout_error
|
48
50
|
timer.attach(::Coolio::Loop.default)
|
49
51
|
timer
|
50
|
-
when /EmHttpRequest/
|
51
|
-
EventMachineTimer.new(timeout(env), timeout_error)
|
52
52
|
else
|
53
53
|
raise "BUG: #{run} is not supported"
|
54
54
|
end
|
@@ -56,6 +56,12 @@ class RestCore::Timeout
|
|
56
56
|
|
57
57
|
def timeout_with_resume env, class_name
|
58
58
|
case class_name
|
59
|
+
when /EmHttpRequest/
|
60
|
+
f = Fiber.current
|
61
|
+
EventMachineTimer.new(timeout(env), error = timeout_error){
|
62
|
+
f.resume(error) if f.alive?
|
63
|
+
}
|
64
|
+
|
59
65
|
when /Coolio/
|
60
66
|
f = Fiber.current
|
61
67
|
timer = CoolioTimer.new(timeout(env))
|
@@ -64,11 +70,6 @@ class RestCore::Timeout
|
|
64
70
|
timer.attach(::Coolio::Loop.default)
|
65
71
|
timer
|
66
72
|
|
67
|
-
when /EmHttpRequest/
|
68
|
-
f = Fiber.current
|
69
|
-
EventMachineTimer.new(timeout(env), error = timeout_error){
|
70
|
-
f.resume(error) if f.alive?
|
71
|
-
}
|
72
73
|
else
|
73
74
|
raise "BUG: #{run} is not supported"
|
74
75
|
end
|
@@ -78,8 +79,8 @@ class RestCore::Timeout
|
|
78
79
|
::Timeout::Error.new('execution expired')
|
79
80
|
end
|
80
81
|
|
81
|
-
autoload :CoolioTimer,
|
82
|
-
'rest-core/middleware/timeout/coolio_timer'
|
83
82
|
autoload :EventMachineTimer,
|
84
83
|
'rest-core/middleware/timeout/eventmachine_timer'
|
84
|
+
autoload :CoolioTimer,
|
85
|
+
'rest-core/middleware/timeout/coolio_timer'
|
85
86
|
end
|
data/lib/rest-core/version.rb
CHANGED
data/rest-core.gemspec
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "rest-core"
|
5
|
-
s.version = "1.0.
|
5
|
+
s.version = "1.0.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = [
|
9
9
|
"Cardinal Blue",
|
10
10
|
"Lin Jen-Shin (godfat)"]
|
11
|
-
s.date = "2012-
|
11
|
+
s.date = "2012-05-14"
|
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-core]: https://github.com/cardinalblue/rest-core\n[rest-more]: https://github.com/cardinalblue/rest-more"
|
13
13
|
s.email = ["dev (XD) cardinalblue.com"]
|
14
14
|
s.files = [
|
@@ -84,29 +84,33 @@ Gem::Specification.new do |s|
|
|
84
84
|
"test/test_builder.rb",
|
85
85
|
"test/test_client.rb",
|
86
86
|
"test/test_client_oauth1.rb",
|
87
|
+
"test/test_default_query.rb",
|
87
88
|
"test/test_error_detector.rb",
|
88
89
|
"test/test_error_detector_http.rb",
|
89
90
|
"test/test_follow_redirect.rb",
|
90
91
|
"test/test_json_decode.rb",
|
91
92
|
"test/test_oauth1_header.rb",
|
92
93
|
"test/test_payload.rb",
|
94
|
+
"test/test_timeout.rb",
|
93
95
|
"test/test_universal.rb",
|
94
96
|
"test/test_wrapper.rb"]
|
95
97
|
s.homepage = "https://github.com/cardinalblue/rest-core"
|
96
98
|
s.require_paths = ["lib"]
|
97
|
-
s.rubygems_version = "1.8.
|
99
|
+
s.rubygems_version = "1.8.24"
|
98
100
|
s.summary = "Modular Ruby clients interface for REST APIs"
|
99
101
|
s.test_files = [
|
100
102
|
"test/test_auth_basic.rb",
|
101
103
|
"test/test_builder.rb",
|
102
104
|
"test/test_client.rb",
|
103
105
|
"test/test_client_oauth1.rb",
|
106
|
+
"test/test_default_query.rb",
|
104
107
|
"test/test_error_detector.rb",
|
105
108
|
"test/test_error_detector_http.rb",
|
106
109
|
"test/test_follow_redirect.rb",
|
107
110
|
"test/test_json_decode.rb",
|
108
111
|
"test/test_oauth1_header.rb",
|
109
112
|
"test/test_payload.rb",
|
113
|
+
"test/test_timeout.rb",
|
110
114
|
"test/test_universal.rb",
|
111
115
|
"test/test_wrapper.rb"]
|
112
116
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/test'
|
3
|
+
|
4
|
+
describe RC::DefaultQuery do
|
5
|
+
describe 'when given query' do
|
6
|
+
before do
|
7
|
+
@app = RC::DefaultQuery.new(RC::Dry.new, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def app
|
11
|
+
@app
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'do nothing' do
|
15
|
+
app.call({})[RC::REQUEST_QUERY].should.eq({})
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'merge query' do
|
19
|
+
app.instance_eval{@query = {'q' => 'uery'}}
|
20
|
+
|
21
|
+
app.call({}).should.eq({RC::REQUEST_QUERY =>
|
22
|
+
{'q' => 'uery'}})
|
23
|
+
|
24
|
+
format = {'format' => 'json'}
|
25
|
+
env = {RC::REQUEST_QUERY => format}
|
26
|
+
|
27
|
+
app.call(env).should.eq({RC::REQUEST_QUERY =>
|
28
|
+
{'q' => 'uery'}.merge(format)})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'when not given query' do
|
33
|
+
before do
|
34
|
+
@app = RC::DefaultQuery.new(RC::Dry.new)
|
35
|
+
end
|
36
|
+
|
37
|
+
def app
|
38
|
+
@app
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'merge query with {}' do
|
42
|
+
app.call({}).should.eq({RC::REQUEST_QUERY => {}})
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
require 'rest-core/test'
|
3
|
+
|
4
|
+
describe RC::Timeout do
|
5
|
+
before do
|
6
|
+
@app = RC::Timeout.new(RC::Dry.new, 0)
|
7
|
+
end
|
8
|
+
|
9
|
+
should 'bypass timeout if timeout is 0' do
|
10
|
+
mock(@app).monitor.times(0)
|
11
|
+
@app.call({}).should.eq({})
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'run the monitor to setup timeout' do
|
15
|
+
env = {'timeout' => 2}
|
16
|
+
mock.proxy(@app).monitor(env).times(1)
|
17
|
+
@app.call(env).should.eq(env)
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-05-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rest-client
|
@@ -125,12 +125,14 @@ files:
|
|
125
125
|
- test/test_builder.rb
|
126
126
|
- test/test_client.rb
|
127
127
|
- test/test_client_oauth1.rb
|
128
|
+
- test/test_default_query.rb
|
128
129
|
- test/test_error_detector.rb
|
129
130
|
- test/test_error_detector_http.rb
|
130
131
|
- test/test_follow_redirect.rb
|
131
132
|
- test/test_json_decode.rb
|
132
133
|
- test/test_oauth1_header.rb
|
133
134
|
- test/test_payload.rb
|
135
|
+
- test/test_timeout.rb
|
134
136
|
- test/test_universal.rb
|
135
137
|
- test/test_wrapper.rb
|
136
138
|
homepage: https://github.com/cardinalblue/rest-core
|
@@ -153,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
155
|
version: '0'
|
154
156
|
requirements: []
|
155
157
|
rubyforge_project:
|
156
|
-
rubygems_version: 1.8.
|
158
|
+
rubygems_version: 1.8.24
|
157
159
|
signing_key:
|
158
160
|
specification_version: 3
|
159
161
|
summary: Modular Ruby clients interface for REST APIs
|
@@ -162,12 +164,14 @@ test_files:
|
|
162
164
|
- test/test_builder.rb
|
163
165
|
- test/test_client.rb
|
164
166
|
- test/test_client_oauth1.rb
|
167
|
+
- test/test_default_query.rb
|
165
168
|
- test/test_error_detector.rb
|
166
169
|
- test/test_error_detector_http.rb
|
167
170
|
- test/test_follow_redirect.rb
|
168
171
|
- test/test_json_decode.rb
|
169
172
|
- test/test_oauth1_header.rb
|
170
173
|
- test/test_payload.rb
|
174
|
+
- test/test_timeout.rb
|
171
175
|
- test/test_universal.rb
|
172
176
|
- test/test_wrapper.rb
|
173
177
|
has_rdoc:
|