asynchro 0.1.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  = asynchro
2
2
 
3
- This is a set of extensions for Test::Unit::TestCase and any plain Ruby
4
- classes that will be operating inside the EventMachine framework.
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
- The gem should be a good place to start:
16
+ Using the gem installation tool is the easiest way to get started:
17
17
 
18
18
  gem install asynchro
19
19
 
20
- The best approach is to put this in your Gemfile:
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. An example is:
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
- done.call
100
+ example_async_call do
101
+ done.call
102
+ end
89
103
  end
90
104
 
91
105
  tracker.perform(4) do |done|
92
- 4.times do
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 completion.
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 will always trigger
116
- a callback of some sort within an acceptable period of time.
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. See LICENSE.txt for
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.5
1
+ 0.4.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "asynchro"
8
- s.version = "0.1.5"
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 = "2011-09-27"
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.10"
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
@@ -1,3 +1,5 @@
1
+ require 'fiber'
2
+
1
3
  module Asynchro
2
4
  autoload(:Extensions, 'asynchro/extensions')
3
5
  autoload(:State, 'asynchro/state')
@@ -6,4 +6,14 @@ module Asynchro::Extensions
6
6
  def async_state(&block)
7
7
  Asynchro::State.new(&block)
8
8
  end
9
+
10
+ def yield_to
11
+ this = Fiber.current
12
+
13
+ fiber = Fiber.new do
14
+ yield(lambda { fiber.transfer(this) })
15
+ end
16
+
17
+ fiber.transfer(fiber)
18
+ end
9
19
  end
@@ -46,7 +46,7 @@ module Asynchro::TestHelper
46
46
  def assert_callback(time = nil, message = nil)
47
47
  called_back = false
48
48
 
49
- Pigeonrocket.execute_in_main_thread do
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
- Pigeonrocket.execute_in_main_thread do
71
+ EventMachine.next_tick do
72
72
  yield(lambda { called_back += 1 })
73
73
  end
74
74
 
@@ -6,58 +6,82 @@ class Asynchro::Tracker
6
6
 
7
7
  if (block_given?)
8
8
  yield(self)
9
-
10
- self.run!
9
+
10
+ Fiber.new do
11
+ self.run!
12
+ end.resume
11
13
  end
12
14
  end
13
15
 
14
- # Runs through the tasks to perform for this tracker. Should only be
15
- # called if this object is initialized without a supplied block as in that
16
- # case, this would have been called already.
17
- def run!
18
- if (@procs)
19
- @procs.each(&:call)
20
- else
21
- @finish and @finish.each(&:call)
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
- !@blocks or @blocks.empty?
68
+ !@operations or @operations.empty?
36
69
  end
37
70
 
38
- # Performs an action. The supplied block will be called with a callback
39
- # tracking Proc that should be triggered with `call` as many times as
40
- # are specified in the `count` argument. When the correct number of calls
41
- # have been made, this action is considered finished.
42
- def perform(count = 1, &block)
43
- @blocks ||= { }
44
-
45
- _sequence = @sequence += 1
46
- @blocks[_sequence] = count
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
- callback = lambda {
49
- if (@blocks[_sequence])
50
- if ((@blocks[_sequence] -=1) <= 0)
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
@@ -18,4 +18,14 @@ require 'asynchro'
18
18
 
19
19
  class Test::Unit::TestCase
20
20
  include Asynchro::TestHelper
21
+
22
+ def assert_exception(type)
23
+ begin
24
+ yield
25
+ rescue => e
26
+ assert_equal type, e.class
27
+ else
28
+ assert_equal type, nil
29
+ end
30
+ end
21
31
  end
@@ -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 |done|
22
- tracker.perform do |done|
23
- count += 2
24
- done.call
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
- done.call
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
- 4.times do
33
- count += 1
34
- done.call
35
- end
40
+ count += 1000
41
+ trigger.call
42
+ trigger.call
43
+ done.call
36
44
  end
37
45
 
38
46
  tracker.finish do
39
- success = true
47
+ success += 1
48
+ end
49
+
50
+ tracker.finish do
51
+ success += 100
40
52
  end
41
53
  end
42
54
 
43
- assert_eventually(5) do
44
- count == 7
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.1.5
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: 2011-09-27 00:00:00.000000000Z
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: &2154668920 !ruby/object:Gem::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: *2154668920
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: &2154668040 !ruby/object:Gem::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: *2154668040
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: &2154657980 !ruby/object:Gem::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: *2154657980
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.10
110
+ rubygems_version: 1.8.24
96
111
  signing_key:
97
112
  specification_version: 3
98
113
  summary: Ruby EventMachine Async Programming Toolkit