rest-core 1.0.3 → 2.0.0
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/.travis.yml +6 -7
- data/CHANGES.md +137 -0
- data/Gemfile +1 -1
- data/README.md +183 -191
- data/TODO.md +5 -8
- data/example/multi.rb +31 -24
- data/example/simple.rb +28 -0
- data/example/use-cases.rb +194 -0
- data/lib/rest-core.rb +26 -19
- data/lib/rest-core/builder.rb +2 -2
- data/lib/rest-core/client.rb +40 -27
- data/lib/rest-core/client/universal.rb +16 -13
- data/lib/rest-core/client_oauth1.rb +5 -5
- data/lib/rest-core/engine/auto.rb +25 -0
- data/lib/rest-core/{app → engine}/dry.rb +1 -2
- data/lib/rest-core/engine/em-http-request.rb +39 -0
- data/lib/rest-core/engine/future/future.rb +106 -0
- data/lib/rest-core/engine/future/future_fiber.rb +39 -0
- data/lib/rest-core/engine/future/future_thread.rb +29 -0
- data/lib/rest-core/engine/rest-client.rb +56 -0
- data/lib/rest-core/middleware.rb +27 -5
- data/lib/rest-core/middleware/auth_basic.rb +5 -5
- data/lib/rest-core/middleware/bypass.rb +2 -2
- data/lib/rest-core/middleware/cache.rb +67 -54
- data/lib/rest-core/middleware/common_logger.rb +5 -8
- data/lib/rest-core/middleware/default_headers.rb +2 -2
- data/lib/rest-core/middleware/default_payload.rb +26 -2
- data/lib/rest-core/middleware/default_query.rb +4 -2
- data/lib/rest-core/middleware/default_site.rb +8 -6
- data/lib/rest-core/middleware/error_detector.rb +9 -16
- data/lib/rest-core/middleware/error_handler.rb +25 -11
- data/lib/rest-core/middleware/follow_redirect.rb +11 -14
- data/lib/rest-core/middleware/json_request.rb +19 -0
- data/lib/rest-core/middleware/json_response.rb +28 -0
- data/lib/rest-core/middleware/oauth1_header.rb +2 -7
- data/lib/rest-core/middleware/oauth2_header.rb +4 -7
- data/lib/rest-core/middleware/oauth2_query.rb +2 -2
- data/lib/rest-core/middleware/timeout.rb +21 -65
- data/lib/rest-core/middleware/timeout/{eventmachine_timer.rb → timer_em.rb} +3 -1
- data/lib/rest-core/middleware/timeout/timer_thread.rb +36 -0
- data/lib/rest-core/patch/multi_json.rb +8 -0
- data/lib/rest-core/test.rb +3 -12
- data/lib/rest-core/util/json.rb +65 -0
- data/lib/rest-core/util/parse_query.rb +2 -2
- data/lib/rest-core/version.rb +1 -1
- data/lib/rest-core/wrapper.rb +16 -16
- data/rest-core.gemspec +28 -27
- data/test/test_auth_basic.rb +14 -10
- data/test/test_builder.rb +7 -7
- data/test/test_cache.rb +126 -37
- data/test/test_client.rb +3 -1
- data/test/test_client_oauth1.rb +2 -3
- data/test/test_default_query.rb +17 -23
- data/test/test_em_http_request.rb +146 -0
- data/test/test_error_detector.rb +0 -1
- data/test/test_error_handler.rb +44 -0
- data/test/test_follow_redirect.rb +17 -19
- data/test/test_json_request.rb +28 -0
- data/test/test_json_response.rb +51 -0
- data/test/test_oauth1_header.rb +4 -4
- data/test/test_payload.rb +20 -12
- data/test/test_simple.rb +14 -0
- data/test/test_timeout.rb +11 -19
- data/test/test_universal.rb +5 -5
- data/test/test_wrapper.rb +19 -13
- metadata +28 -29
- data/doc/ToC.md +0 -7
- data/doc/dependency.md +0 -4
- data/doc/design.md +0 -4
- data/example/auto.rb +0 -51
- data/example/coolio.rb +0 -21
- data/example/eventmachine.rb +0 -30
- data/example/rest-client.rb +0 -16
- data/lib/rest-core/app/abstract/async_fiber.rb +0 -13
- data/lib/rest-core/app/auto.rb +0 -23
- data/lib/rest-core/app/coolio-async.rb +0 -32
- data/lib/rest-core/app/coolio-fiber.rb +0 -30
- data/lib/rest-core/app/coolio.rb +0 -9
- data/lib/rest-core/app/em-http-request-async.rb +0 -37
- data/lib/rest-core/app/em-http-request-fiber.rb +0 -45
- data/lib/rest-core/app/em-http-request.rb +0 -9
- data/lib/rest-core/app/rest-client.rb +0 -41
- data/lib/rest-core/middleware/json_decode.rb +0 -93
- data/lib/rest-core/middleware/timeout/coolio_timer.rb +0 -10
- data/pending/test_multi.rb +0 -123
- data/pending/test_test_util.rb +0 -86
- data/test/test_json_decode.rb +0 -24
data/TODO.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
-
* HTTP method in Requested log
|
4
3
|
* middleware revisit (how to initialize?)
|
5
|
-
*
|
4
|
+
* streaming?
|
5
|
+
* connection pool?
|
6
|
+
* X-Method-Override
|
6
7
|
|
7
8
|
# BUG
|
8
9
|
|
@@ -11,10 +12,6 @@
|
|
11
12
|
# FEATURE
|
12
13
|
|
13
14
|
* middleware composer
|
14
|
-
*
|
15
|
-
* how to pass a arbitrary payload generator?
|
16
|
-
* RC::JsonRequest
|
17
|
-
* RC::FakeGetWithPayload
|
15
|
+
* headers and payload logs for CommonLogger
|
18
16
|
|
19
|
-
|
20
|
-
* options for apps
|
17
|
+
# rest-request
|
data/example/multi.rb
CHANGED
@@ -1,35 +1,42 @@
|
|
1
1
|
|
2
|
+
require 'fiber'
|
3
|
+
require 'em-http-request'
|
2
4
|
require 'rest-core'
|
3
|
-
require 'eventmachine'
|
4
|
-
RestCore::EmHttpRequest # there might be a autoload bug?
|
5
|
-
# omitting this line would cause
|
6
|
-
# stack level too deep (SystemStackError)
|
7
5
|
|
8
|
-
YourClient =
|
9
|
-
|
10
|
-
use
|
11
|
-
use
|
12
|
-
use
|
13
|
-
use s::Cache , nil, 3600
|
14
|
-
run s::Auto
|
6
|
+
YourClient = RC::Builder.client do
|
7
|
+
use RC::DefaultSite , 'https://api.github.com/users/'
|
8
|
+
use RC::JsonResponse, true
|
9
|
+
use RC::CommonLogger, method(:puts)
|
10
|
+
use RC::Cache , nil, 3600
|
15
11
|
end
|
16
12
|
|
17
13
|
client = YourClient.new
|
14
|
+
puts "rest-client with threads doing concurrent requests"
|
15
|
+
a = [client.get('cardinalblue')['name'], client.get('godfat')['name']]
|
16
|
+
puts "It's not blocking... but doing concurrent requests underneath"
|
17
|
+
p a # here we want the values, so it blocks here
|
18
|
+
puts "DONE"
|
19
|
+
|
20
|
+
puts; puts
|
21
|
+
|
22
|
+
puts "eventmachine with threads doing concurrent requests"
|
23
|
+
EM.run{
|
24
|
+
Thread.new{
|
25
|
+
p [client.get('cardinalblue')['name'], client.get('godfat')['name']]
|
26
|
+
puts "DONE"
|
27
|
+
EM.stop
|
28
|
+
}
|
29
|
+
puts "It's not blocking... but doing concurrent requests underneath"
|
30
|
+
}
|
31
|
+
|
32
|
+
puts; puts
|
33
|
+
|
34
|
+
puts "eventmachine with fibers doing concurrent requests"
|
18
35
|
EM.run{
|
19
36
|
Fiber.new{
|
20
|
-
|
21
|
-
|
22
|
-
client.get('cardinalblue'){ |response|
|
23
|
-
result[0] = response
|
24
|
-
fiber.resume(result) if result.size == 2
|
25
|
-
}
|
26
|
-
puts "It's not blocking..."
|
27
|
-
client.get('cardinalblue'){ |response|
|
28
|
-
result[1] = response
|
29
|
-
fiber.resume(result) if result.size == 2
|
30
|
-
}
|
31
|
-
p Fiber.yield
|
37
|
+
p [client.get('cardinalblue')['name'], client.get('godfat')['name']]
|
38
|
+
puts "DONE"
|
32
39
|
EM.stop
|
33
40
|
}.resume
|
34
|
-
puts "It's not blocking..."
|
41
|
+
puts "It's not blocking... but doing concurrent requests underneath"
|
35
42
|
}
|
data/example/simple.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
require 'rest-core'
|
3
|
+
|
4
|
+
YourClient = RC::Builder.client do
|
5
|
+
use RC::DefaultSite , 'https://api.github.com/users/'
|
6
|
+
use RC::JsonResponse, true
|
7
|
+
use RC::CommonLogger, method(:puts)
|
8
|
+
use RC::Cache , nil, 3600
|
9
|
+
end
|
10
|
+
|
11
|
+
client = YourClient.new(:cache => {})
|
12
|
+
p client.get('cardinalblue') # cache miss
|
13
|
+
puts
|
14
|
+
p client.get('cardinalblue') # cache hit
|
15
|
+
|
16
|
+
client.cache = false
|
17
|
+
|
18
|
+
puts "concurrent requests"
|
19
|
+
a = [client.get('cardinalblue')['name'], client.get('godfat')['name']]
|
20
|
+
puts "It's not blocking... but doing concurrent requests underneath"
|
21
|
+
p a # here we want the values, so it blocks here
|
22
|
+
puts "DONE"
|
23
|
+
|
24
|
+
puts "callback"
|
25
|
+
client.get('cardinalblue'){ |v| p v }
|
26
|
+
puts "It's not blocking... but doing concurrent requests underneath"
|
27
|
+
client.wait # we block here to wait for the request done
|
28
|
+
puts "DONE"
|
@@ -0,0 +1,194 @@
|
|
1
|
+
|
2
|
+
require 'fiber'
|
3
|
+
require 'em-http-request'
|
4
|
+
require 'rest-core'
|
5
|
+
RC.eagerload
|
6
|
+
|
7
|
+
def def_use_case name, &block
|
8
|
+
singleton_class.send(:define_method, "#{name}_", &block)
|
9
|
+
singleton_class.send(:define_method, name) do
|
10
|
+
@count ||= 0
|
11
|
+
printf "Use case #%02d: %s\n", @count+=1, name
|
12
|
+
puts '-' * 70
|
13
|
+
start = Time.now
|
14
|
+
send("#{name}_")
|
15
|
+
puts "Spent #{Time.now - start} seconds for this use case."
|
16
|
+
puts
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def q str, m=nil
|
21
|
+
p = lambda{ puts "\e[33m=> #{str.inspect}\e[0m" }
|
22
|
+
if m
|
23
|
+
m.synchronize(&p)
|
24
|
+
else
|
25
|
+
p.call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# ----------------------------------------------------------------------
|
30
|
+
|
31
|
+
def_use_case 'pure_ruby_single_request' do
|
32
|
+
q RC::Universal.new(:json_response => true).
|
33
|
+
get('https://api.github.com/users/godfat')['name']
|
34
|
+
end
|
35
|
+
|
36
|
+
def_use_case 'pure_ruby_concurrent_requests' do
|
37
|
+
client = RC::Universal.new(:json_response => true,
|
38
|
+
:site => 'https://api.github.com/users/')
|
39
|
+
q [client.get('godfat'), client.get('cardinalblue')].map{ |u| u['name'] }
|
40
|
+
end
|
41
|
+
|
42
|
+
def_use_case 'pure_ruby_cache_requests' do
|
43
|
+
client = RC::Universal.new(:json_response => true, :cache => {})
|
44
|
+
3.times{ q client.get('https://api.github.com/users/godfat')['name'] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def_use_case 'pure_ruby_callback_requests' do
|
48
|
+
m = Mutex.new
|
49
|
+
RC::Universal.new(:json_response => true ,
|
50
|
+
:site => 'https://api.github.com/users/' ,
|
51
|
+
:log_method => lambda{|str| m.synchronize{puts(str)}}).
|
52
|
+
get('godfat'){ |res|
|
53
|
+
q res['name'], m
|
54
|
+
}.
|
55
|
+
get('cardinalblue'){ |res|
|
56
|
+
q res['name'], m
|
57
|
+
}.wait
|
58
|
+
end
|
59
|
+
|
60
|
+
def_use_case 'pure_ruby_nested_concurrent_requests' do
|
61
|
+
m = Mutex.new
|
62
|
+
c = RC::Universal.new(:json_response => true ,
|
63
|
+
:site => 'https://api.github.com' ,
|
64
|
+
:log_method => lambda{|str| m.synchronize{puts(str)}})
|
65
|
+
|
66
|
+
%w[rubytaiwan godfat].each{ |user|
|
67
|
+
c.get("/users/#{user}/repos", :per_page => 100){ |repos|
|
68
|
+
rs = repos.reject{ |r| r['fork'] }
|
69
|
+
most_watched = rs.max_by{ |r| r['watchers'] }['name']
|
70
|
+
most_size = rs.max_by{ |r| r['size'] }['name']
|
71
|
+
|
72
|
+
watch_contri = c.get("/repos/#{user}/#{most_watched}/contributors")
|
73
|
+
size_contri = c.get("/repos/#{user}/#{most_size}/contributors")
|
74
|
+
|
75
|
+
most_watched_most_contri = watch_contri.max_by{ |c| c['contributions'] }
|
76
|
+
most_size_most_contri = size_contri.max_by{ |c| c['contributions'] }
|
77
|
+
|
78
|
+
q "Most contributed user for most watched: #{user}/#{most_watched}:", m
|
79
|
+
q most_watched_most_contri['login'], m
|
80
|
+
|
81
|
+
q "Most contributed user for most size : #{user}/#{most_size}:", m
|
82
|
+
q most_size_most_contri['login'], m
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
c.wait
|
87
|
+
end
|
88
|
+
|
89
|
+
# ----------------------------------------------------------------------
|
90
|
+
|
91
|
+
def_use_case 'eventmachine_fiber_single_request' do
|
92
|
+
EM.run{ Fiber.new{ pure_ruby_single_request_ ; EM.stop }.resume}
|
93
|
+
end
|
94
|
+
|
95
|
+
def_use_case 'eventmachine_fiber_concurrent_requests' do
|
96
|
+
EM.run{ Fiber.new{ pure_ruby_concurrent_requests_ ; EM.stop }.resume}
|
97
|
+
end
|
98
|
+
|
99
|
+
def_use_case 'eventmachine_fiber_cache_requests' do
|
100
|
+
EM.run{ Fiber.new{ pure_ruby_cache_requests_ ; EM.stop }.resume}
|
101
|
+
end
|
102
|
+
|
103
|
+
def_use_case 'eventmachine_fiber_callback_requests' do
|
104
|
+
EM.run{ Fiber.new{ pure_ruby_callback_requests_ ; EM.stop }.resume}
|
105
|
+
end
|
106
|
+
|
107
|
+
def_use_case 'eventmachine_fiber_nested_concurrent_requests' do
|
108
|
+
EM.run{ Fiber.new{ pure_ruby_nested_concurrent_requests_; EM.stop }.resume}
|
109
|
+
end
|
110
|
+
|
111
|
+
# ----------------------------------------------------------------------
|
112
|
+
|
113
|
+
def_use_case 'eventmachine_thread_single_request' do
|
114
|
+
EM.run{ Thread.new{ pure_ruby_single_request_ ; EM.stop } }
|
115
|
+
end
|
116
|
+
|
117
|
+
def_use_case 'eventmachine_thread_concurrent_requests' do
|
118
|
+
EM.run{ Thread.new{ pure_ruby_concurrent_requests_ ; EM.stop } }
|
119
|
+
end
|
120
|
+
|
121
|
+
def_use_case 'eventmachine_thread_cache_requests' do
|
122
|
+
EM.run{ Thread.new{ pure_ruby_cache_requests_ ; EM.stop } }
|
123
|
+
end
|
124
|
+
|
125
|
+
def_use_case 'eventmachine_thread_callback_requests' do
|
126
|
+
EM.run{ Thread.new{ pure_ruby_callback_requests_ ; EM.stop } }
|
127
|
+
end
|
128
|
+
|
129
|
+
def_use_case 'eventmachine_thread_nested_concurrent_requests' do
|
130
|
+
EM.run{ Thread.new{ pure_ruby_nested_concurrent_requests_; EM.stop } }
|
131
|
+
end
|
132
|
+
|
133
|
+
# ----------------------------------------------------------------------
|
134
|
+
|
135
|
+
def_use_case 'eventmachine_rest_client_single_request' do
|
136
|
+
EM.run{ pure_ruby_single_request_ ; EM.stop }
|
137
|
+
end
|
138
|
+
|
139
|
+
def_use_case 'eventmachine_rest_client_concurrent_requests' do
|
140
|
+
EM.run{ pure_ruby_concurrent_requests_ ; EM.stop }
|
141
|
+
end
|
142
|
+
|
143
|
+
def_use_case 'eventmachine_rest_client_cache_requests' do
|
144
|
+
EM.run{ pure_ruby_cache_requests_ ; EM.stop }
|
145
|
+
end
|
146
|
+
|
147
|
+
def_use_case 'eventmachine_rest_client_callback_requests' do
|
148
|
+
EM.run{ pure_ruby_callback_requests_ ; EM.stop }
|
149
|
+
end
|
150
|
+
|
151
|
+
def_use_case 'eventmachine_rest_client_nested_concurrent_requests' do
|
152
|
+
EM.run{ pure_ruby_nested_concurrent_requests_; EM.stop }
|
153
|
+
end
|
154
|
+
|
155
|
+
# ----------------------------------------------------------------------
|
156
|
+
|
157
|
+
def_use_case 'pure_ruby' do
|
158
|
+
pure_ruby_single_request
|
159
|
+
pure_ruby_concurrent_requests
|
160
|
+
pure_ruby_cache_requests
|
161
|
+
pure_ruby_callback_requests
|
162
|
+
pure_ruby_nested_concurrent_requests
|
163
|
+
end
|
164
|
+
|
165
|
+
def_use_case 'eventmachine_fiber' do
|
166
|
+
eventmachine_fiber_single_request
|
167
|
+
eventmachine_fiber_concurrent_requests
|
168
|
+
eventmachine_fiber_cache_requests
|
169
|
+
eventmachine_fiber_callback_requests
|
170
|
+
eventmachine_fiber_nested_concurrent_requests
|
171
|
+
end
|
172
|
+
|
173
|
+
def_use_case 'eventmachine_thread' do
|
174
|
+
eventmachine_thread_single_request
|
175
|
+
eventmachine_thread_concurrent_requests
|
176
|
+
eventmachine_thread_cache_requests
|
177
|
+
eventmachine_thread_callback_requests
|
178
|
+
eventmachine_thread_nested_concurrent_requests
|
179
|
+
end
|
180
|
+
|
181
|
+
def_use_case 'eventmachine_rest_client' do
|
182
|
+
eventmachine_rest_client_single_request
|
183
|
+
eventmachine_rest_client_concurrent_requests
|
184
|
+
eventmachine_rest_client_cache_requests
|
185
|
+
eventmachine_rest_client_callback_requests
|
186
|
+
eventmachine_rest_client_nested_concurrent_requests
|
187
|
+
end
|
188
|
+
|
189
|
+
# ----------------------------------------------------------------------
|
190
|
+
|
191
|
+
pure_ruby
|
192
|
+
eventmachine_fiber
|
193
|
+
eventmachine_thread
|
194
|
+
eventmachine_rest_client
|
data/lib/rest-core.rb
CHANGED
@@ -16,6 +16,9 @@ module RestCore
|
|
16
16
|
|
17
17
|
ASYNC = 'async.callback'
|
18
18
|
TIMER = 'async.timer'
|
19
|
+
FUTURE = 'async.future'
|
20
|
+
|
21
|
+
RootFiber = Fiber.respond_to?(:current) && Fiber.current
|
19
22
|
|
20
23
|
# core utilities
|
21
24
|
autoload :Builder , 'rest-core/builder'
|
@@ -46,34 +49,38 @@ module RestCore
|
|
46
49
|
autoload :ErrorDetectorHttp, 'rest-core/middleware/error_detector_http'
|
47
50
|
autoload :ErrorHandler , 'rest-core/middleware/error_handler'
|
48
51
|
autoload :FollowRedirect, 'rest-core/middleware/follow_redirect'
|
49
|
-
autoload :
|
52
|
+
autoload :JsonRequest , 'rest-core/middleware/json_request'
|
53
|
+
autoload :JsonResponse , 'rest-core/middleware/json_response'
|
50
54
|
autoload :Oauth1Header , 'rest-core/middleware/oauth1_header'
|
51
55
|
autoload :Oauth2Header , 'rest-core/middleware/oauth2_header'
|
52
56
|
autoload :Oauth2Query , 'rest-core/middleware/oauth2_query'
|
53
57
|
autoload :Timeout , 'rest-core/middleware/timeout'
|
54
58
|
|
55
|
-
#
|
56
|
-
autoload :Auto , 'rest-core/
|
57
|
-
autoload :Dry , 'rest-core/
|
58
|
-
autoload :RestClient , 'rest-core/
|
59
|
-
autoload :
|
60
|
-
autoload :CoolioAsync , 'rest-core/app/coolio-async'
|
61
|
-
autoload :CoolioFiber , 'rest-core/app/coolio-fiber'
|
62
|
-
autoload :EmHttpRequest , 'rest-core/app/em-http-request'
|
63
|
-
autoload :EmHttpRequestAsync, 'rest-core/app/em-http-request-async'
|
64
|
-
autoload :EmHttpRequestFiber, 'rest-core/app/em-http-request-fiber'
|
59
|
+
# engines
|
60
|
+
autoload :Auto , 'rest-core/engine/auto'
|
61
|
+
autoload :Dry , 'rest-core/engine/dry'
|
62
|
+
autoload :RestClient , 'rest-core/engine/rest-client'
|
63
|
+
autoload :EmHttpRequest , 'rest-core/engine/em-http-request'
|
65
64
|
|
66
65
|
# clients
|
67
66
|
autoload :Simple , 'rest-core/client/simple'
|
68
67
|
autoload :Universal , 'rest-core/client/universal'
|
68
|
+
|
69
|
+
# You might want to call this before launching your application in a
|
70
|
+
# threaded environment to avoid thread-safety issue in autoload.
|
71
|
+
def self.eagerload const=self, loaded={}
|
72
|
+
return if loaded[const.name]
|
73
|
+
loaded[const.name] = true
|
74
|
+
const.constants.each{ |n|
|
75
|
+
begin
|
76
|
+
c = const.const_get(n)
|
77
|
+
rescue LoadError => e
|
78
|
+
warn "RestCore: WARN: #{e} for #{const}\n" \
|
79
|
+
" from #{e.backtrace.grep(/top.+required/).first}"
|
80
|
+
end
|
81
|
+
eagerload(c, loaded) if c.respond_to?(:constants) && !loaded[n]
|
82
|
+
}
|
83
|
+
end
|
69
84
|
end
|
70
85
|
|
71
86
|
RC = RestCore unless Object.const_defined?(:RC)
|
72
|
-
|
73
|
-
begin
|
74
|
-
require 'fiber'
|
75
|
-
rescue LoadError
|
76
|
-
end
|
77
|
-
# assume we would always require 'rest-core' in root fiber
|
78
|
-
RestCore::RootFiber = Fiber.current if Object.const_defined?(:Fiber) &&
|
79
|
-
Fiber.respond_to?(:current)
|
data/lib/rest-core/builder.rb
CHANGED
data/lib/rest-core/client.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
|
2
2
|
require 'rest-core'
|
3
3
|
|
4
|
+
require 'weakref'
|
5
|
+
|
4
6
|
module RestCore::Client
|
5
7
|
include RestCore
|
6
8
|
|
@@ -48,10 +50,12 @@ module RestCore::Client
|
|
48
50
|
mod.send(:include, accessor)
|
49
51
|
end
|
50
52
|
|
51
|
-
attr_reader :app, :dry
|
53
|
+
attr_reader :app, :dry, :futures
|
52
54
|
def initialize o={}
|
53
|
-
@app ||= self.class.builder.to_app
|
55
|
+
@app ||= self.class.builder.to_app # lighten! would reinitialize anyway
|
54
56
|
@dry ||= self.class.builder.to_app(Dry)
|
57
|
+
@futures = [] # don't record any futures in lighten!
|
58
|
+
@mutex = nil # for locking futures, lazily initialized for serialization
|
55
59
|
o.each{ |key, value| send("#{key}=", value) if respond_to?("#{key}=") }
|
56
60
|
end
|
57
61
|
|
@@ -81,12 +85,27 @@ module RestCore::Client
|
|
81
85
|
dup.lighten!(o)
|
82
86
|
end
|
83
87
|
|
88
|
+
def wait
|
89
|
+
return self if futures.empty?
|
90
|
+
current_futures = nil
|
91
|
+
mutex.synchronize{
|
92
|
+
current_futures = futures.dup
|
93
|
+
futures.clear
|
94
|
+
}
|
95
|
+
current_futures.each{ |f|
|
96
|
+
begin
|
97
|
+
f.wait
|
98
|
+
rescue WeakRef::RefError # it's gc'ed after we think it's alive
|
99
|
+
end if f.weakref_alive?
|
100
|
+
}
|
101
|
+
wait
|
102
|
+
end
|
103
|
+
|
84
104
|
def url path, query={}, opts={}
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
DRY => true}.merge(opts))))
|
105
|
+
dry.call(build_env({
|
106
|
+
REQUEST_PATH => path,
|
107
|
+
REQUEST_QUERY => query,
|
108
|
+
DRY => true}.merge(opts)), &Middleware.method(:request_uri))
|
90
109
|
end
|
91
110
|
|
92
111
|
def get path, query={}, opts={}, &cb
|
@@ -151,7 +170,7 @@ module RestCore::Client
|
|
151
170
|
end
|
152
171
|
end
|
153
172
|
|
154
|
-
def request_full env, app=app
|
173
|
+
def request_full env, app=app, &k
|
155
174
|
response = app.call(build_env(
|
156
175
|
{REQUEST_METHOD => :get,
|
157
176
|
REQUEST_PATH => '/' ,
|
@@ -160,11 +179,16 @@ module RestCore::Client
|
|
160
179
|
REQUEST_HEADERS => {} ,
|
161
180
|
FAIL => [] ,
|
162
181
|
LOG => [] ,
|
163
|
-
ASYNC =>
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
182
|
+
ASYNC => !!k }.merge(env)),
|
183
|
+
&(k || Middleware.id))
|
184
|
+
|
185
|
+
# under ASYNC callback, response might not be a response hash
|
186
|
+
# in that case (maybe in a user created engine), Client#wait
|
187
|
+
# won't work because we have no way to track the future.
|
188
|
+
if response.kind_of?(Hash) && RestCore.const_defined?(:Future) &&
|
189
|
+
response[FUTURE].kind_of?(Future)
|
190
|
+
mutex.synchronize{ futures << WeakRef.new(response[FUTURE]) }
|
191
|
+
end
|
168
192
|
|
169
193
|
if block_given?
|
170
194
|
self
|
@@ -174,26 +198,15 @@ module RestCore::Client
|
|
174
198
|
end
|
175
199
|
|
176
200
|
def build_env env={}
|
177
|
-
string_keys(attributes).merge(string_keys(env))
|
201
|
+
Middleware.string_keys(attributes).merge(Middleware.string_keys(env))
|
178
202
|
end
|
179
203
|
# ------------------------ instance ---------------------
|
180
204
|
|
181
205
|
|
182
206
|
|
183
207
|
private
|
184
|
-
def
|
185
|
-
|
186
|
-
if v.kind_of?(Hash)
|
187
|
-
r[k.to_s] = case k.to_s
|
188
|
-
when REQUEST_QUERY, REQUEST_PAYLOAD, REQUEST_HEADERS
|
189
|
-
string_keys(v)
|
190
|
-
else; v
|
191
|
-
end
|
192
|
-
else
|
193
|
-
r[k.to_s] = v
|
194
|
-
end
|
195
|
-
r
|
196
|
-
}
|
208
|
+
def mutex
|
209
|
+
@mutex ||= Mutex.new
|
197
210
|
end
|
198
211
|
|
199
212
|
def lighten_hash hash
|