eventmachine 0.12.10-x86-mswin32-60 → 1.0.0.beta.2-x86-mswin32-60

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.
Files changed (150) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +1 -0
  3. data/README +80 -81
  4. data/Rakefile +7 -370
  5. data/docs/COPYING +60 -60
  6. data/docs/ChangeLog +211 -211
  7. data/docs/DEFERRABLES +246 -133
  8. data/docs/EPOLL +141 -141
  9. data/docs/GNU +281 -281
  10. data/docs/INSTALL +13 -13
  11. data/docs/KEYBOARD +42 -38
  12. data/docs/LEGAL +25 -25
  13. data/docs/LIGHTWEIGHT_CONCURRENCY +130 -70
  14. data/docs/PURE_RUBY +75 -75
  15. data/docs/RELEASE_NOTES +94 -94
  16. data/docs/SMTP +4 -2
  17. data/docs/SPAWNED_PROCESSES +148 -89
  18. data/docs/TODO +8 -8
  19. data/eventmachine.gemspec +19 -26
  20. data/examples/ex_channel.rb +42 -42
  21. data/examples/ex_queue.rb +2 -2
  22. data/examples/ex_tick_loop_array.rb +15 -0
  23. data/examples/ex_tick_loop_counter.rb +32 -0
  24. data/examples/helper.rb +1 -1
  25. data/ext/binder.cpp +0 -1
  26. data/ext/cmain.cpp +36 -25
  27. data/ext/ed.cpp +104 -113
  28. data/ext/ed.h +24 -30
  29. data/ext/em.cpp +349 -283
  30. data/ext/em.h +25 -29
  31. data/ext/eventmachine.h +5 -4
  32. data/ext/extconf.rb +58 -49
  33. data/ext/fastfilereader/extconf.rb +5 -3
  34. data/ext/fastfilereader/mapper.cpp +214 -214
  35. data/ext/fastfilereader/mapper.h +59 -59
  36. data/ext/fastfilereader/rubymain.cpp +127 -127
  37. data/ext/kb.cpp +1 -3
  38. data/ext/page.cpp +107 -107
  39. data/ext/page.h +51 -51
  40. data/ext/pipe.cpp +9 -11
  41. data/ext/project.h +12 -8
  42. data/ext/rubymain.cpp +138 -104
  43. data/java/.classpath +8 -8
  44. data/java/.project +17 -17
  45. data/java/src/com/rubyeventmachine/EmReactor.java +1 -0
  46. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -40
  47. data/lib/em/buftok.rb +138 -138
  48. data/lib/em/callback.rb +25 -25
  49. data/lib/em/channel.rb +1 -1
  50. data/lib/em/connection.rb +6 -1
  51. data/lib/em/deferrable.rb +16 -2
  52. data/lib/em/file_watch.rb +53 -53
  53. data/lib/em/future.rb +61 -61
  54. data/lib/em/iterator.rb +270 -0
  55. data/lib/em/messages.rb +66 -66
  56. data/lib/em/process_watch.rb +43 -43
  57. data/lib/em/protocols.rb +1 -1
  58. data/lib/em/protocols/header_and_content.rb +138 -138
  59. data/lib/em/protocols/httpclient.rb +267 -262
  60. data/lib/em/protocols/line_protocol.rb +28 -0
  61. data/lib/em/protocols/memcache.rb +322 -322
  62. data/lib/em/protocols/postgres3.rb +247 -247
  63. data/lib/em/protocols/saslauth.rb +175 -175
  64. data/lib/em/protocols/smtpserver.rb +640 -547
  65. data/lib/em/protocols/stomp.rb +200 -200
  66. data/lib/em/protocols/tcptest.rb +52 -52
  67. data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +1013 -1022
  68. data/lib/em/queue.rb +1 -0
  69. data/lib/em/spawnable.rb +85 -85
  70. data/lib/em/streamer.rb +130 -130
  71. data/lib/em/tick_loop.rb +85 -0
  72. data/lib/em/timers.rb +2 -1
  73. data/lib/em/version.rb +1 -1
  74. data/lib/eventmachine.rb +40 -84
  75. data/lib/jeventmachine.rb +2 -1
  76. data/lib/rubyeventmachine.rb +2 -0
  77. data/setup.rb +1585 -1585
  78. data/tasks/doc.rake +30 -0
  79. data/tasks/package.rake +85 -0
  80. data/tasks/test.rake +6 -0
  81. data/tests/client.crt +31 -31
  82. data/tests/client.key +51 -51
  83. data/tests/test_attach.rb +13 -3
  84. data/tests/test_basic.rb +60 -95
  85. data/tests/test_channel.rb +3 -2
  86. data/tests/test_defer.rb +49 -47
  87. data/tests/test_deferrable.rb +35 -0
  88. data/tests/test_error_handler.rb +35 -35
  89. data/tests/test_errors.rb +82 -82
  90. data/tests/test_exc.rb +55 -55
  91. data/tests/test_file_watch.rb +49 -49
  92. data/tests/test_futures.rb +198 -198
  93. data/tests/test_handler_check.rb +36 -36
  94. data/tests/test_hc.rb +190 -218
  95. data/tests/test_httpclient.rb +227 -218
  96. data/tests/test_httpclient2.rb +3 -2
  97. data/tests/test_inactivity_timeout.rb +3 -3
  98. data/tests/test_kb.rb +60 -60
  99. data/tests/test_ltp.rb +13 -5
  100. data/tests/test_ltp2.rb +317 -317
  101. data/tests/test_next_tick.rb +1 -1
  102. data/tests/test_object_protocol.rb +36 -36
  103. data/tests/test_pending_connect_timeout.rb +2 -2
  104. data/tests/test_process_watch.rb +50 -48
  105. data/tests/test_proxy_connection.rb +52 -0
  106. data/tests/test_pure.rb +134 -125
  107. data/tests/test_queue.rb +44 -44
  108. data/tests/test_running.rb +42 -42
  109. data/tests/test_sasl.rb +72 -72
  110. data/tests/test_send_file.rb +251 -242
  111. data/tests/test_servers.rb +76 -76
  112. data/tests/test_smtpclient.rb +83 -83
  113. data/tests/test_smtpserver.rb +85 -85
  114. data/tests/test_spawn.rb +322 -322
  115. data/tests/test_ssl_methods.rb +49 -49
  116. data/tests/test_ssl_verify.rb +82 -82
  117. data/tests/test_tick_loop.rb +59 -0
  118. data/tests/test_timers.rb +13 -15
  119. data/tests/test_ud.rb +36 -36
  120. data/tests/testem.rb +31 -31
  121. metadata +66 -51
  122. data/ext/cplusplus.cpp +0 -202
  123. data/ext/emwin.cpp +0 -300
  124. data/ext/emwin.h +0 -94
  125. data/ext/epoll.cpp +0 -26
  126. data/ext/epoll.h +0 -25
  127. data/ext/eventmachine_cpp.h +0 -96
  128. data/ext/files.cpp +0 -94
  129. data/ext/files.h +0 -65
  130. data/ext/sigs.cpp +0 -89
  131. data/ext/sigs.h +0 -32
  132. data/java/src/com/rubyeventmachine/application/Application.java +0 -194
  133. data/java/src/com/rubyeventmachine/application/Connection.java +0 -74
  134. data/java/src/com/rubyeventmachine/application/ConnectionFactory.java +0 -37
  135. data/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java +0 -46
  136. data/java/src/com/rubyeventmachine/application/PeriodicTimer.java +0 -38
  137. data/java/src/com/rubyeventmachine/application/Timer.java +0 -54
  138. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +0 -109
  139. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +0 -148
  140. data/java/src/com/rubyeventmachine/tests/EMTest.java +0 -80
  141. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +0 -53
  142. data/java/src/com/rubyeventmachine/tests/TestServers.java +0 -75
  143. data/java/src/com/rubyeventmachine/tests/TestTimers.java +0 -90
  144. data/lib/evma.rb +0 -32
  145. data/lib/evma/callback.rb +0 -32
  146. data/lib/evma/container.rb +0 -75
  147. data/lib/evma/factory.rb +0 -77
  148. data/lib/evma/protocol.rb +0 -87
  149. data/lib/evma/reactor.rb +0 -48
  150. data/web/whatis +0 -7
