eventmachine-maglev- 0.12.10 → 1.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. data/.gitignore +7 -0
  2. data/.yardopts +7 -0
  3. data/Gemfile +3 -0
  4. data/README.md +109 -0
  5. data/Rakefile +14 -368
  6. data/docs/DocumentationGuidesIndex.md +27 -0
  7. data/docs/GettingStarted.md +521 -0
  8. data/docs/old/DEFERRABLES +246 -0
  9. data/docs/{KEYBOARD → old/KEYBOARD} +15 -11
  10. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  11. data/docs/old/SMTP +4 -0
  12. data/docs/old/SPAWNED_PROCESSES +148 -0
  13. data/eventmachine.gemspec +20 -26
  14. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  15. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  16. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  17. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  18. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  19. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  20. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  21. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  22. data/examples/{ex_channel.rb → old/ex_channel.rb} +3 -3
  23. data/examples/old/ex_tick_loop_array.rb +15 -0
  24. data/examples/old/ex_tick_loop_counter.rb +32 -0
  25. data/ext/binder.cpp +0 -1
  26. data/ext/cmain.cpp +40 -29
  27. data/ext/ed.cpp +189 -134
  28. data/ext/ed.h +34 -40
  29. data/ext/em.cpp +388 -340
  30. data/ext/em.h +29 -32
  31. data/ext/eventmachine.h +7 -6
  32. data/ext/extconf.rb +57 -48
  33. data/ext/fastfilereader/extconf.rb +5 -3
  34. data/ext/fastfilereader/mapper.cpp +1 -1
  35. data/ext/fastfilereader/rubymain.cpp +0 -1
  36. data/ext/kb.cpp +1 -3
  37. data/ext/pipe.cpp +9 -11
  38. data/ext/project.h +12 -8
  39. data/ext/rubymain.cpp +158 -112
  40. data/java/src/com/rubyeventmachine/EmReactor.java +3 -2
  41. data/lib/em/buftok.rb +35 -63
  42. data/lib/em/callback.rb +43 -11
  43. data/lib/em/channel.rb +22 -15
  44. data/lib/em/completion.rb +303 -0
  45. data/lib/em/connection.rb +341 -208
  46. data/lib/em/deferrable/pool.rb +2 -0
  47. data/lib/em/deferrable.rb +20 -2
  48. data/lib/em/file_watch.rb +37 -18
  49. data/lib/em/iterator.rb +270 -0
  50. data/lib/em/pool.rb +146 -0
  51. data/lib/em/process_watch.rb +5 -4
  52. data/lib/em/processes.rb +8 -4
  53. data/lib/em/protocols/httpclient.rb +27 -11
  54. data/lib/em/protocols/httpclient2.rb +15 -5
  55. data/lib/em/protocols/line_protocol.rb +29 -0
  56. data/lib/em/protocols/memcache.rb +17 -9
  57. data/lib/em/protocols/object_protocol.rb +2 -1
  58. data/lib/em/protocols/postgres3.rb +2 -1
  59. data/lib/em/protocols/smtpclient.rb +19 -11
  60. data/lib/em/protocols/smtpserver.rb +101 -8
  61. data/lib/em/protocols/stomp.rb +9 -7
  62. data/lib/em/protocols/tcptest.rb +3 -2
  63. data/lib/em/protocols.rb +1 -1
  64. data/lib/{pr_eventmachine.rb → em/pure_ruby.rb} +188 -205
  65. data/lib/em/queue.rb +23 -13
  66. data/lib/em/resolver.rb +192 -0
  67. data/lib/em/spawnable.rb +9 -10
  68. data/lib/em/streamer.rb +34 -46
  69. data/lib/em/threaded_resource.rb +90 -0
  70. data/lib/em/tick_loop.rb +85 -0
  71. data/lib/em/timers.rb +8 -3
  72. data/lib/em/version.rb +1 -1
  73. data/lib/eventmachine.rb +582 -686
  74. data/lib/jeventmachine.rb +25 -3
  75. data/tasks/package.rake +98 -0
  76. data/tasks/test.rake +8 -0
  77. data/tests/em_test_helper.rb +64 -0
  78. data/tests/test_attach.rb +56 -56
  79. data/tests/test_basic.rb +111 -168
  80. data/tests/test_channel.rb +5 -6
  81. data/tests/test_completion.rb +177 -0
  82. data/tests/test_connection_count.rb +1 -3
  83. data/tests/test_defer.rb +3 -32
  84. data/tests/test_deferrable.rb +35 -0
  85. data/tests/test_epoll.rb +27 -57
  86. data/tests/test_error_handler.rb +10 -7
  87. data/tests/test_exc.rb +6 -33
  88. data/tests/test_file_watch.rb +51 -35
  89. data/tests/test_futures.rb +10 -38
  90. data/tests/test_get_sock_opt.rb +27 -20
  91. data/tests/test_handler_check.rb +1 -3
  92. data/tests/test_hc.rb +49 -112
  93. data/tests/test_httpclient.rb +34 -62
  94. data/tests/test_httpclient2.rb +14 -39
  95. data/tests/test_inactivity_timeout.rb +44 -40
  96. data/tests/test_kb.rb +26 -52
  97. data/tests/test_ltp.rb +27 -71
  98. data/tests/test_ltp2.rb +1 -30
  99. data/tests/test_next_tick.rb +2 -31
  100. data/tests/test_object_protocol.rb +8 -9
  101. data/tests/test_pause.rb +45 -37
  102. data/tests/test_pending_connect_timeout.rb +42 -38
  103. data/tests/test_pool.rb +128 -0
  104. data/tests/test_process_watch.rb +37 -37
  105. data/tests/test_processes.rb +92 -110
  106. data/tests/test_proxy_connection.rb +137 -61
  107. data/tests/test_pure.rb +30 -67
  108. data/tests/test_queue.rb +10 -4
  109. data/tests/test_resolver.rb +55 -0
  110. data/tests/test_running.rb +1 -29
  111. data/tests/test_sasl.rb +8 -33
  112. data/tests/test_send_file.rb +163 -188
  113. data/tests/test_servers.rb +12 -55
  114. data/tests/test_shutdown_hooks.rb +23 -0
  115. data/tests/test_smtpclient.rb +1 -29
  116. data/tests/test_smtpserver.rb +1 -29
  117. data/tests/test_spawn.rb +2 -31
  118. data/tests/test_ssl_args.rb +9 -10
  119. data/tests/test_ssl_methods.rb +1 -3
  120. data/tests/test_ssl_verify.rb +63 -63
  121. data/tests/test_threaded_resource.rb +53 -0
  122. data/tests/test_tick_loop.rb +59 -0
  123. data/tests/test_timers.rb +52 -91
  124. data/tests/test_ud.rb +1 -29
  125. data/tests/test_unbind_reason.rb +31 -0
  126. metadata +113 -70
  127. data/README +0 -82
  128. data/docs/DEFERRABLES +0 -133
  129. data/docs/LIGHTWEIGHT_CONCURRENCY +0 -70
  130. data/docs/SMTP +0 -2
  131. data/docs/SPAWNED_PROCESSES +0 -89
  132. data/ext/cplusplus.cpp +0 -202
  133. data/ext/emwin.cpp +0 -300
  134. data/ext/emwin.h +0 -94
  135. data/ext/epoll.cpp +0 -26
  136. data/ext/epoll.h +0 -25
  137. data/ext/eventmachine_cpp.h +0 -96
  138. data/ext/files.cpp +0 -94
  139. data/ext/files.h +0 -65
  140. data/ext/sigs.cpp +0 -89
  141. data/ext/sigs.h +0 -32
  142. data/java/src/com/rubyeventmachine/application/Application.java +0 -194
  143. data/java/src/com/rubyeventmachine/application/Connection.java +0 -74
  144. data/java/src/com/rubyeventmachine/application/ConnectionFactory.java +0 -37
  145. data/java/src/com/rubyeventmachine/application/DefaultConnectionFactory.java +0 -46
  146. data/java/src/com/rubyeventmachine/application/PeriodicTimer.java +0 -38
  147. data/java/src/com/rubyeventmachine/application/Timer.java +0 -54
  148. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +0 -109
  149. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +0 -148
  150. data/java/src/com/rubyeventmachine/tests/EMTest.java +0 -80
  151. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +0 -53
  152. data/java/src/com/rubyeventmachine/tests/TestServers.java +0 -75
  153. data/java/src/com/rubyeventmachine/tests/TestTimers.java +0 -90
  154. data/lib/evma/callback.rb +0 -32
  155. data/lib/evma/container.rb +0 -75
  156. data/lib/evma/factory.rb +0 -77
  157. data/lib/evma/protocol.rb +0 -87
  158. data/lib/evma/reactor.rb +0 -48
  159. data/lib/evma.rb +0 -32
  160. data/setup.rb +0 -1585
  161. data/tests/test_errors.rb +0 -82
  162. data/tests/testem.rb +0 -31
  163. data/web/whatis +0 -7
  164. /data/{docs/GNU → GNU} +0 -0
  165. /data/{docs/COPYING → LICENSE} +0 -0
  166. /data/docs/{ChangeLog → old/ChangeLog} +0 -0
  167. /data/docs/{EPOLL → old/EPOLL} +0 -0
  168. /data/docs/{INSTALL → old/INSTALL} +0 -0
  169. /data/docs/{LEGAL → old/LEGAL} +0 -0
  170. /data/docs/{PURE_RUBY → old/PURE_RUBY} +0 -0
  171. /data/docs/{RELEASE_NOTES → old/RELEASE_NOTES} +0 -0
  172. /data/docs/{TODO → old/TODO} +0 -0
  173. /data/examples/{ex_queue.rb → old/ex_queue.rb} +0 -0
  174. /data/examples/{helper.rb → old/helper.rb} +0 -0
