asynchro 0.1.5 → 0.4.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/README.rdoc +42 -14
- data/VERSION +1 -1
- data/asynchro.gemspec +3 -3
- data/lib/asynchro.rb +2 -0
- data/lib/asynchro/extensions.rb +10 -0
- data/lib/asynchro/test_helper.rb +2 -2
- data/lib/asynchro/tracker.rb +61 -37
- data/test/helper.rb +10 -0
- data/test/test_asynchro_extensions.rb +16 -0
- data/test/test_asynchro_tracker.rb +45 -12
- metadata +24 -9
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= asynchro
|
2
2
|
|
3
|
-
This is a set of extensions for
|
4
|
-
|
3
|
+
This is a set of extensions for event-driven, asynchronous Ruby applications
|
4
|
+
and improved support for testing within the EventMachine engine.
|
5
5
|
|
6
6
|
The two primary components are:
|
7
7
|
|
@@ -13,15 +13,15 @@ The two primary components are:
|
|
13
13
|
|
14
14
|
== Installation
|
15
15
|
|
16
|
-
|
16
|
+
Using the gem installation tool is the easiest way to get started:
|
17
17
|
|
18
18
|
gem install asynchro
|
19
19
|
|
20
|
-
|
20
|
+
If you're using it within a bundler managed project, add to your Gemfile:
|
21
21
|
|
22
22
|
gem 'asynchro'
|
23
23
|
|
24
|
-
== State
|
24
|
+
== Using Asynchro::State
|
25
25
|
|
26
26
|
To define a state mapping, include the Asynchro::Extensions methods and
|
27
27
|
then use the `async_state` method:
|
@@ -75,21 +75,35 @@ If this warning is undesirable, declare the state with an empty block.
|
|
75
75
|
Be careful to avoid entering states for which there is no exit condition
|
76
76
|
or the execution will never successfully complete.
|
77
77
|
|
78
|
-
== Tracker
|
78
|
+
== Using Asynchro::Tracker
|
79
79
|
|
80
80
|
The tracker component is used to process multiple blocks independently, yet
|
81
|
-
confirm that they have all completed before moving on.
|
81
|
+
confirm that they have all completed before moving on. This control structure
|
82
|
+
helps to avoid duplicating logic in callback methods.
|
82
83
|
|
84
|
+
A simple example is:
|
85
|
+
|
86
|
+
require 'rubygems'
|
87
|
+
gem 'asynchro'
|
88
|
+
require 'asynchro'
|
89
|
+
|
83
90
|
include Asynchro::Extensions
|
91
|
+
|
92
|
+
def example_async_call
|
93
|
+
yield
|
94
|
+
end
|
95
|
+
|
84
96
|
success = false
|
85
97
|
|
86
98
|
async_tracker do |tracker|
|
87
99
|
tracker.perform do |done|
|
88
|
-
|
100
|
+
example_async_call do
|
101
|
+
done.call
|
102
|
+
end
|
89
103
|
end
|
90
104
|
|
91
105
|
tracker.perform(4) do |done|
|
92
|
-
|
106
|
+
example_async_call do
|
93
107
|
done.call
|
94
108
|
end
|
95
109
|
end
|
@@ -98,6 +112,13 @@ confirm that they have all completed before moving on. An example is:
|
|
98
112
|
success = true
|
99
113
|
end
|
100
114
|
end
|
115
|
+
|
116
|
+
The main `async_tracker` call will appear to block until all of the actions
|
117
|
+
defined by `perform` are completed. More specifically, the `done` trigger
|
118
|
+
must be called in order to carry forward. Generally this trigger is passed
|
119
|
+
through to the final block that must be executed before the operation is
|
120
|
+
completed. In this example the trigger is scoped such that the inner block
|
121
|
+
has access to it.
|
101
122
|
|
102
123
|
The `perform` method is used to declare something that must be performed,
|
103
124
|
and the `finish` method will be executed once all of the declared blocks
|
@@ -107,13 +128,20 @@ The `perform` method takes a numerical argument that indicates the number of
|
|
107
128
|
times the callback must be called in order to be completed. The default is 1.
|
108
129
|
Negative or zero values will result in undefined behavior.
|
109
130
|
|
131
|
+
It is possible to declare `perform` blocks at any time prior to the completion
|
132
|
+
of the last block. This enables additional processing to be performed only
|
133
|
+
if certain requirements are met, or for actions to be chained together as
|
134
|
+
required.
|
135
|
+
|
110
136
|
Just as multiple `perform` blocks can be declared, multiple `finish` blocks
|
111
|
-
can be supplied. These will execute in order, once, upon
|
137
|
+
can be supplied. These will execute in the order they are defined, once, upon
|
138
|
+
completion of all the prerequisite blocks.
|
112
139
|
|
113
140
|
As there is no timeout, the tracker will wait for an indefinite period of
|
114
141
|
time if something precludes one or more of the callbacks from being executed.
|
115
|
-
This tracker is only suitable for asynchronous code that
|
116
|
-
a callback of some sort within an acceptable period of
|
142
|
+
This tracker is only suitable for asynchronous code that is designed such that
|
143
|
+
it will always trigger a callback of some sort within an acceptable period of
|
144
|
+
time.
|
117
145
|
|
118
146
|
== Other Notes
|
119
147
|
|
@@ -121,5 +149,5 @@ Additional demonstrations of these are included in the test/ directory.
|
|
121
149
|
|
122
150
|
== Copyright
|
123
151
|
|
124
|
-
Copyright (c) 2011 Scott Tadman, The Working Group Inc.
|
125
|
-
further details.
|
152
|
+
Copyright (c) 2011-2012 Scott Tadman, The Working Group Inc.
|
153
|
+
See LICENSE.txt for further details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/asynchro.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "asynchro"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Scott Tadman"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2012-07-23"
|
13
13
|
s.description = "Provides a number of tools to help make developing and testing asynchronous applications more manageable."
|
14
14
|
s.email = "github@tadman.ca"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -39,7 +39,7 @@ Gem::Specification.new do |s|
|
|
39
39
|
s.homepage = "http://github.com/tadman/asynchro"
|
40
40
|
s.licenses = ["MIT"]
|
41
41
|
s.require_paths = ["lib"]
|
42
|
-
s.rubygems_version = "1.8.
|
42
|
+
s.rubygems_version = "1.8.24"
|
43
43
|
s.summary = "Ruby EventMachine Async Programming Toolkit"
|
44
44
|
|
45
45
|
if s.respond_to? :specification_version then
|
data/lib/asynchro.rb
CHANGED
data/lib/asynchro/extensions.rb
CHANGED
data/lib/asynchro/test_helper.rb
CHANGED
@@ -46,7 +46,7 @@ module Asynchro::TestHelper
|
|
46
46
|
def assert_callback(time = nil, message = nil)
|
47
47
|
called_back = false
|
48
48
|
|
49
|
-
|
49
|
+
EventMachine.next_tick do
|
50
50
|
yield(lambda { called_back = true })
|
51
51
|
end
|
52
52
|
|
@@ -68,7 +68,7 @@ module Asynchro::TestHelper
|
|
68
68
|
def assert_callback_times(count = 1, time = nil, message = nil)
|
69
69
|
called_back = 0
|
70
70
|
|
71
|
-
|
71
|
+
EventMachine.next_tick do
|
72
72
|
yield(lambda { called_back += 1 })
|
73
73
|
end
|
74
74
|
|
data/lib/asynchro/tracker.rb
CHANGED
@@ -6,58 +6,82 @@ class Asynchro::Tracker
|
|
6
6
|
|
7
7
|
if (block_given?)
|
8
8
|
yield(self)
|
9
|
-
|
10
|
-
|
9
|
+
|
10
|
+
Fiber.new do
|
11
|
+
self.run!
|
12
|
+
end.resume
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
# Performs an action. The supplied block will be called with a callback
|
17
|
+
# tracking Proc that should be triggered with `call` as many times as
|
18
|
+
# are specified in the `count` argument. When the correct number of calls
|
19
|
+
# have been made, this action is considered finished.
|
20
|
+
def perform(count = 1, *args)
|
21
|
+
@operations ||= { }
|
22
|
+
|
23
|
+
_sequence = @sequence += 1
|
24
|
+
@operations[_sequence] = true
|
25
|
+
|
26
|
+
fiber = Fiber.new do
|
27
|
+
called = false
|
28
|
+
should_resume = false
|
29
|
+
|
30
|
+
callback = lambda {
|
31
|
+
called = true
|
32
|
+
|
33
|
+
if (should_resume)
|
34
|
+
fiber.resume
|
35
|
+
end
|
36
|
+
}
|
37
|
+
|
38
|
+
count.times do
|
39
|
+
called = false
|
40
|
+
|
41
|
+
yield(callback, *args)
|
42
|
+
|
43
|
+
unless (called)
|
44
|
+
should_resume = true
|
45
|
+
Fiber.yield
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
@operations.delete(_sequence)
|
50
|
+
|
51
|
+
if (self.finished?)
|
52
|
+
self.finish!
|
53
|
+
end
|
22
54
|
end
|
55
|
+
|
56
|
+
(@fibers ||= [ ]) << fiber
|
23
57
|
end
|
24
|
-
|
58
|
+
|
25
59
|
# Executes this block when all the actions to be performed have checked in
|
26
60
|
# as finsished.
|
27
61
|
def finish(&block)
|
28
|
-
@finish ||= [ ]
|
29
|
-
@finish << block
|
62
|
+
(@finish ||= [ ]) << block
|
30
63
|
end
|
31
64
|
|
32
65
|
# Returns true if this tracker has completed all supplied blocks, or false
|
33
66
|
# otherwise.
|
34
67
|
def finished?
|
35
|
-
!@
|
68
|
+
!@operations or @operations.empty?
|
36
69
|
end
|
37
70
|
|
38
|
-
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
71
|
+
protected
|
72
|
+
# Runs through the tasks to perform for this tracker. Should only be
|
73
|
+
# called if this object is initialized without a supplied block as in that
|
74
|
+
# case, this would have been called already.
|
75
|
+
def run!
|
76
|
+
if (self.finished?)
|
77
|
+
self.finish!
|
78
|
+
else
|
79
|
+
@fibers.each(&:resume)
|
80
|
+
end
|
81
|
+
end
|
47
82
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
@blocks.delete(_sequence)
|
52
|
-
end
|
53
|
-
|
54
|
-
if (self.finished?)
|
55
|
-
@finish and @finish.each(&:call)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
}
|
59
|
-
|
60
|
-
@procs ||= [ ]
|
61
|
-
@procs << lambda { block.call(callback) }
|
83
|
+
# Executes the defined finish blocks and resumes execution
|
84
|
+
def finish!
|
85
|
+
@finish and @finish.each(&:call)
|
62
86
|
end
|
63
87
|
end
|
data/test/helper.rb
CHANGED
@@ -43,4 +43,20 @@ class TestAsynchroExtensions < Test::Unit::TestCase
|
|
43
43
|
|
44
44
|
assert_equal Asynchro::State, state.class
|
45
45
|
end
|
46
|
+
|
47
|
+
def test_yield_to
|
48
|
+
ran = false
|
49
|
+
subfiber = nil
|
50
|
+
|
51
|
+
yield_to do |f|
|
52
|
+
ran = true
|
53
|
+
sleep(10)
|
54
|
+
end
|
55
|
+
|
56
|
+
assert_equal ran, true
|
57
|
+
|
58
|
+
assert_exception LocalJumpError do
|
59
|
+
yield_to
|
60
|
+
end
|
61
|
+
end
|
46
62
|
end
|
@@ -16,32 +16,65 @@ class TestAsynchroTracker < Test::Unit::TestCase
|
|
16
16
|
|
17
17
|
def test_simple_tracker
|
18
18
|
count = 0
|
19
|
+
success = 0
|
19
20
|
|
20
21
|
Asynchro::Tracker.new do |tracker|
|
21
|
-
tracker.perform do |
|
22
|
-
tracker.perform do |
|
23
|
-
count +=
|
24
|
-
|
22
|
+
tracker.perform(3) do |done1|
|
23
|
+
tracker.perform(2) do |done2|
|
24
|
+
count += 10
|
25
|
+
done2.call
|
25
26
|
end
|
26
27
|
|
27
28
|
count += 1
|
28
|
-
|
29
|
+
done1.call
|
30
|
+
end
|
31
|
+
|
32
|
+
trigger = nil
|
33
|
+
|
34
|
+
tracker.perform(8) do |done|
|
35
|
+
count += 100
|
36
|
+
trigger = done
|
29
37
|
end
|
30
38
|
|
31
39
|
tracker.perform(4) do |done|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
count += 1000
|
41
|
+
trigger.call
|
42
|
+
trigger.call
|
43
|
+
done.call
|
36
44
|
end
|
37
45
|
|
38
46
|
tracker.finish do
|
39
|
-
success
|
47
|
+
success += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
tracker.finish do
|
51
|
+
success += 100
|
40
52
|
end
|
41
53
|
end
|
42
54
|
|
43
|
-
|
44
|
-
|
55
|
+
assert_equal 4863, count
|
56
|
+
assert_equal 101, success
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_repetition
|
60
|
+
test_cycle = 1000
|
61
|
+
count = 0
|
62
|
+
|
63
|
+
Asynchro::Tracker.new do |tracker1|
|
64
|
+
tracker1.perform(test_cycle) do |done1|
|
65
|
+
Asynchro::Tracker.new do |tracker2|
|
66
|
+
tracker2.perform(test_cycle) do |done2|
|
67
|
+
count += 1
|
68
|
+
done2.call
|
69
|
+
end
|
70
|
+
|
71
|
+
tracker2.finish do
|
72
|
+
done1.call
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
45
76
|
end
|
77
|
+
|
78
|
+
assert_equal test_cycle * test_cycle, count
|
46
79
|
end
|
47
80
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: asynchro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-07-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: bundler
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: '0'
|
33
38
|
type: :development
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: jeweler
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,7 +53,12 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
description: Provides a number of tools to help make developing and testing asynchronous
|
48
63
|
applications more manageable.
|
49
64
|
email: github@tadman.ca
|
@@ -92,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
107
|
version: '0'
|
93
108
|
requirements: []
|
94
109
|
rubyforge_project:
|
95
|
-
rubygems_version: 1.8.
|
110
|
+
rubygems_version: 1.8.24
|
96
111
|
signing_key:
|
97
112
|
specification_version: 3
|
98
113
|
summary: Ruby EventMachine Async Programming Toolkit
|