@@ -35,7 +35,7 @@ module EventMachine
35
35
  # Add items to the channel, which are pushed out to all subscribers.
36
36
  def push(*items)
37
37
  items = items.dup
38
- EM.schedule { @subs.values.each { |s| items.each { |i| s.call i } } }
38
+ EM.schedule { items.each { |i| @subs.values.each { |s| s.call i } } }
39
39
  end
40
40
  alias << push
41
41
 
@@ -36,7 +36,7 @@ module EventMachine
36
36
  allocate.instance_eval do
37
37
  # Store signature
38
38
  @signature = sig
39
- associate_callback_target sig
39
+ # associate_callback_target sig
40
40
 
41
41
  # Call a superclass's #initialize if it has one
42
42
  initialize(*args)
@@ -134,6 +134,11 @@ module EventMachine
134
134
  def proxy_target_unbound
135
135
  end
136
136
 
137
+ # EventMachine::Connection#proxy_completed is called when the reactor finished proxying all
138
+ # of the requested bytes.
139
+ def proxy_completed
140
+ end
141
+
137
142
  # EventMachine::Connection#proxy_incoming_to is called only by user code. It sets up
138
143
  # a low-level proxy relay for all data inbound for this connection, to the connection given
139
144
  # as the argument. This is essentially just a helper method for enable_proxy.