@@ -0,0 +1,2 @@
1
+ warn "EM::Deferrable::Pool is deprecated, please use EM::Pool"
2
+ EM::Deferrable::Pool = EM::Pool
data/lib/em/deferrable.rb CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  module EventMachine
27
27
  module Deferrable
28
+ autoload :Pool, 'em/deferrable/pool'
28
29
 
29
30
  # Specify a block to be executed if and when the Deferrable object receives
30
31
  # a status of :succeeded. See #set_deferred_status for more information.
@@ -49,6 +50,14 @@ module EventMachine
49
50
  @callbacks ||= []
50
51
  @callbacks.unshift block # << block
51
52
  end
53
+ self
54
+ end
55
+
56
+ # Cancels an outstanding callback to &block if any. Undoes the action of #callback.
57
+ #
58
+ def cancel_callback block
59
+ @callbacks ||= []
60
+ @callbacks.delete block
52
61
  end
53
62
 
54
63
  # Specify a block to be executed if and when the Deferrable object receives
@@ -67,6 +76,14 @@ module EventMachine
67
76
  @errbacks ||= []
68
77
  @errbacks.unshift block # << block
69
78
  end
79
+ self
80
+ end
81
+
82
+ # Cancels an outstanding errback to &block if any. Undoes the action of #errback.
83
+ #
84
+ def cancel_errback block
85
+ @errbacks ||= []
86
+ @errbacks.delete block
70
87
  end
