async_io 0.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.
- checksums.yaml +7 -0
- data/lib/async_io/base.rb +97 -0
- data/lib/async_io/load.rb +1 -0
- data/lib/async_io/rescuer.rb +19 -0
- data/lib/async_io/version.rb +3 -0
- data/lib/async_io/worker.rb +80 -0
- data/lib/async_io.rb +34 -0
- data/spec/async_io/base_spec.rb +140 -0
- data/spec/async_io/rescuer_spec.rb +49 -0
- data/spec/async_io/worker_spec.rb +80 -0
- data/spec/spec_helper.rb +30 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6987084439444f8b65360b694efe1367a3abda31
|
4
|
+
data.tar.gz: 875b9b38b82ed030e711d84790029f1d0034bbc5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 994b5606932c7a7e2b9c5d3052f1468a9a08a85448ab009801a00da31b7d8c09328747f95af1ef5d4d4624345bf0434a8caa27d9c3336ace26825e4b2d30c8d1
|
7
|
+
data.tar.gz: 24629df537bf4b3f7313a2fbb5340a3577d4733b24394cb42ac854407f532d13708f6c14a1dc12843ca744d8c0a60d03b178d2d89e8d10ccfda2fca1adcbcdba
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'async_io/worker'
|
3
|
+
require 'async_io/rescuer'
|
4
|
+
|
5
|
+
module AsyncIO
|
6
|
+
class Base
|
7
|
+
include AsyncIO::Rescuer
|
8
|
+
|
9
|
+
##
|
10
|
+
# Default:
|
11
|
+
# Number of threads to be spanwed is 1
|
12
|
+
#
|
13
|
+
# NOTE: Any sort of exception raised while
|
14
|
+
# 'getting' a job done will not be raised at all.
|
15
|
+
# Instead it will be logged to a specified log file.
|
16
|
+
#
|
17
|
+
# Whenever an exception is raised, the thread that the
|
18
|
+
# exception was raised from is killed, so we need a
|
19
|
+
# way to prevent threads from being killed. Therefore it
|
20
|
+
# rescues all exceptions raised and logs them.
|
21
|
+
#
|
22
|
+
attr_reader :queue, :threads
|
23
|
+
attr_accessor :logger
|
24
|
+
def initialize(n_threads=1)
|
25
|
+
@logger = AsyncIO::Logger
|
26
|
+
@queue = Queue.new
|
27
|
+
@threads = []
|
28
|
+
n_threads.times { @threads << Thread.new { consumer } }
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Ruby Queue#pop sets non_block to false.
|
33
|
+
# It waits until data is pushed on to
|
34
|
+
# the queue and then process it.
|
35
|
+
#
|
36
|
+
def consumer
|
37
|
+
rescuer do
|
38
|
+
while worker = queue.pop
|
39
|
+
worker.call
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
private(:consumer)
|
44
|
+
|
45
|
+
##
|
46
|
+
# It creates a new Worker, pushes it onto the queue,
|
47
|
+
# whenever a 'job' (i.e a Ruby object ) is finished
|
48
|
+
# it calls the payload and passes the result of job
|
49
|
+
# to it.
|
50
|
+
#
|
51
|
+
# For example:
|
52
|
+
#
|
53
|
+
# def aget_user(uid, &payload)
|
54
|
+
# worker(payload) do
|
55
|
+
# User.find(ui)
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# It returns the worker created for a particular job
|
60
|
+
# which you could send message +done+ to it in order
|
61
|
+
# to retrieve its job done.
|
62
|
+
# see prediction_io/worker.rb
|
63
|
+
#
|
64
|
+
# For example:
|
65
|
+
# result = aget_user(1) { |u| Logger.info(u.name) }
|
66
|
+
#
|
67
|
+
# # job may take a while to be done...
|
68
|
+
#
|
69
|
+
# user = result.done
|
70
|
+
# user.name
|
71
|
+
# => "John"
|
72
|
+
#
|
73
|
+
# NOTE: Whenever you use the snippet above, if the job
|
74
|
+
# has not been finished yet you will get +false+
|
75
|
+
# whenever you send a message +job+ to it. Once
|
76
|
+
# job is finished you will be able to get its result.
|
77
|
+
#
|
78
|
+
def worker(payload, &job)
|
79
|
+
rescuer do
|
80
|
+
Worker.new(payload, job).tap { |w|
|
81
|
+
queue.push(w)
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# Perform any sort of task that needs to be
|
88
|
+
# asynchronously done.
|
89
|
+
# NOTE: It does not return anything, as it receives
|
90
|
+
# and empty job. ( i.e empty block of code )
|
91
|
+
#
|
92
|
+
def async(&payload)
|
93
|
+
worker(payload) { }
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'async_io/async_io'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AsyncIO
|
2
|
+
module Rescuer
|
3
|
+
|
4
|
+
##
|
5
|
+
# Rescues any sort of exception raised and
|
6
|
+
# log it to a default logger, returns :rescued
|
7
|
+
# if any exception was raised.
|
8
|
+
#
|
9
|
+
def rescuer
|
10
|
+
begin
|
11
|
+
yield
|
12
|
+
rescue Exception => notice
|
13
|
+
AsyncIO::Logger.error("[-:AsyncIO::AsyncIO:-] - #{notice}\n")
|
14
|
+
:rescued
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'timeout'
|
3
|
+
require 'async_io/rescuer'
|
4
|
+
|
5
|
+
module AsyncIO
|
6
|
+
class Worker
|
7
|
+
include AsyncIO::Rescuer
|
8
|
+
|
9
|
+
attr_reader :payload, :job
|
10
|
+
attr_reader :finished, :done
|
11
|
+
|
12
|
+
def initialize(payload, job)
|
13
|
+
@payload = payload
|
14
|
+
@job = job
|
15
|
+
@done = false
|
16
|
+
@finished = false
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# It sends payload a message +call+
|
21
|
+
# and passes the result of a job, by sending
|
22
|
+
# job a message +call+ as well, as its argument.
|
23
|
+
# This allows us to do:
|
24
|
+
#
|
25
|
+
# aget_user(1) { |u| print u.name }
|
26
|
+
#=> Paul Clark Manson
|
27
|
+
#
|
28
|
+
# Or any other sort of task that you may
|
29
|
+
# need its result to be available within a block without
|
30
|
+
# needing to wait for it to finish and non blocking IO.
|
31
|
+
#
|
32
|
+
# A payload is a Ruby object must pass, for example:
|
33
|
+
#
|
34
|
+
# payload = lambda { |u| print u.name }
|
35
|
+
# payload = Object.new
|
36
|
+
# def payload.call(result); warn(result); end
|
37
|
+
#
|
38
|
+
# job is pre-definied inside a method, it can
|
39
|
+
# be anything, for example:
|
40
|
+
# worker(payload) do
|
41
|
+
# User.find(uid)
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
def call
|
45
|
+
try do
|
46
|
+
@finished = true
|
47
|
+
@done = job.call
|
48
|
+
payload.call(done)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Tries to get the first job done, when an exception
|
54
|
+
# is raised it then calls payload again passing a
|
55
|
+
# fallback as its argument.
|
56
|
+
#
|
57
|
+
def try
|
58
|
+
begin
|
59
|
+
yield
|
60
|
+
rescue Exception => notice
|
61
|
+
rescuer { payload.call(fallback(notice)) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
##
|
68
|
+
# Instances of OpenStruct returns nil when the method
|
69
|
+
# called does not exist. This prevents another exception
|
70
|
+
# from being raised.
|
71
|
+
# It returns an instance of OpenStruct object with the
|
72
|
+
# exception rescued ( i.e notice ) and the worker that
|
73
|
+
# was assigned to this particular job.
|
74
|
+
#
|
75
|
+
def fallback(notice)
|
76
|
+
OpenStruct.new({ notice: notice, worker: self })
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
data/lib/async_io.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'async_io/load'
|
2
|
+
|
3
|
+
module AsyncIO
|
4
|
+
|
5
|
+
def self.async_creator=(new_async)
|
6
|
+
@@async_creator = new_async
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.async_creator
|
10
|
+
@@async_creator ||= Base.new(5)
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# Creates async jobs, if a payload (i.e ruby block)
|
15
|
+
# is not given it passes an empty payload to woker.
|
16
|
+
# That allows us to do:
|
17
|
+
#
|
18
|
+
# User.aget(1)
|
19
|
+
# User.aget(1) { |u| print u.id }
|
20
|
+
#
|
21
|
+
# The response will be a worker that was created for this
|
22
|
+
# particular job.
|
23
|
+
#
|
24
|
+
# NOTE: If you read PredictionIO::Worker you will see that
|
25
|
+
# it calls payload and passes job as its arguments. This is
|
26
|
+
# how it is available within a block later on.
|
27
|
+
# NOTE: You must pass a job ( i.e ruby block ).
|
28
|
+
#
|
29
|
+
def self.async(payload, &job)
|
30
|
+
payload = payload || ->(n) { n }
|
31
|
+
async_creator.worker(payload, &job)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'async_io/base'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module AsyncIO
|
6
|
+
Base.class_eval do
|
7
|
+
##
|
8
|
+
# Returns queue's size and pops one
|
9
|
+
# item from queue.
|
10
|
+
def extract_size!
|
11
|
+
queue.size.tap { queue.pop }
|
12
|
+
end
|
13
|
+
|
14
|
+
public(:consumer)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe AsyncIO::Base do
|
20
|
+
|
21
|
+
before { Thread.stub(:new).and_return(double) }
|
22
|
+
let(:alien) { AsyncIO::Base.new }
|
23
|
+
let(:logger) { AsyncIO::Logger }
|
24
|
+
|
25
|
+
context "initialising" do
|
26
|
+
it "should have a queue" do
|
27
|
+
alien.queue.should respond_to :push
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should create 1 thread as default" do
|
31
|
+
alien.threads.size.should eq(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
context "#worker" do
|
37
|
+
|
38
|
+
it "should have a worker" do
|
39
|
+
alien.should respond_to :worker
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should_not raise_error when no block is passed" do
|
43
|
+
expect {
|
44
|
+
alien.worker(:jason)
|
45
|
+
}.to_not raise_error
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should not raise_error when block is passed" do
|
49
|
+
expect {
|
50
|
+
alien.worker(:lizza) { }
|
51
|
+
}.to_not raise_error
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should push worker onto the queue" do
|
55
|
+
alien.worker(:paul) { }
|
56
|
+
alien.extract_size!.should eq(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should return worker" do
|
60
|
+
result = alien.worker(:blunt) { }
|
61
|
+
result.should be_instance_of AsyncIO::Worker
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "#async" do
|
66
|
+
it { alien.should respond_to :async }
|
67
|
+
|
68
|
+
##
|
69
|
+
# NOTE: this snippet here can be a little tricky
|
70
|
+
# First we create a lambda ( -> its just Ruby syntatic sugar )
|
71
|
+
# Second we create a double ( a mock )
|
72
|
+
# Third we stub out the state of our original
|
73
|
+
# method and return the double
|
74
|
+
#
|
75
|
+
# Then we call the method +async+ which takes a block to be
|
76
|
+
# 'passed' an object.
|
77
|
+
#
|
78
|
+
# Last but not least we check if our object has received
|
79
|
+
# that message with that particular argument.
|
80
|
+
#
|
81
|
+
# Ruby allows us to do some cool stuff with coercion and
|
82
|
+
# closures while using blocks.
|
83
|
+
# The following might be helpul:
|
84
|
+
# robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas
|
85
|
+
|
86
|
+
#
|
87
|
+
it "should pass this payload block onto Worker" do
|
88
|
+
payload = -> { :im_an_async_job }
|
89
|
+
|
90
|
+
worker = double
|
91
|
+
alien.stub(:worker).
|
92
|
+
and_return(worker)
|
93
|
+
|
94
|
+
##
|
95
|
+
# See the link below for a better understading how to
|
96
|
+
# pass/call blocks of code in Ruby.
|
97
|
+
# pragdave.pragprog.com/pragdave/2005/11/symbolto_proc.html
|
98
|
+
#
|
99
|
+
alien.async(&payload)
|
100
|
+
|
101
|
+
alien.should have_received(:worker).with(payload)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "#consumer" do
|
106
|
+
it { alien.should respond_to :consumer }
|
107
|
+
|
108
|
+
it "should pop and consume items in a queue" do
|
109
|
+
bob = double
|
110
|
+
|
111
|
+
##
|
112
|
+
# returns false to break the while loop.
|
113
|
+
#
|
114
|
+
bob.should_receive(:pop).and_return(false)
|
115
|
+
alien.stub(:queue).and_return(bob)
|
116
|
+
|
117
|
+
alien.consumer.should be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should not raise any error" do
|
121
|
+
tob = -> { raise "Not going to be Raised" }
|
122
|
+
alien.queue.push(tob)
|
123
|
+
|
124
|
+
expect {
|
125
|
+
alien.consumer
|
126
|
+
}.to_not raise_error
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should call a worker" do
|
130
|
+
duck = double
|
131
|
+
duck.should_receive(:call)
|
132
|
+
|
133
|
+
alien.stub(:queue).and_return([duck])
|
134
|
+
|
135
|
+
alien.consumer
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'async_io/rescuer.rb'
|
3
|
+
|
4
|
+
class Guard
|
5
|
+
include AsyncIO::Rescuer
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@logger = AsyncIO::Logger
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
describe AsyncIO::Rescuer do
|
14
|
+
let(:logger) { AsyncIO::Logger }
|
15
|
+
let(:guard) { Guard.new }
|
16
|
+
|
17
|
+
it { guard.should respond_to :rescuer }
|
18
|
+
|
19
|
+
context "#rescuer" do
|
20
|
+
|
21
|
+
it "should rescue when an exception is raised" do
|
22
|
+
expect {
|
23
|
+
guard.rescuer { raise "hell" }
|
24
|
+
}.to_not raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not raise when no block" do
|
28
|
+
expect {
|
29
|
+
guard.rescuer
|
30
|
+
}.to_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not raise when block is present" do
|
34
|
+
expect {
|
35
|
+
guard.rescuer { :imma_block }
|
36
|
+
}.to_not raise_error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "Logs", "whenever an exception is raised" do
|
41
|
+
|
42
|
+
it "should write error to logger" do
|
43
|
+
guard.rescuer { raise "log me!" }
|
44
|
+
logger.read.should match /log me!/
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'async_io/worker'
|
3
|
+
|
4
|
+
module AsyncIO
|
5
|
+
Worker.class_eval do
|
6
|
+
public(:fallback)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Worker do
|
10
|
+
|
11
|
+
let(:payload) { ->(r) { r } }
|
12
|
+
let(:job) { -> { :am_a_dog } }
|
13
|
+
let(:worker) { Worker.new(payload, job) }
|
14
|
+
|
15
|
+
context "initialising" do
|
16
|
+
|
17
|
+
it "must take a payload" do
|
18
|
+
worker.payload.should be_kind_of Proc
|
19
|
+
end
|
20
|
+
|
21
|
+
it "must take a job" do
|
22
|
+
worker.job.should be_kind_of Proc
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
it { worker.should respond_to :done }
|
28
|
+
|
29
|
+
it "should not be done as job is not finished" do
|
30
|
+
worker.done.should be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must call payload and pass job as its argument" do
|
34
|
+
worker.call.should eq(:am_a_dog)
|
35
|
+
end
|
36
|
+
|
37
|
+
context "Getting a job done" do
|
38
|
+
it "should set done to its last finished job" do
|
39
|
+
worker.call
|
40
|
+
worker.done.should eq(:am_a_dog)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#try" do
|
45
|
+
it { worker.should respond_to :try }
|
46
|
+
|
47
|
+
context "failed" do
|
48
|
+
it "should call fallback" do
|
49
|
+
worker.should_receive(:fallback)
|
50
|
+
worker.try { raise "failed" }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "success" do
|
55
|
+
it "should not call fallback" do
|
56
|
+
worker.should_not_receive(:fallback)
|
57
|
+
worker.try { :happy }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "#fallback" do
|
62
|
+
let(:fallback) { worker.fallback("notice") }
|
63
|
+
|
64
|
+
it "should have a notice" do
|
65
|
+
fallback.notice.should eq("notice")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should have a worker" do
|
69
|
+
fallback.worker.should eq worker
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should return nil when method not found" do
|
73
|
+
fallback.will_return_nil.should be_nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module AsyncIO
|
2
|
+
CONFIG_PATH = File.expand_path("../support", __FILE__)
|
3
|
+
|
4
|
+
Logger = Object.new
|
5
|
+
def Logger.error(n); @n = n; end
|
6
|
+
def Logger.read; @n; end
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec.configure do |c|
|
10
|
+
c.treat_symbols_as_metadata_keys_with_true_values = true
|
11
|
+
end
|
12
|
+
|
13
|
+
class AsyncIO::FakeAsync
|
14
|
+
##
|
15
|
+
# Calls payload and passes whatever
|
16
|
+
# job.call yields as its argument.
|
17
|
+
#
|
18
|
+
def self.async(payload, &job)
|
19
|
+
payload = payload || ->(n) { n }
|
20
|
+
payload.call(job.call)
|
21
|
+
end
|
22
|
+
|
23
|
+
def worker(payload, &job)
|
24
|
+
payload.call(job.call)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec::Matchers.define(:be_rescued){ |e| match { |a| a === :rescued } }
|
30
|
+
RSpec::Matchers.define(:be_deleted){ |e| match { |a| a === :deleted } }
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: async_io
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Antonio C Nalesso
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Perform asyncrhonous IO for ruby using blocks and threads just pure old
|
28
|
+
ruby code.
|
29
|
+
email:
|
30
|
+
- acnalesso@yahoo.co.uk
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/async_io.rb
|
36
|
+
- lib/async_io/base.rb
|
37
|
+
- lib/async_io/load.rb
|
38
|
+
- lib/async_io/rescuer.rb
|
39
|
+
- lib/async_io/version.rb
|
40
|
+
- lib/async_io/worker.rb
|
41
|
+
- spec/async_io/base_spec.rb
|
42
|
+
- spec/async_io/rescuer_spec.rb
|
43
|
+
- spec/async_io/worker_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
homepage: https://github.com/acnalesso/async_io
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
metadata: {}
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 2.0.3
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: Simple asynchronous IO for ruby.
|
69
|
+
test_files:
|
70
|
+
- spec/async_io/base_spec.rb
|
71
|
+
- spec/async_io/rescuer_spec.rb
|
72
|
+
- spec/async_io/worker_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
has_rdoc:
|