@@ -51,6 +51,13 @@ module EventMachine
51
51
  end
52
52
  end
53
53
 
54
+ # Cancels an outstanding callback to &block if any. Undoes the action of #callback.
55
+ #
56
+ def cancel_callback block
57
+ @callbacks ||= []
58
+ @callbacks.delete block
59
+ end
60
+
54
61
  # Specify a block to be executed if and when the Deferrable object receives
55
62
  # a status of :failed. See #set_deferred_status for more information.
56
63
  #--
@@ -69,6 +76,13 @@ module EventMachine
69
76
  end
70
77
  end
71
78
 
79
+ # Cancels an outstanding errback to &block if any. Undoes the action of #errback.
80
+ #
81
+ def cancel_errback block
82
+ @errbacks ||= []
83
+ @errbacks.delete block
84
+ end
85
+
72
86
  # Sets the "disposition" (status) of the Deferrable object. See also the large set of
73
87
  # sugarings for this method.
74
88
  # Note that if you call this method without arguments,
@@ -150,10 +164,10 @@ module EventMachine
150
164
  # the Timeout expires (passing no arguments to the object's errbacks).
151
165
  # Setting the status at any time prior to a call to the expiration of the timeout
152
166
  # will cause the timer to be cancelled.
153
- def timeout seconds
167
+ def timeout seconds, *args
154
168
  cancel_timeout
155
169
  me = self
156
- @deferred_timeout = EventMachine::Timer.new(seconds) {me.fail}
170
+ @deferred_timeout = EventMachine::Timer.new(seconds) {me.fail(*args)}
157
171
  end
158
172
 
159
173
  # Cancels an outstanding timeout if any. Undoes the action of #timeout.