71
88
 
72
89
  # Sets the "disposition" (status) of the Deferrable object. See also the large set of
@@ -150,10 +167,11 @@ module EventMachine
150
167
  # the Timeout expires (passing no arguments to the object's errbacks).
151
168
  # Setting the status at any time prior to a call to the expiration of the timeout
152
169
  # will cause the timer to be cancelled.
153
- def timeout seconds
170
+ def timeout seconds, *args
154
171
  cancel_timeout
155
172
  me = self
156
- @deferred_timeout = EventMachine::Timer.new(seconds) {me.fail}
173
+ @deferred_timeout = EventMachine::Timer.new(seconds) {me.fail(*args)}
174
+ self
157
175
  end
158
176
 
159
177
  # Cancels an outstanding timeout if any. Undoes the action of #timeout.
data/lib/em/file_watch.rb CHANGED
@@ -1,15 +1,24 @@
1
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.
2
+ # Utility class that is useful for file monitoring. Supported events are
3
+ #
4
+ # * File is modified
5
+ # * File is deleted
6
+ # * File is moved
7
+ #
8
+ # @note On Mac OS X, file watching only works when kqueue is enabled
9
+ #
10
+ # @see EventMachine.watch_file
5
11
  class FileWatch < Connection
6
- # :stopdoc:
12
+ # @private
7
13
  Cmodified = 'modified'.freeze
