ocean-rails 3.8.8 → 3.8.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ocean/api.rb +103 -50
- data/lib/ocean/api_remote_resource.rb +20 -19
- data/lib/ocean/continuation_experiments.rb +139 -0
- data/lib/ocean/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25d86f8d69015565033c12e60edb7017186d6829
|
4
|
+
data.tar.gz: 83a3f9b85153a9708225aa5ab46ad58e5b7837fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7540bf449eb3e0d3aa5755f2fa75fd9ed22b8e74f6fd7409908a4836bd6f3e858e4f82cadbeb50a6236fe50d17d39cc891b9a87068347d14201e7ba8b084a1e
|
7
|
+
data.tar.gz: 00618f27d71e1446ac3f1135757c8944a0b8dd69873d5978166f6d058b6694e47173892ccbbf22bd4104511dc1afc4b41d5a8542e9d3acff1cf783c732b24448
|
data/lib/ocean/api.rb
CHANGED
@@ -172,7 +172,8 @@ class Api
|
|
172
172
|
reauthentication: true,
|
173
173
|
ssl_verifypeer: true, ssl_verifyhost: 2,
|
174
174
|
retries: 0, backoff_time: 1, backoff_rate: 0.9, backoff_max: 30,
|
175
|
-
x_metadata: Thread.current[:metadata]
|
175
|
+
x_metadata: Thread.current[:metadata],
|
176
|
+
&block)
|
176
177
|
# Set up the request
|
177
178
|
headers['Accept'] = "application/json"
|
178
179
|
headers['Content-Type'] = "application/json" if [:post, :put].include?(http_method)
|
@@ -181,63 +182,109 @@ class Api
|
|
181
182
|
headers['X-API-Token'] = x_api_token if x_api_token.present?
|
182
183
|
headers['X-Metadata'] = x_metadata if x_metadata.present?
|
183
184
|
|
184
|
-
response = nil
|
185
185
|
tries_done = 0
|
186
186
|
max_tries = [:get, "GET", "get"].include?(http_method) ? retries + 1 : 1
|
187
|
-
while (true) do
|
188
|
-
tries_done += 1
|
189
|
-
last_try = tries_done >= max_tries
|
190
|
-
|
191
|
-
while (true) do
|
192
|
-
request = Typhoeus::Request.new(url,
|
193
|
-
method: http_method,
|
194
|
-
headers: headers,
|
195
|
-
params: args,
|
196
|
-
body: body,
|
197
|
-
ssl_verifypeer: ssl_verifypeer,
|
198
|
-
ssl_verifyhost: ssl_verifyhost)
|
199
|
-
# Run it
|
200
|
-
response = Response.new(request.run)
|
201
|
-
# If successful, return
|
202
|
-
return response if response.success?
|
203
|
-
|
204
|
-
# Not successful, deal with it
|
205
|
-
if last_try
|
206
|
-
raise Api::TimeoutError, "Api.request timed out" if response.timed_out?
|
207
|
-
raise Api::NoResponseError, "Api.request could not obtain a response" if response.status == 0
|
208
|
-
else
|
209
|
-
break if response.timed_out?
|
210
|
-
break if response.status == 0
|
211
|
-
end
|
212
187
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
188
|
+
@hydra ||= Typhoeus::Hydra.hydra
|
189
|
+
request = nil # For clarity
|
190
|
+
response = nil
|
191
|
+
|
192
|
+
# This is a Proc when run queues the request and schedules retries
|
193
|
+
enqueue_request = Proc.new do
|
194
|
+
# First construct a request. It will not be sent yet.
|
195
|
+
request = Typhoeus::Request.new(url,
|
196
|
+
method: http_method,
|
197
|
+
headers: headers,
|
198
|
+
params: args,
|
199
|
+
body: body,
|
200
|
+
ssl_verifypeer: ssl_verifypeer,
|
201
|
+
ssl_verifyhost: ssl_verifyhost)
|
202
|
+
|
203
|
+
# Define a callback to process the response and do retries
|
204
|
+
request.on_complete do |typhoeus_response|
|
205
|
+
response = Response.new(typhoeus_response)
|
206
|
+
case response.status
|
207
|
+
when 100..199
|
208
|
+
enqueue_request.call # Ignore and retry
|
209
|
+
when 200..299, 304
|
210
|
+
# Success, call the post-processor if any
|
211
|
+
if block
|
212
|
+
response = block.call response
|
222
213
|
end
|
223
|
-
|
224
|
-
|
225
|
-
|
214
|
+
when 300..399
|
215
|
+
nil # Done, redirect
|
216
|
+
when 400, 419
|
217
|
+
if reauthentication && x_api_token.present?
|
218
|
+
# Re-authenticate and retry
|
219
|
+
if credentials
|
220
|
+
x_api_token = Api.authenticate(*Api.decode_credentials(credentials))
|
221
|
+
headers['X-API-Token'] = x_api_token
|
222
|
+
else
|
223
|
+
Api.reset_service_token
|
224
|
+
headers['X-API-Token'] = Api.service_token
|
225
|
+
end
|
226
|
+
reauthentication = false # This prevents us from ending up here twice
|
227
|
+
enqueue_request.call
|
228
|
+
else
|
229
|
+
nil # Done, fail
|
230
|
+
end
|
231
|
+
when 400..499
|
232
|
+
nil # Done, fail
|
226
233
|
else
|
227
|
-
# Retry
|
228
|
-
|
234
|
+
# Retry if there are any retries left
|
235
|
+
if retries > 0
|
236
|
+
retries -= 1
|
237
|
+
sleep backoff_time
|
238
|
+
backoff_time = [backoff_time + backoff_time * backoff_rate, 30].min
|
239
|
+
enqueue_request.call
|
240
|
+
else
|
241
|
+
nil # Done, don't retry
|
242
|
+
end
|
229
243
|
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Finally, queue the request (and its callback) for execution
|
247
|
+
@hydra.queue request
|
248
|
+
# Return nil, to emphasise that the side effects are what's important
|
249
|
+
nil
|
250
|
+
end
|
230
251
|
|
252
|
+
# So create and enqueue the request
|
253
|
+
enqueue_request.call
|
254
|
+
# Run it, unless we're accumulating calls inside an Api.simultaneously block
|
255
|
+
unless @inside_simultaneously
|
256
|
+
# The next line blocks until completed, possibly after several retries
|
257
|
+
@hydra.run
|
258
|
+
if response.is_a?(Response)
|
259
|
+
# Raise any exceptions
|
260
|
+
raise Api::TimeoutError, "Api.request timed out" if response.timed_out?
|
261
|
+
raise Api::NoResponseError, "Api.request could not obtain a response" if response.status == 0
|
231
262
|
end
|
232
|
-
|
233
|
-
break if last_try
|
234
|
-
sleep backoff_time
|
235
|
-
seconds = [backoff_time + backoff_time * backoff_rate, 30].min
|
236
|
-
backoff_time = seconds
|
263
|
+
return response
|
237
264
|
end
|
238
|
-
# Return
|
239
|
-
|
240
|
-
end
|
265
|
+
# Return nil: we have no response yet
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
def self.simultaneously (&block)
|
271
|
+
raise "block required" unless block
|
272
|
+
@inside_simultaneously = true
|
273
|
+
@hydra = nil
|
274
|
+
block.call
|
275
|
+
ensure
|
276
|
+
@inside_simultaneously = false
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
def self.simultaneously?
|
281
|
+
@inside_simultaneously ||= false
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
# http://liufengyun.chaos-lab.com/prog/2013/10/23/continuation-in-ruby.html
|
286
|
+
# http://gnuu.org/2009/03/21/demystifying-continuations-in-ruby/
|
287
|
+
# http://www.ruby-doc.org/core-2.1.3/Continuation.html
|
241
288
|
|
242
289
|
|
243
290
|
class TimeoutError < StandardError; end
|
@@ -556,6 +603,7 @@ class Api
|
|
556
603
|
h
|
557
604
|
end
|
558
605
|
|
606
|
+
|
559
607
|
#
|
560
608
|
# Takes the same args as +.async_job_body+, post an +AsyncJob+ and returns the +Response+.
|
561
609
|
# The +AsyncJob+ is always created as the service +ApiUser+; the job carries its own
|
@@ -564,8 +612,13 @@ class Api
|
|
564
612
|
# +:job+ as the only parameter.
|
565
613
|
#
|
566
614
|
# A block may be given. It will be called with any +TimeoutError+ or +NoResponseError+,
|
567
|
-
# which allows +Api.run_async_job+ to be used very tersely in controllers.
|
615
|
+
# which allows +Api.run_async_job+ to be used very tersely in controllers, e.g.:
|
568
616
|
#
|
617
|
+
# Api.run_async_job(...) do |e|
|
618
|
+
# render_api_error 422, e.message
|
619
|
+
# return
|
620
|
+
# end
|
621
|
+
#
|
569
622
|
def self.run_async_job(href=nil, method=nil, job: nil, **keywords, &block)
|
570
623
|
job ||= async_job_body(href, method, **keywords)
|
571
624
|
Api.request "#{INTERNAL_OCEAN_API_URL}/v1/async_jobs", :post,
|
@@ -271,7 +271,7 @@ class Api
|
|
271
271
|
# Instance method to GET a resource or any of its hyperlinks. Will raise exceptions
|
272
272
|
# if they occur, otherwise will return the resource itself.
|
273
273
|
#
|
274
|
-
def get!(hlink=nil)
|
274
|
+
def get!(hlink=nil, args: nil)
|
275
275
|
RemoteResource._retrieve self unless present?
|
276
276
|
hlink = hlink.to_s if hlink
|
277
277
|
if !hlink || hlink == 'self'
|
@@ -305,7 +305,7 @@ class Api
|
|
305
305
|
# Instance method to do a PUT to a resource or any of its hyperlinks. Will raise exceptions
|
306
306
|
# if they occur, otherwise will return the resource itself.
|
307
307
|
#
|
308
|
-
def put!(hlink=nil, body: {})
|
308
|
+
def put!(hlink=nil, args: nil, body: {})
|
309
309
|
get!
|
310
310
|
hlink = hlink.to_s if hlink
|
311
311
|
if !hlink || hlink == 'self'
|
@@ -315,7 +315,7 @@ class Api
|
|
315
315
|
hl_data = hyperlink[hlink]
|
316
316
|
raise HyperlinkMissing, "#{resource_type} has no #{hlink} hyperlink" unless hl_data
|
317
317
|
hl_res = RemoteResource.new(hl_data['href'])
|
318
|
-
hl_res.send :_modify, body
|
318
|
+
hl_res.send :_modify, body, args: args
|
319
319
|
hl_res
|
320
320
|
end
|
321
321
|
end
|
@@ -325,7 +325,7 @@ class Api
|
|
325
325
|
# exceptions if they occur, but will always return the resource itself.
|
326
326
|
# A missing hyperlink, though, will always raise a HyperlinkMissing exception.
|
327
327
|
#
|
328
|
-
def put(hlink=nil, body: {})
|
328
|
+
def put(hlink=nil, args: nil, body: {})
|
329
329
|
get
|
330
330
|
hlink = hlink.to_s if hlink
|
331
331
|
if !hlink || hlink == 'self'
|
@@ -335,7 +335,7 @@ class Api
|
|
335
335
|
hl_data = hyperlink[hlink]
|
336
336
|
raise HyperlinkMissing, "#{resource_type} has no #{hlink} hyperlink" unless hl_data
|
337
337
|
hl_res = RemoteResource.new(hl_data['href'])
|
338
|
-
hl_res.send :_modify, body
|
338
|
+
hl_res.send :_modify, body, args: args
|
339
339
|
hl_res
|
340
340
|
end
|
341
341
|
end
|
@@ -345,12 +345,12 @@ class Api
|
|
345
345
|
# Instance method to do a POST to a resource or any of its hyperlinks. Will raise exceptions
|
346
346
|
# if they occur, otherwise will return the resource itself.
|
347
347
|
#
|
348
|
-
def post!(hlink=nil, body: {})
|
348
|
+
def post!(hlink=nil, args: nil, body: {})
|
349
349
|
get!
|
350
350
|
hlink = (hlink || 'self').to_s
|
351
351
|
hl_data = hyperlink[hlink]
|
352
352
|
raise HyperlinkMissing, "#{resource_type} has no #{hlink} hyperlink" unless hl_data
|
353
|
-
_create(hl_data['href'], body)
|
353
|
+
_create(hl_data['href'], body, args: args)
|
354
354
|
end
|
355
355
|
|
356
356
|
#
|
@@ -358,12 +358,12 @@ class Api
|
|
358
358
|
# exceptions if they occur, but will always return the resource itself.
|
359
359
|
# A missing hyperlink, though, will always raise a HyperlinkMissing exception.
|
360
360
|
#
|
361
|
-
def post(hlink=nil, body: {})
|
361
|
+
def post(hlink=nil, args: nil, body: {})
|
362
362
|
get
|
363
363
|
hlink = (hlink || 'self').to_s
|
364
364
|
hl_data = hyperlink[hlink]
|
365
365
|
raise HyperlinkMissing, "#{resource_type} has no #{hlink} hyperlink" unless hl_data
|
366
|
-
_create(hl_data['href'], body) rescue nil
|
366
|
+
_create(hl_data['href'], body, args: args) rescue nil
|
367
367
|
end
|
368
368
|
|
369
369
|
|
@@ -371,12 +371,12 @@ class Api
|
|
371
371
|
# Instance method to do a DELETE to a resource or any of its hyperlinks. Will raise exceptions
|
372
372
|
# if they occur, otherwise will return the resource itself.
|
373
373
|
#
|
374
|
-
def delete!(hlink=nil)
|
374
|
+
def delete!(hlink=nil, args: nil)
|
375
375
|
get!
|
376
376
|
hlink = (hlink || 'self').to_s
|
377
377
|
hl_data = hyperlink[hlink]
|
378
378
|
raise HyperlinkMissing, "#{resource_type} has no #{hlink} hyperlink" unless hl_data
|
379
|
-
_destroy(hl_data['href'])
|
379
|
+
_destroy(hl_data['href'], args: args)
|
380
380
|
self
|
381
381
|
end
|
382
382
|
|
@@ -385,12 +385,12 @@ class Api
|
|
385
385
|
# exceptions if they occur, but will always return the resource itself.
|
386
386
|
# A missing hyperlink, though, will always raise a HyperlinkMissing exception.
|
387
387
|
#
|
388
|
-
def delete(hlink=nil)
|
388
|
+
def delete(hlink=nil, args: nil)
|
389
389
|
get
|
390
390
|
hlink = (hlink || 'self').to_s
|
391
391
|
hl_data = hyperlink[hlink]
|
392
392
|
raise HyperlinkMissing, "#{resource_type} has no #{hlink} hyperlink" unless hl_data
|
393
|
-
_destroy(hl_data['href']) rescue nil
|
393
|
+
_destroy(hl_data['href'], args: args) rescue nil
|
394
394
|
self
|
395
395
|
end
|
396
396
|
|
@@ -466,6 +466,7 @@ class Api
|
|
466
466
|
rr
|
467
467
|
end
|
468
468
|
|
469
|
+
|
469
470
|
def _conditional_get
|
470
471
|
credentials, token = RemoteResource._credentials(self)
|
471
472
|
response = Api.request Api.internalize_uri(uri), :get,
|
@@ -515,12 +516,12 @@ class Api
|
|
515
516
|
end
|
516
517
|
|
517
518
|
|
518
|
-
def _modify(body)
|
519
|
+
def _modify(body, args: nil)
|
519
520
|
credentials, token = RemoteResource._credentials(self)
|
520
521
|
response = Api.request Api.internalize_uri(uri), :put, headers: {}, body: body.to_json,
|
521
522
|
credentials: credentials, x_api_token: token,
|
522
523
|
retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
|
523
|
-
backoff_max: backoff_max
|
524
|
+
backoff_max: backoff_max, args: args
|
524
525
|
self.response = response
|
525
526
|
self.status = response.status
|
526
527
|
self.status_message = response.message
|
@@ -540,12 +541,12 @@ class Api
|
|
540
541
|
end
|
541
542
|
|
542
543
|
|
543
|
-
def _create(post_uri, body)
|
544
|
+
def _create(post_uri, body, args: nil)
|
544
545
|
credentials, token = RemoteResource._credentials(self)
|
545
546
|
response = Api.request Api.internalize_uri(post_uri), :post, headers: {}, body: body.to_json,
|
546
547
|
credentials: credentials, x_api_token: token,
|
547
548
|
retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
|
548
|
-
backoff_max: backoff_max
|
549
|
+
backoff_max: backoff_max, args: args
|
549
550
|
self.response = response
|
550
551
|
self.status = response.status
|
551
552
|
self.status_message = response.message
|
@@ -564,12 +565,12 @@ class Api
|
|
564
565
|
end
|
565
566
|
|
566
567
|
|
567
|
-
def _destroy(delete_uri)
|
568
|
+
def _destroy(delete_uri, args: nil)
|
568
569
|
credentials, token = RemoteResource._credentials(self)
|
569
570
|
response = Api.request Api.internalize_uri(delete_uri), :delete, headers: {},
|
570
571
|
credentials: credentials, x_api_token: token,
|
571
572
|
retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
|
572
|
-
backoff_max: backoff_max
|
573
|
+
backoff_max: backoff_max, args: args
|
573
574
|
self.response = response
|
574
575
|
self.status = response.status
|
575
576
|
self.status_message = response.message
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'continuation'
|
2
|
+
|
3
|
+
# Api.simultaneously takes a block. It executes the block straight away.
|
4
|
+
#
|
5
|
+
# When executing inside an Api.simultaneously block, Api.request only enqueues its request,
|
6
|
+
# but doesn't run it. They register on_complete handlers that will be run asynchronously
|
7
|
+
# when Api.simultaneously fires hydra to run after the block.
|
8
|
+
#
|
9
|
+
# When Api.request runs outside Api.simultaneously, it simply returns an Api::Response.
|
10
|
+
# When running inside Api.simultaneously, however, Api.request creates a continuation
|
11
|
+
# and registers it with Api.simultaneously. After the block has run, Api.simultaneously
|
12
|
+
# invokes each continuation in turn with another continuation to allow the next
|
13
|
+
# continuation to be called. When there are no more continuations to process,
|
14
|
+
# Api.simultaneously returns normally. In this way, all continuations are executed serially
|
15
|
+
# to process the Api.request results, no matter who called them or from where.
|
16
|
+
|
17
|
+
class Quux
|
18
|
+
|
19
|
+
require 'continuation'
|
20
|
+
|
21
|
+
|
22
|
+
def self.simultaneously?
|
23
|
+
!!@continuations
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.accumulating?
|
27
|
+
@accumulating
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def self.simultaneously
|
32
|
+
raise "Api.simultaneously may not be nested" if simultaneously?
|
33
|
+
@continuations = []
|
34
|
+
# Execute the block
|
35
|
+
begin
|
36
|
+
puts "--------------------------- ACCUMULATING ------------------------"
|
37
|
+
@accumulating = true
|
38
|
+
yield @continuations
|
39
|
+
ensure
|
40
|
+
@accumulating = false
|
41
|
+
end
|
42
|
+
|
43
|
+
# The continuations array should now have entries. Create a continuation
|
44
|
+
# to return to this point.
|
45
|
+
begin
|
46
|
+
puts '', "--------------------------- EXECUTING ------------------------"
|
47
|
+
cc, results, result = callcc { |cc, results, result| [cc, [], :init] }
|
48
|
+
# '-BACK AT TOP LEVEL-----', [cc, results, result].inspect
|
49
|
+
raise "must be a continuation" unless cc.is_a? Continuation
|
50
|
+
results << result if result && result != :init
|
51
|
+
# Call the first accumulated continuation (if any) with it. When they call
|
52
|
+
# here, the next continuation will be called (if any). This is a loop,
|
53
|
+
# continuation style.
|
54
|
+
if @continuations.size > 0
|
55
|
+
@continuations.shift.call(cc, results)
|
56
|
+
end
|
57
|
+
ensure
|
58
|
+
# No longer nested
|
59
|
+
@continuations = nil
|
60
|
+
end
|
61
|
+
results
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def self.doubler (arg, k: nil)
|
66
|
+
raise "K is not a continuation: #{k.inspect}" if k && !k.is_a?(Continuation)
|
67
|
+
cc, acc = callcc { |cc, acc| [cc, acc] }
|
68
|
+
puts '', '-CONTINUATION CALL TO DOUBLER-----', [cc, acc].inspect
|
69
|
+
return cc if accumulating?
|
70
|
+
|
71
|
+
# Execution phase
|
72
|
+
|
73
|
+
# Calculate a result
|
74
|
+
result = arg * 2 # Calculate the result
|
75
|
+
|
76
|
+
# Transfer back
|
77
|
+
k.call(k, result) if k # Go elsewhere if k is there (e.g. to a nested caller)
|
78
|
+
cc.call(cc, acc, result) # Return to caller
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def self.nested(arg, k: nil)
|
83
|
+
raise "K is not a continuation: #{k.inspect}" if k && !k.is_a?(Continuation)
|
84
|
+
cc, acc = callcc { |cc, acc| [cc, acc] }
|
85
|
+
puts '', '-CONTINUATION CALL TO NESTED-----', [cc, acc].inspect
|
86
|
+
return cc if accumulating?
|
87
|
+
|
88
|
+
# Execution phase
|
89
|
+
# Set up the overriding continuation for the callee
|
90
|
+
callee_k, result = callcc { |callee_k, result| [callee_k, result] }
|
91
|
+
# The callee returns to the following statement
|
92
|
+
result = doubler(arg, k: callee_k) unless result # Can't be nil or false (fix later)
|
93
|
+
|
94
|
+
# Calculate a result
|
95
|
+
result = "#{result} processed"
|
96
|
+
|
97
|
+
# Transfer back
|
98
|
+
k.call(k, result) if k # Go elsewhere if k is there (e.g. to a nested caller)
|
99
|
+
cc.call(cc, acc, result) # Return to caller
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def self.deeply_nested(arg, k: nil)
|
104
|
+
raise "K is not a continuation: #{k.inspect}" if k && !k.is_a?(Continuation)
|
105
|
+
cc, acc = callcc { |cc, acc| [cc, acc] }
|
106
|
+
puts '', '-CONTINUATION CALL TO DEEPLY_NESTED-----', [cc, acc].inspect
|
107
|
+
return cc if accumulating?
|
108
|
+
|
109
|
+
# Execution phase
|
110
|
+
# Set up the overriding continuation for the callee
|
111
|
+
callee_k, result = callcc { |callee_k, result| [callee_k, result] }
|
112
|
+
# The callee returns to the following statement
|
113
|
+
result = nested(arg, k: callee_k) unless result # Can't be nil or false (fix later)
|
114
|
+
|
115
|
+
# Calculate a result
|
116
|
+
result = "#{result} more deeply"
|
117
|
+
|
118
|
+
# Transfer back
|
119
|
+
k.call(k, result) if k # Go elsewhere if k is there (e.g. to a nested caller)
|
120
|
+
cc.call(cc, acc, result) # Return to caller
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
# This accumulates stuff
|
128
|
+
res = Quux.simultaneously do |r|
|
129
|
+
r << Quux.doubler(24)
|
130
|
+
r << Quux.nested("hej")
|
131
|
+
r << Quux.doubler(100)
|
132
|
+
r << Quux.deeply_nested("woo")
|
133
|
+
r << Quux.doubler(1)
|
134
|
+
end
|
135
|
+
|
136
|
+
puts
|
137
|
+
puts res.inspect
|
138
|
+
puts
|
139
|
+
|
data/lib/ocean/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ocean-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.8.
|
4
|
+
version: 3.8.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Bengtson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|
@@ -252,6 +252,7 @@ files:
|
|
252
252
|
- lib/ocean/api.rb
|
253
253
|
- lib/ocean/api_remote_resource.rb
|
254
254
|
- lib/ocean/api_resource.rb
|
255
|
+
- lib/ocean/continuation_experiments.rb
|
255
256
|
- lib/ocean/engine.rb
|
256
257
|
- lib/ocean/flooding.rb
|
257
258
|
- lib/ocean/ocean_application_controller.rb
|