@@ -1,54 +1,54 @@
1
- module EventMachine
2
-
3
- # This is subclassed from EventMachine::Connection for use with the file monitoring API. Read the
4
- # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_file.
5
- class FileWatch < Connection
6
- # :stopdoc:
7
- Cmodified = 'modified'.freeze
8
- Cdeleted = 'deleted'.freeze
9
- Cmoved = 'moved'.freeze
10
- # :startdoc:
11
-
12
- def receive_data(data) #:nodoc:
13
- case data
14
- when Cmodified
15
- file_modified
16
- when Cdeleted
17
- file_deleted
18
- when Cmoved
19
- file_moved
20
- end
21
- end
22
-
23
- # Returns the path that EventMachine::watch_file was originally called with. The current implementation
24
- # does not pick up on the new filename after a rename occurs.
25
- def path
26
- @path
27
- end
28
-
29
- # Should be redefined with the user's custom callback that will be fired when the file is modified.
30
- def file_modified
31
- end
32
-
33
- # Should be redefined with the user's custom callback that will be fired when the file is deleted.
34
- # When the file is deleted, stop_watching will be called after this to make sure everything is
35
- # cleaned up correctly.
36
- #
37
- # Note that on linux (with inotify), file_deleted will not be called until all open file descriptors to
38
- # the file have been closed.
39
- def file_deleted
40
- end
41
-
42
- # Should be redefined with the user's custom callback that will be fired when the file is moved or renamed.
43
- def file_moved
44
- end
45
-
46
- # Discontinue monitoring of the file.
47
- # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing unbind.
48
- # This will be called automatically when a file is deleted. User code may call it as well.
49
- def stop_watching
50
- EventMachine::unwatch_filename(@signature)
51
- end
52
- end
53
-
1
+ module EventMachine
2
+
3
+ # This is subclassed from EventMachine::Connection for use with the file monitoring API. Read the
4
+ # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_file.
5
+ class FileWatch < Connection
6
+ # :stopdoc:
7
+ Cmodified = 'modified'.freeze
8
+ Cdeleted = 'deleted'.freeze
9
+ Cmoved = 'moved'.freeze
10
+ # :startdoc:
11
+
12
+ def receive_data(data) #:nodoc:
13
+ case data
14
+ when Cmodified
15
+ file_modified
16
+ when Cdeleted
17
+ file_deleted
18
+ when Cmoved
19
+ file_moved
20
+ end
21
+ end
22
+
23
+ # Returns the path that EventMachine::watch_file was originally called with. The current implementation
24
+ # does not pick up on the new filename after a rename occurs.
25
+ def path
26
+ @path
27
+ end
28
+
29
+ # Should be redefined with the user's custom callback that will be fired when the file is modified.
30
+ def file_modified
31
+ end
32
+
33
+ # Should be redefined with the user's custom callback that will be fired when the file is deleted.
34
+ # When the file is deleted, stop_watching will be called after this to make sure everything is
35
+ # cleaned up correctly.
36
+ #
37
+ # Note that on linux (with inotify), file_deleted will not be called until all open file descriptors to
38
+ # the file have been closed.
39
+ def file_deleted
40
+ end
41
+
42
+ # Should be redefined with the user's custom callback that will be fired when the file is moved or renamed.
43
+ def file_moved
44
+ end
45
+
46
+ # Discontinue monitoring of the file.
47
+ # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing unbind.
48
+ # This will be called automatically when a file is deleted. User code may call it as well.
49
+ def stop_watching
50
+ EventMachine::unwatch_filename(@signature)
51
+ end
52
+ end
53
+
54
54
  end