14
+ # @private
8
15
  Cdeleted = 'deleted'.freeze
16
+ # @private
9
17
  Cmoved = 'moved'.freeze
10
- # :startdoc:
11
18
 
12
- def receive_data(data) #:nodoc:
19
+
20
+ # @private
21
+ def receive_data(data)
13
22
  case data
14
23
  when Cmodified
15
24
  file_modified
@@ -20,35 +29,45 @@ module EventMachine
20
29
  end
21
30
  end
22
31
 
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.
32
+ # Returns the path that is being monitored.
33
+ #
34
+ # @note Current implementation does not pick up on the new filename after a rename occurs.
35
+ #
36
+ # @return [String]
37
+ # @see EventMachine.watch_file
25
38
  def path
26
39
  @path
27
40
  end
28
41
 
29
- # Should be redefined with the user's custom callback that will be fired when the file is modified.
42
+ # Will be called when the file is modified. Supposed to be redefined by subclasses.
43
+ #
44
+ # @abstract
30
45
  def file_modified
31
46
  end
32
47
 
33
- # Should be redefined with the user's custom callback that will be fired when the file is deleted.
48
+ # Will be called when the file is deleted. Supposed to be redefined by subclasses.
34
49
  # When the file is deleted, stop_watching will be called after this to make sure everything is
35
50
  # cleaned up correctly.
36
51
  #
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.
52
+ # @note On Linux (with {http://en.wikipedia.org/wiki/Inotify inotify}), this method will not be called until *all* open file descriptors to
53
+ # the file have been closed.
54
+ #
55
+ # @abstract
39
56
  def file_deleted
40
57
  end
41
58
 
42
- # Should be redefined with the user's custom callback that will be fired when the file is moved or renamed.
59
+ # Will be called when the file is moved or renamed. Supposed to be redefined by subclasses.
60
+ #
61
+ # @abstract
43
62
  def file_moved
44
63
  end
45
64
 
46
65
  # Discontinue monitoring of the file.
47
- # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing unbind.
66
+ #
67
+ # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing {EventMachine::Connection#unbind}.
48
68
  # This will be called automatically when a file is deleted. User code may call it as well.
49
69
  def stop_watching
50
70
  EventMachine::unwatch_filename(@signature)