@@ -1,61 +1,61 @@
1
- #--
2
- #
3
- # Author:: Francis Cianfrocca (gmail: blackhedd)
4
- # Homepage:: http://rubyeventmachine.com
5
- # Date:: 16 Jul 2006
6
- #
7
- # See EventMachine and EventMachine::Connection for documentation and
8
- # usage examples.
9
- #
10
- #----------------------------------------------------------------------------
11
- #
12
- # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
- # Gmail: blackhedd
14
- #
15
- # This program is free software; you can redistribute it and/or modify
16
- # it under the terms of either: 1) the GNU General Public License
17
- # as published by the Free Software Foundation; either version 2 of the
18
- # License, or (at your option) any later version; or 2) Ruby's License.
19
- #
20
- # See the file COPYING for complete licensing information.
21
- #
22
- #---------------------------------------------------------------------------
23
- #
24
- #
25
-
26
- #--
27
- # This defines EventMachine::Deferrable#future, which requires
28
- # that the rest of EventMachine::Deferrable has already been seen.
29
- # (It's in deferrable.rb.)
30
-
31
- module EventMachine
32
- module Deferrable
33
-
34
- # A future is a sugaring of a typical deferrable usage.
35
- #--
36
- # Evaluate arg (which may be an expression or a block).
37
- # What's the class of arg?
38
- # If arg is an ordinary expression, then return it.
39
- # If arg is deferrable (responds to :set_deferred_status),
40
- # then look at the arguments. If either callback or errback
41
- # are defined, then use them. If neither are defined, then
42
- # use the supplied block (if any) as the callback.
43
- # Then return arg.
44
- def self.future arg, cb=nil, eb=nil, &blk
45
- arg = arg.call if arg.respond_to?(:call)
46
-
47
- if arg.respond_to?(:set_deferred_status)
48
- if cb || eb
49
- arg.callback(&cb) if cb
50
- arg.errback(&eb) if eb
51
- else
52
- arg.callback(&blk) if blk
53
- end
54
- end
55
-
56
- arg
57
- end
58
-
59
- end
60
- end
61
-
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 Jul 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+
26
+ #--
27
+ # This defines EventMachine::Deferrable#future, which requires
28
+ # that the rest of EventMachine::Deferrable has already been seen.
29
+ # (It's in deferrable.rb.)
30
+
31
+ module EventMachine
32
+ module Deferrable
33
+
34
+ # A future is a sugaring of a typical deferrable usage.
35
+ #--
36
+ # Evaluate arg (which may be an expression or a block).
37
+ # What's the class of arg?
38
+ # If arg is an ordinary expression, then return it.
39
+ # If arg is deferrable (responds to :set_deferred_status),
40
+ # then look at the arguments. If either callback or errback
41
+ # are defined, then use them. If neither are defined, then
42
+ # use the supplied block (if any) as the callback.
43
+ # Then return arg.
44
+ def self.future arg, cb=nil, eb=nil, &blk
45
+ arg = arg.call if arg.respond_to?(:call)
46
+
47
+ if arg.respond_to?(:set_deferred_status)
48
+ if cb || eb
49
+ arg.callback(&cb) if cb
50
+ arg.errback(&eb) if eb
51
+ else
52
+ arg.callback(&blk) if blk
53
+ end
54
+ end
55
+
56
+ arg
57
+ end
58
+
59
+ end
60
+ end
61
+
@@ -0,0 +1,270 @@
1
+ module EventMachine
2
+ # A simple iterator for concurrent asynchronous work.
3
+ #
4
+ # Unlike ruby's built-in iterators, the end of the current iteration cycle is signaled manually,
5
+ # instead of happening automatically after the yielded block finishes executing. For example:
6
+ #
7
+ # (0..10).each{ |num| }
8
+ #
9
+ # becomes:
10
+ #
11
+ # EM::Iterator.new(0..10).each{ |num,iter| iter.next }
12
+ #
13
+ # This is especially useful when doing asynchronous work via reactor libraries and
14
+ # functions. For example, given a sync and async http api:
15
+ #
16
+ # response = sync_http_get(url); ...
17
+ # async_http_get(url){ |response| ... }
18
+ #
19
+ # a synchronous iterator such as:
20
+ #
21
+ # responses = urls.map{ |url| sync_http_get(url) }
22
+ # ...
23
+ # puts 'all done!'
24
+ #
25
+ # could be written as:
26
+ #
27
+ # EM::Iterator.new(urls).map(proc{ |url,iter|
28
+ # async_http_get(url){ |res|
29
+ # iter.return(res)
30
+ # }
31
+ # }, proc{ |responses|
32
+ # ...
33
+ # puts 'all done!'
34
+ # })
35
+ #
36
+ # Now, you can take advantage of the asynchronous api to issue requests in parallel. For example,
37
+ # to fetch 10 urls at a time, simply pass in a concurrency of 10:
38
+ #
39
+ # EM::Iterator.new(urls, 10).each do |url,iter|
40
+ # async_http_get(url){ iter.next }
41
+ # end
42
+ #
43
+ class Iterator
44
+ # Create a new parallel async iterator with specified concurrency.
45
+ #
46
+ # i = EM::Iterator.new(1..100, 10)
47
+ #
48
+ # will create an iterator over the range that processes 10 items at a time. Iteration
49
+ # is started via #each, #map or #inject
50
+ #
51
+ def initialize(list, concurrency = 1)
52
+ raise ArgumentError, 'argument must be an array' unless list.respond_to?(:to_a)
53
+ @list = list.to_a.dup
54
+ @concurrency = concurrency
55
+
56
+ @started = false
57
+ @ended = false
58
+ end
59
+
60
+ # Change the concurrency of this iterator. Workers will automatically be spawned or destroyed
61
+ # to accomodate the new concurrency level.
62
+ #
63
+ def concurrency=(val)
64
+ old = @concurrency
65
+ @concurrency = val
66
+
67
+ spawn_workers if val > old and @started and !@ended
68
+ end
69
+ attr_reader :concurrency
70
+
71
+ # Iterate over a set of items using the specified block or proc.
72
+ #
73
+ # EM::Iterator.new(1..100).each do |num, iter|
74
+ # puts num
75
+ # iter.next
76
+ # end
77
+ #
78
+ # An optional second proc is invoked after the iteration is complete.
79
+ #
80
+ # EM::Iterator.new(1..100).each(
81
+ # proc{ |num,iter| iter.next },
82
+ # proc{ puts 'all done' }
83
+ # )
84
+ #
85
+ def each(foreach=nil, after=nil, &blk)
86
+ raise ArgumentError, 'proc or block required for iteration' unless foreach ||= blk
87
+ raise RuntimeError, 'cannot iterate over an iterator more than once' if @started or @ended
88
+
89
+ @started = true
90
+ @pending = 0
91
+ @workers = 0
92
+
93
+ all_done = proc{
94
+ after.call if after and @ended and @pending == 0
95
+ }
96
+
97
+ @process_next = proc{
98
+ # p [:process_next, :pending=, @pending, :workers=, @workers, :ended=, @ended, :concurrency=, @concurrency, :list=, @list]
99
+ unless @ended or @workers > @concurrency
100
+ if @list.empty?
101
+ @ended = true
102
+ @workers -= 1
103
+ all_done.call
104
+ else
105
+ item = @list.shift
106
+ @pending += 1
107
+
108
+ is_done = false
109
+ on_done = proc{
110
+ raise RuntimeError, 'already completed this iteration' if is_done
111
+ is_done = true
112
+
113
+ @pending -= 1
114
+
115
+ if @ended
116
+ all_done.call
117
+ else
118
+ EM.next_tick(@process_next)
119
+ end
120
+ }
121
+ class << on_done
122
+ alias :next :call
123
+ end
124
+
125
+ foreach.call(item, on_done)
126
+ end
127
+ else
128
+ @workers -= 1
129
+ end
130
+ }
131
+
132
+ spawn_workers
133
+
134
+ self
135
+ end
136
+
137
+ # Collect the results of an asynchronous iteration into an array.
138
+ #
139
+ # EM::Iterator.new(%w[ pwd uptime uname date ], 2).map(proc{ |cmd,iter|
140
+ # EM.system(cmd){ |output,status|
141
+ # iter.return(output)
142
+ # }
143
+ # }, proc{ |results|
144
+ # p results
145
+ # })
146
+ #
147
+ def map(foreach, after)
148
+ index = 0
149
+
150
+ inject([], proc{ |results,item,iter|
151
+ i = index
152
+ index += 1
153
+
154
+ is_done = false
155
+ on_done = proc{ |res|
156
+ raise RuntimeError, 'already returned a value for this iteration' if is_done
157
+ is_done = true
158
+
159
+ results[i] = res
160
+ iter.return(results)
161
+ }
162
+ class << on_done
163
+ alias :return :call
164
+ def next
165
+ raise NoMethodError, 'must call #return on a map iterator'
166
+ end
167
+ end
168
+
169
+ foreach.call(item, on_done)
170
+ }, proc{ |results|
171
+ after.call(results)
172
+ })
173
+ end
174
+
175
+ # Inject the results of an asynchronous iteration onto a given object.
176
+ #
177
+ # EM::Iterator.new(%w[ pwd uptime uname date ], 2).inject({}, proc{ |hash,cmd,iter|
178
+ # EM.system(cmd){ |output,status|
179
+ # hash[cmd] = status.exitstatus == 0 ? output.strip : nil
180
+ # iter.return(hash)
181
+ # }
182
+ # }, proc{ |results|
183
+ # p results
184
+ # })
185
+ #
186
+ def inject(obj, foreach, after)
187
+ each(proc{ |item,iter|
188
+ is_done = false
189
+ on_done = proc{ |res|
190
+ raise RuntimeError, 'already returned a value for this iteration' if is_done
191
+ is_done = true
192
+
193
+ obj = res
194
+ iter.next
195
+ }
196
+ class << on_done
197
+ alias :return :call
198
+ def next
199
+ raise NoMethodError, 'must call #return on an inject iterator'
200
+ end
201
+ end
202
+
203
+ foreach.call(obj, item, on_done)
204
+ }, proc{
205
+ after.call(obj)
206
+ })
207
+ end
208
+
209
+ private
210
+
211
+ # Spawn workers to consume items from the iterator's enumerator based on the current concurrency level.
212
+ #
213
+ def spawn_workers
214
+ EM.next_tick(start_worker = proc{
215
+ if @workers < @concurrency and !@ended
216
+ # p [:spawning_worker, :workers=, @workers, :concurrency=, @concurrency, :ended=, @ended]
217
+ @workers += 1
218
+ @process_next.call
219
+ EM.next_tick(start_worker)
220
+ end
221
+ })
222
+ nil
223
+ end
224
+ end
225
+ end
226
+
227
+ if __FILE__ == $0
228
+ $:.unshift File.join(File.dirname(__FILE__), '..')
229
+ require 'eventmachine'
230
+
231
+ # TODO: real tests
232
+ # TODO: pass in one object instead of two? .each{ |iter| puts iter.current; iter.next }
233
+ # TODO: support iter.pause/resume/stop/break/continue?
234
+ # TODO: create some exceptions instead of using RuntimeError
235
+ # TODO: support proc instead of enumerable? EM::Iterator.new(proc{ return queue.pop })
236
+
237
+ EM.run{
238
+ EM::Iterator.new(1..50).each{ |num,iter| p num; iter.next }
239
+ EM::Iterator.new([1,2,3], 10).each{ |num,iter| p num; iter.next }
240
+
241
+ i = EM::Iterator.new(1..100, 5)
242
+ i.each(proc{|num,iter|
243
+ p num.to_s
244
+ iter.next
245
+ }, proc{
246
+ p :done
247
+ })
248
+ EM.add_timer(0.03){
249
+ i.concurrency = 1
250
+ }
251
+ EM.add_timer(0.04){
252
+ i.concurrency = 3
253
+ }
254
+
255
+ EM::Iterator.new(100..150).map(proc{ |num,iter|
256
+ EM.add_timer(0.01){ iter.return(num) }
257
+ }, proc{ |results|
258
+ p results
259
+ })
260
+
261
+ EM::Iterator.new(%w[ pwd uptime uname date ], 2).inject({}, proc{ |hash,cmd,iter|
262
+ EM.system(cmd){ |output,status|
263
+ hash[cmd] = status.exitstatus == 0 ? output.strip : nil
264
+ iter.return(hash)
265
+ }
266
+ }, proc{ |results|
267
+ p results
268
+ })
269
+ }
270
+ end