51
- end
52
- end
53
-
54
- end
71
+ end # stop_watching
72
+ end # FileWatch
73
+ end # EventMachine
@@ -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
data/lib/em/pool.rb ADDED
@@ -0,0 +1,146 @@
1
+ module EventMachine
2
+ # = EventMachine::Pool
3
+ #
4
+ # A simple async resource pool based on a resource and work queue. Resources
5
+ # are enqueued and work waits for resources to become available.
6
+ #
7
+ # Example:
8
+ #
9
+ # EM.run do
10
+ # pool = EM::Pool.new
11
+ # spawn = lambda { pool.add EM::HttpRequest.new('http://example.org') }
12
+ # 10.times { spawn[] }
13
+ # done, scheduled = 0, 0
14
+ #
15
+ # check = lambda do
16
+ # done += 1
17
+ # if done >= scheduled
18
+ # EM.stop
19
+ # end
20
+ # end
21
+ #
22
+ # pool.on_error { |conn| spawn[] }
23
+ #
24
+ # 100.times do
25
+ # pool.perform do |conn|
26
+ # req = conn.get :path => '/', :keepalive => true
27
+ #
28
+ # req.callback do
29
+ # p [:success, conn.object_id, i, req.response.size]
30
+ # check[]
31
+ # end
32
+ #
33
+ # req.errback { check[] }
34
+ #
35
+ # req
36
+ # end
37
+ # end
38
+ # end
39
+ #
40
+ # Resources are expected to be controlled by an object responding to a
41
+ # deferrable/completion style API with callback and errback blocks.
42
+ #
43
+ class Pool
44
+
45
+ def initialize
46
+ @resources = EM::Queue.new
47
+ @removed = []
48
+ @contents = []
49
+ @on_error = nil
50
+ end
51
+
52
+ def add resource
53
+ @contents << resource
54
+ requeue resource
55
+ end
56
+
57
+ def remove resource
58
+ @contents.delete resource
59
+ @removed << resource
60
+ end
61
+
62
+ # Returns a list for introspection purposes only. You should *NEVER* call
63
+ # modification or work oriented methods on objects in this list. A good
64
+ # example use case is periodic statistics collection against a set of
65
+ # connection resources.
66
+ #
67
+ # For example:
68
+ # pool.contents.inject(0) { |sum, connection| connection.num_bytes }
69
+ def contents
70
+ @contents.dup
71
+ end
72
+
73
+ # Define a default catch-all for when the deferrables returned by work
74
+ # blocks enter a failed state. By default all that happens is that the
75
+ # resource is returned to the pool. If on_error is defined, this block is
76
+ # responsible for re-adding the resource to the pool if it is still usable.
77
+ # In other words, it is generally assumed that on_error blocks explicitly
78
+ # handle the rest of the lifetime of the resource.
79
+ def on_error *a, &b
80
+ @on_error = EM::Callback(*a, &b)
81
+ end
82
+
83
+ # Perform a given #call-able object or block. The callable object will be
84
+ # called with a resource from the pool as soon as one is available, and is
85
+ # expected to return a deferrable.
86
+ #
87
+ # The deferrable will have callback and errback added such that when the
88
+ # deferrable enters a finished state, the object is returned to the pool.
89
+ #
90
+ # If on_error is defined, then objects are not automatically returned to the
91
+ # pool.
92
+ def perform(*a, &b)
93
+ work = EM::Callback(*a, &b)
94
+
95
+ @resources.pop do |resource|
96
+ if removed? resource
97
+ @removed.delete resource
98
+ reschedule work
99
+ else
100
+ process work, resource
101
+ end
102
+ end
103
+ end
104
+ alias reschedule perform
105
+
106
+ # A peek at the number of enqueued jobs waiting for resources
107
+ def num_waiting
108
+ @resources.num_waiting
109
+ end
110
+
111
+ # Removed will show resources in a partial pruned state. Resources in the
112
+ # removed list may not appear in the contents list if they are currently in
113
+ # use.
114
+ def removed? resource
115
+ @removed.include? resource
116
+ end
117
+
118
+ protected
119
+ def requeue resource
120
+ @resources.push resource
121
+ end
122
+
123
+ def failure resource
124
+ if @on_error
125
+ @on_error.call resource
126
+ else
127
+ requeue resource
128
+ end
129
+ end
130
+
131
+ def completion deferrable, resource
132
+ deferrable.callback { requeue resource }
133
+ deferrable.errback { failure resource }
134
+ end
135
+
136
+ def process work, resource
137
+ deferrable = work.call resource
138
+ if deferrable.kind_of?(EM::Deferrable)
139
+ completion deferrable, resource
140
+ else
141
+ raise ArgumentError, "deferrable expected from work"
142
+ end
143
+ end
144
+
145
+ end
146
+ end
@@ -3,12 +3,13 @@ module EventMachine
3
3
  # This is subclassed from EventMachine::Connection for use with the process monitoring API. Read the
4
4
  # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_process.
5
5
  class ProcessWatch < Connection
6
- # :stopdoc:
6
+ # @private
7
7
  Cfork = 'fork'.freeze
8
+ # @private
8
9
  Cexit = 'exit'.freeze
9
- # :startdoc:
10
10
 
11
- def receive_data(data) # :nodoc:
11
+ # @private
12
+ def receive_data(data)
12
13
  case data
13
14
  when Cfork
14
15
  process_forked
@@ -41,4 +42,4 @@ module EventMachine
41
42
  end
42
43
  end
43
44
 
44
- end
45
+ end
data/lib/em/processes.rb CHANGED
@@ -39,7 +39,8 @@ module EventMachine
39
39
  class DeferrableChildProcess < EventMachine::Connection
40
40
  include EventMachine::Deferrable
41
41
 
42
- def initialize # :nodoc:
42
+ # @private
43
+ def initialize
43
44
  super
44
45
  @data = []
45
46
  end
@@ -60,16 +61,19 @@ module EventMachine
60
61
  EventMachine.popen( cmd, DeferrableChildProcess )
61
62
  end
62
63
 
63
- def receive_data data # :nodoc:
64
+ # @private
65
+ def receive_data data
64
66
  @data << data
65
67
  end
66
68
 
67
- def unbind # :nodoc:
69
+ # @private
70
+ def unbind
68
71
  succeed( @data.join )
69
72
  end
70
73
  end
71
74
 
72
- class SystemCmd < EventMachine::Connection # :nodoc:
75
+ # @private
76
+ class SystemCmd < EventMachine::Connection
73
77
  def initialize cb
74
78
  @cb = cb
75
79
  @output = []
@@ -3,7 +3,7 @@
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
5
5
  # Date:: 16 July 2006
6
- #
6
+ #
7
7
  # See EventMachine and EventMachine::Connection for documentation and
8
8
  # usage examples.
9
9
  #
@@ -11,25 +11,26 @@
11
11
  #
12
12
  # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
13
  # Gmail: blackhedd
14
- #
14
+ #
15
15
  # This program is free software; you can redistribute it and/or modify
16
16
  # it under the terms of either: 1) the GNU General Public License
17
17
  # as published by the Free Software Foundation; either version 2 of the
18
18
  # License, or (at your option) any later version; or 2) Ruby's License.
19
- #
19
+ #
20
20
  # See the file COPYING for complete licensing information.
21
21
  #
22
22
  #---------------------------------------------------------------------------
23
23
  #
24
- #
24
+ #
25
25
 
26
26
 
27
27
 
28
28
  module EventMachine
29
29
  module Protocols
30
30
 
31
- # === Usage
31
+ # <b>Note:</b> This class is deprecated and will be removed. Please use EM-HTTP-Request instead.
32
32
  #
33
+ # @example
33
34
  # EventMachine.run {
34
35
  # http = EventMachine::Protocols::HttpClient.request(
35
36
  # :host => server,
@@ -62,11 +63,21 @@ module EventMachine
62
63
 
63
64
  MaxPostContentLength = 20 * 1024 * 1024
64
65
 
65
- # === Arg list
66
- # :host => 'ip/dns', :port => fixnum, :verb => 'GET', :request => 'path',
67
- # :basic_auth => {:username => '', :password => ''}, :content => 'content',
68
- # :contenttype => 'text/plain', :query_string => '', :host_header => '',
69
- # :cookie => ''
66
+ def initialize
67
+ warn "HttpClient is deprecated and will be removed. EM-Http-Request should be used instead."
68
+ end
69
+
70
+ # @param args [Hash] The request arguments
71
+ # @option args [String] :host The host IP/DNS name
72
+ # @option args [Integer] :port The port to connect too
73
+ # @option args [String] :verb The request type [GET | POST | DELETE | PUT]
74
+ # @option args [String] :request The request path
75
+ # @option args [Hash] :basic_auth The basic auth credentials (:username and :password)
76
+ # @option args [String] :content The request content
77
+ # @option args [String] :contenttype The content type (e.g. text/plain)
78
+ # @option args [String] :query_string The query string
79
+ # @option args [String] :host_header The host header to set
80
+ # @option args [String] :cookie Cookies to set
70
81
  def self.request( args = {} )
71
82
  args[:port] ||= 80
72
83
  EventMachine.connect( args[:host], args[:port], self ) {|c|
@@ -142,6 +153,11 @@ module EventMachine
142
153
  req << "Cookie: #{args[:cookie]}"
143
154
  end
144
155
 
156
+ # Allow custom HTTP headers, e.g. SOAPAction
157
+ args[:custom_headers].each do |k,v|
158
+ req << "#{k}: #{v}"
159
+ end if args[:custom_headers]
160
+
145
161
  # Basic-auth stanza contributed by Matt Murphy.
146
162
  if args[:basic_auth]
147
163
  basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')
@@ -260,4 +276,4 @@ module EventMachine
260
276
  end
261
277
 
262
278
  end
263
- end
279
+ end