io-reactor 0.05 → 1.0.4

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/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2008, Michael Granger
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of the author/s, nor the names of the project's
15
+ contributors may be used to endorse or promote products derived from this
16
+ software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README CHANGED
@@ -69,7 +69,7 @@ INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
69
69
  FITNESS FOR A PARTICULAR PURPOSE.
70
70
 
71
71
 
72
- $Id: README,v 1.5 2003/07/21 15:04:50 deveiant Exp $
72
+ $Id: README 85 2008-08-19 20:30:57Z deveiant $
73
73
 
74
74
 
75
75
 
@@ -0,0 +1,257 @@
1
+ #!rake
2
+ #
3
+ # IO-Reactor rakefile
4
+ #
5
+ # Based on various other Rakefiles, especially one by Ben Bleything
6
+ #
7
+ # Copyright (c) 2008 The FaerieMUD Consortium
8
+ #
9
+ # Authors:
10
+ # * Michael Granger <ged@FaerieMUD.org>
11
+ #
12
+
13
+ BEGIN {
14
+ require 'pathname'
15
+ basedir = Pathname.new( __FILE__ ).dirname
16
+
17
+ libdir = basedir + "lib"
18
+ extdir = basedir + "ext"
19
+
20
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
21
+ $LOAD_PATH.unshift( extdir.to_s ) unless $LOAD_PATH.include?( extdir.to_s )
22
+ }
23
+
24
+
25
+ require 'rbconfig'
26
+ require 'rubygems'
27
+ require 'rake'
28
+ require 'rake/rdoctask'
29
+ require 'rake/testtask'
30
+ require 'rake/packagetask'
31
+ require 'rake/clean'
32
+
33
+ $dryrun = false
34
+
35
+ ### Config constants
36
+ BASEDIR = Pathname.new( __FILE__ ).dirname.relative_path_from( Pathname.getwd )
37
+ LIBDIR = BASEDIR + 'lib'
38
+ EXTDIR = BASEDIR + 'ext'
39
+ DOCSDIR = BASEDIR + 'docs'
40
+ PKGDIR = BASEDIR + 'pkg'
41
+
42
+ PKG_NAME = 'io-reactor'
43
+ PKG_SUMMARY = 'Multiplexed IO Reactor class'
44
+ VERSION_FILE = LIBDIR + 'io/reactor.rb'
45
+ PKG_VERSION = VERSION_FILE.read[ /VERSION = '(\d+\.\d+\.\d+)'/, 1 ]
46
+ PKG_FILE_NAME = "#{PKG_NAME.downcase}-#{PKG_VERSION}"
47
+ GEM_FILE_NAME = "#{PKG_FILE_NAME}.gem"
48
+
49
+ ARTIFACTS_DIR = Pathname.new( ENV['CC_BUILD_ARTIFACTS'] || 'artifacts' )
50
+
51
+ TEXT_FILES = %w( Rakefile ChangeLog README LICENSE ).collect {|filename| BASEDIR + filename }
52
+ LIB_FILES = Pathname.glob( LIBDIR + '**/*.rb' ).delete_if {|item| item =~ /\.svn/ }
53
+ EXT_FILES = Pathname.glob( EXTDIR + '**/*.{c,h,rb}' ).delete_if {|item| item =~ /\.svn/ }
54
+
55
+ SPECDIR = BASEDIR + 'spec'
56
+ SPEC_FILES = Pathname.glob( SPECDIR + '**/*_spec.rb' ).delete_if {|item| item =~ /\.svn/ }
57
+
58
+ TESTDIR = BASEDIR + 'tests'
59
+ TEST_FILES = Pathname.glob( TESTDIR + '**/*.tests.rb' ).delete_if {|item| item =~ /\.svn/ }
60
+
61
+ RAKE_TASKDIR = BASEDIR + 'rake'
62
+ RAKE_TASKLIBS = Pathname.glob( RAKE_TASKDIR + '*.rb' )
63
+
64
+ LOCAL_RAKEFILE = BASEDIR + 'Rakefile.local'
65
+
66
+ EXTRA_PKGFILES = []
67
+ EXTRA_PKGFILES.concat Pathname.glob( BASEDIR + 'examples/*.rb' ).delete_if {|item| item =~ /\.svn/ }
68
+
69
+ RELEASE_FILES = TEXT_FILES +
70
+ SPEC_FILES +
71
+ TEST_FILES +
72
+ LIB_FILES +
73
+ EXT_FILES +
74
+ RAKE_TASKLIBS +
75
+ EXTRA_PKGFILES
76
+
77
+ RELEASE_FILES << LOCAL_RAKEFILE if LOCAL_RAKEFILE.exist?
78
+
79
+ COVERAGE_MINIMUM = ENV['COVERAGE_MINIMUM'] ? Float( ENV['COVERAGE_MINIMUM'] ) : 85.0
80
+ RCOV_EXCLUDES = 'spec,tests,/Library/Ruby,/var/lib,/usr/local/lib'
81
+ RCOV_OPTS = [
82
+ '--exclude', RCOV_EXCLUDES,
83
+ '--xrefs',
84
+ '--save',
85
+ '--callsites',
86
+ #'--aggregate', 'coverage.data' # <- doesn't work as of 0.8.1.2.0
87
+ ]
88
+
89
+
90
+ # Subversion constants -- directory names for releases and tags
91
+ SVN_TRUNK_DIR = 'trunk'
92
+ SVN_RELEASES_DIR = 'releases'
93
+ SVN_BRANCHES_DIR = 'branches'
94
+ SVN_TAGS_DIR = 'tags'
95
+
96
+ SVN_DOTDIR = BASEDIR + '.svn'
97
+ SVN_ENTRIES = SVN_DOTDIR + 'entries'
98
+
99
+
100
+ ### Load some task libraries that need to be loaded early
101
+ require RAKE_TASKDIR + 'helpers.rb'
102
+ require RAKE_TASKDIR + 'svn.rb'
103
+ require RAKE_TASKDIR + 'verifytask.rb'
104
+
105
+ # Define some constants that depend on the 'svn' tasklib
106
+ PKG_BUILD = get_svn_rev( BASEDIR ) || 0
107
+ SNAPSHOT_PKG_NAME = "#{PKG_FILE_NAME}.#{PKG_BUILD}"
108
+ SNAPSHOT_GEM_NAME = "#{SNAPSHOT_PKG_NAME}.gem"
109
+
110
+ # Documentation constants
111
+ RDOC_OPTIONS = [
112
+ '-w', '4',
113
+ '-SHN',
114
+ '-i', '.',
115
+ '-m', 'README',
116
+ '-W', 'http://deveiate.org/projects/IO-Reactor//browser/trunk/'
117
+ ]
118
+
119
+ # Release constants
120
+ SMTP_HOST = 'mail.faeriemud.org'
121
+ SMTP_PORT = 465 # SMTP + SSL
122
+
123
+ # Project constants
124
+ PROJECT_HOST = 'deveiate.org'
125
+ PROJECT_PUBDIR = "/usr/local/www/public/code"
126
+ PROJECT_DOCDIR = "#{PROJECT_PUBDIR}/#{PKG_NAME}"
127
+ PROJECT_SCPPUBURL = "#{PROJECT_HOST}:#{PROJECT_PUBDIR}"
128
+ PROJECT_SCPDOCURL = "#{PROJECT_HOST}:#{PROJECT_DOCDIR}"
129
+
130
+ # Rubyforge stuff
131
+ RUBYFORGE_GROUP = 'deveiate'
132
+ RUBYFORGE_PROJECT = 'io-reactor'
133
+
134
+ # Gem dependencies: gemname => version
135
+ DEPENDENCIES = {
136
+ }
137
+
138
+ # Non-gem requirements: packagename => version
139
+ REQUIREMENTS = {
140
+ }
141
+
142
+ # RubyGem specification
143
+ GEMSPEC = Gem::Specification.new do |gem|
144
+ gem.name = PKG_NAME.downcase
145
+ gem.version = PKG_VERSION
146
+
147
+ gem.summary = PKG_SUMMARY
148
+ gem.description = <<-EOD
149
+ A pure-Ruby implementation of an asynchronous multiplexed IO
150
+ Reactor which is based on the Reactor design pattern found in _Pattern-Oriented
151
+ Software Architecture, Volume 2: Patterns for Concurrent and Networked
152
+ Objects_. It allows a single thread to demultiplex and dispatch events from one
153
+ or more IO objects to the appropriate handler.
154
+ EOD
155
+
156
+ gem.authors = 'Michael Granger'
157
+ gem.email = 'ged@FaerieMUD.org'
158
+ gem.homepage = 'http://deveiate.org/projects/IO-Reactor/'
159
+ gem.rubyforge_project = RUBYFORGE_PROJECT
160
+
161
+ gem.has_rdoc = true
162
+ gem.rdoc_options = RDOC_OPTIONS
163
+
164
+ gem.files = RELEASE_FILES.
165
+ collect {|f| f.relative_path_from(BASEDIR).to_s }
166
+ gem.test_files = SPEC_FILES.
167
+ collect {|f| f.relative_path_from(BASEDIR).to_s }
168
+
169
+ DEPENDENCIES.each do |name, version|
170
+ version = '>= 0' if version.length.zero?
171
+ gem.add_dependency( name, version )
172
+ end
173
+
174
+ REQUIREMENTS.each do |name, version|
175
+ gem.requirements << [ name, version ].compact.join(' ')
176
+ end
177
+ end
178
+
179
+ $trace = Rake.application.options.trace ? true : false
180
+ $dryrun = Rake.application.options.dryrun ? true : false
181
+
182
+
183
+ # Load any remaining task libraries
184
+ RAKE_TASKLIBS.each do |tasklib|
185
+ next if tasklib =~ %r{/(helpers|svn|verifytask)\.rb$}
186
+ begin
187
+ require tasklib
188
+ rescue ScriptError => err
189
+ fail "Task library '%s' failed to load: %s: %s" %
190
+ [ tasklib, err.class.name, err.message ]
191
+ trace "Backtrace: \n " + err.backtrace.join( "\n " )
192
+ rescue => err
193
+ log "Task library '%s' failed to load: %s: %s. Some tasks may not be available." %
194
+ [ tasklib, err.class.name, err.message ]
195
+ trace "Backtrace: \n " + err.backtrace.join( "\n " )
196
+ end
197
+ end
198
+
199
+ # Load any project-specific rules defined in 'Rakefile.local' if it exists
200
+ import LOCAL_RAKEFILE if LOCAL_RAKEFILE.exist?
201
+
202
+
203
+ #####################################################################
204
+ ### T A S K S
205
+ #####################################################################
206
+
207
+ ### Default task
208
+ task :default => [:clean, :local, :spec, :rdoc, :package]
209
+
210
+ ### Task the local Rakefile can append to -- no-op by default
211
+ task :local
212
+
213
+
214
+ ### Task: clean
215
+ CLEAN.include 'coverage'
216
+ CLOBBER.include 'artifacts', 'coverage.info', PKGDIR
217
+
218
+ # Target to hinge on ChangeLog updates
219
+ file SVN_ENTRIES
220
+
221
+ ### Task: changelog
222
+ file 'ChangeLog' => SVN_ENTRIES.to_s do |task|
223
+ log "Updating #{task.name}"
224
+
225
+ changelog = make_svn_changelog()
226
+ File.open( task.name, 'w' ) do |fh|
227
+ fh.print( changelog )
228
+ end
229
+ end
230
+
231
+
232
+ ### Task: cruise (Cruisecontrol task)
233
+ desc "Cruisecontrol build"
234
+ task :cruise => [:clean, :spec, :package] do |task|
235
+ raise "Artifacts dir not set." if ARTIFACTS_DIR.to_s.empty?
236
+ artifact_dir = ARTIFACTS_DIR.cleanpath
237
+ artifact_dir.mkpath
238
+
239
+ coverage = BASEDIR + 'coverage'
240
+ if coverage.exist? && coverage.directory?
241
+ $stderr.puts "Copying coverage stats..."
242
+ FileUtils.cp_r( 'coverage', artifact_dir )
243
+ end
244
+
245
+ $stderr.puts "Copying packages..."
246
+ FileUtils.cp_r( FileList['pkg/*'].to_a, artifact_dir )
247
+ end
248
+
249
+
250
+ desc "Update the build system to the latest version"
251
+ task :update_build do
252
+ log "Updating the build system"
253
+ sh 'svn', 'up', RAKE_TASKDIR
254
+ log "Updating the Rakefile"
255
+ sh 'rake', '-f', RAKE_TASKDIR + 'Metarakefile'
256
+ end
257
+
@@ -35,7 +35,7 @@
35
35
  #
36
36
  # == Version
37
37
  #
38
- # $Id: chatserver.rb,v 1.4 2003/08/04 23:53:32 deveiant Exp $
38
+ # $Id: chatserver.rb 85 2008-08-19 20:30:57Z deveiant $
39
39
  #
40
40
 
41
41
  require 'io/reactor'
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'io/reactor'
4
+
5
+ def debug( msg )
6
+ return unless $DEBUG
7
+ $stderr.puts( " " + msg )
8
+ end
9
+
10
+ reactor = IO::Reactor.new
11
+ data_to_send = "some stuff to send"
12
+
13
+ reader, writer = IO.pipe
14
+
15
+ # Read from the reader end of the pipe until the writer finishes
16
+ reactor.register( reader, :read ) do |io,event|
17
+ if io.eof?
18
+ debug "done with reading, closing reader"
19
+ reactor.unregister( io )
20
+ io.close
21
+ else
22
+ debug "reading..."
23
+ puts io.read( 256 )
24
+ end
25
+ end
26
+
27
+ # Write to the writer end of the pipe until there's no data left
28
+ reactor.register( writer, :write ) do |io,event|
29
+ debug "writing..."
30
+ bytes = io.write( data_to_send )
31
+ data_to_send.slice!( 0, bytes )
32
+
33
+ if data_to_send.empty?
34
+ debug "done with writing; closing writer."
35
+ reactor.unregister( io )
36
+ io.close
37
+ end
38
+ end
39
+
40
+ # Now pump the reactor until both sides are done
41
+ puts "Starting IO"
42
+ until reactor.empty?
43
+ debug "polling..."
44
+ reactor.poll
45
+ end
46
+ puts "done, exiting."
47
+
48
+
49
+ # $ ruby -d -Ilib examples/simple.rb
50
+ # Starting IO
51
+ # polling...
52
+ # writing...
53
+ # done with writing; closing writer.
54
+ # polling...
55
+ # reading...
56
+ # some stuff to send
57
+ # polling...
58
+ # done with reading, closing reader
59
+ # done, exiting.
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'socket'
4
+ require 'io/reactor'
5
+
6
+ listener = TCPServer.new( '127.0.0.1', 18181 )
7
+ reactor = IO::Reactor.new
8
+
9
+ Signal.trap( 'HUP' ) { reactor.clear }
10
+ Signal.trap( 'INT' ) { reactor.clear }
11
+
12
+ reactor.register( listener, :read ) do |sock, _|
13
+ client = sock.accept
14
+ $stderr.puts "Accepted client from %s:%d" % [ client.peeraddr[2], client.peeraddr[1] ]
15
+ # message = Time.now.to_s + "\r\n\r\n"
16
+ message = File.read(__FILE__) + "\r\n\r\n"
17
+
18
+ reactor.register( client, message, :write ) do |io, event, buf|
19
+ bytes = io.write( buf )
20
+ buf.slice!( 0, bytes )
21
+
22
+ if buf.empty?
23
+ reactor.unregister( io )
24
+ io.close
25
+ end
26
+ end
27
+ end
28
+
29
+
30
+ $stdout.puts "Starting up..."
31
+ until reactor.empty?
32
+ reactor.poll( 0.1 ) do |io, event|
33
+ case event
34
+ when :error
35
+ $stderr.puts " %p: in the error event handler" % [ io ]
36
+ reactor.unregister( io )
37
+ io.close
38
+ else
39
+ fail "Reactor got unexpected event %p on %p" % [ event, io ]
40
+ end
41
+ end
42
+ end
43
+ $stdout.puts "Server shut down."
44
+
@@ -1,48 +1,45 @@
1
- #!/usr/bin/ruby
2
- #
1
+ #!/usr/bin/env ruby
2
+
3
+ class IO # :nodoc:
4
+
3
5
  # An object-oriented multiplexing asynchronous IO mechanism for Ruby.
4
6
  #
5
7
  # == Synopsis
6
8
  #
7
- # require 'io/reactor'
8
- # require 'socket'
9
- #
10
- # reactorobj = IO::Reactor::new
11
- #
12
- # # Open a listening socket on port 1138 and register it with the
13
- # # reactor. When a :read event occurs on it, it means an incoming
14
- # # connection has been established
15
- # sock = TCPServer::new('localhost', 1138)
16
- # reactorobj.register( sock, :read ) {|sock,event|
17
- # case event
18
- #
19
- # # Accept the incoming connection, registering it also with
20
- # # the reactor; events on client sockets are handled by the
21
- # # #clientHandler method.
22
- # when :read
23
- # clsock = sock.accept
24
- # reactorobj.register( clsock, :read, :write,
25
- # &method(:clientHandler) )
26
- #
27
- # # Errors on the main listening socket cause the whole
28
- # # reactor to be shut down
29
- # when :error
30
- # reactorobj.clear
31
- # $stderr.puts "Server error: Shutting down"
32
- #
33
- # else
34
- # $stderr.puts "Unhandled event: #{event}"
35
- # end
36
- # }
37
- #
38
- # # Drive the reactor until it's empty with a single thread
39
- # Thread::new { reactorobj.poll until reactorobj.empty? }
9
+ # reactor = IO::Reactor.new
10
+ # data_to_send = "some stuff to send"
11
+ #
12
+ # reader, writer = IO.pipe
13
+ #
14
+ # # Read from the reader end of the pipe until the writer finishes
15
+ # reactor.register( reader, :read ) do |io,event|
16
+ # if io.eof?
17
+ # reactor.unregister( io )
18
+ # io.close
19
+ # else
20
+ # puts io.read( 256 )
21
+ # end
22
+ # end
23
+ #
24
+ # # Write to the writer end of the pipe until there's no data left
25
+ # reactor.register( writer, :write ) do |io,event|
26
+ # bytes = io.write( data_to_send )
27
+ # data_to_send.slice!( 0, bytes )
28
+ #
29
+ # if data_to_send.empty?
30
+ # reactor.unregister( io )
31
+ # io.close
32
+ # end
33
+ # end
34
+ #
35
+ # # Now pump the reactor until both sides are done
36
+ # reactor.poll until reactor.empty?
40
37
  #
41
38
  # == Author
42
39
  #
43
40
  # Michael Granger <ged@FaerieMUD.org>
44
41
  #
45
- # Copyright (c) 2002, 2003 The FaerieMUD Consortium. All rights reserved.
42
+ # Copyright (c) 2002-2008 The FaerieMUD Consortium. All rights reserved.
46
43
  #
47
44
  # This module is free software. You may use, modify, and/or redistribute this
48
45
  # software under the same terms as Ruby itself.
@@ -53,33 +50,40 @@
53
50
  #
54
51
  # == Version
55
52
  #
56
- # $Id: reactor.rb,v 1.13 2003/08/04 23:56:14 deveiant Exp $
53
+ # $Id: reactor.rb 86 2008-08-19 23:41:58Z deveiant $
57
54
  #
55
+ class Reactor
58
56
 
59
- require 'delegate'
60
- require 'rbconfig'
57
+ ### Class constants
61
58
 
62
- class IO
59
+ # SVN Revision
60
+ SVNRev = %q$Rev: 86 $
63
61
 
64
- ### An object-oriented multiplexing asynchronous IO reactor class.
65
- class Reactor
62
+ # SVN Id
63
+ SVNId = %q$Id: reactor.rb 86 2008-08-19 23:41:58Z deveiant $
66
64
 
67
- ### Class constants
68
- Version = /([\d\.]+)/.match( %q{$Revision: 1.13 $} )[1]
69
- Rcsid = %q$Id: reactor.rb,v 1.13 2003/08/04 23:56:14 deveiant Exp $
65
+ # Package version
66
+ VERSION = '1.0.4'
70
67
 
71
- ValidEvents = [:read, :write, :error]
68
+ # List of valid event types, in the order IO#select returns them
69
+ VALID_EVENTS = [:read, :write, :error]
70
+ VALID_EVENTS.freeze
71
+
72
+
73
+ #################################################################
74
+ ### I N S T A N C E M E T H O D S
75
+ #################################################################
72
76
 
73
77
  ### Create and return a new IO reactor object.
74
78
  def initialize
75
- @handles = Hash::new {|hsh,key|
79
+ @handles = Hash.new {|hsh,key|
76
80
  hsh[ key ] = {
77
81
  :events => [],
78
82
  :handler => nil,
79
83
  :args => [],
80
84
  }
81
85
  }
82
- @pendingEvents = Hash::new {|hsh,key| hsh[ key ] = []}
86
+ @pending_events = Hash.new {|hsh,key| hsh[ key ] = []}
83
87
  end
84
88
 
85
89
 
@@ -94,7 +98,8 @@ class Reactor
94
98
 
95
99
  # The Hash of unhandled events which occurred in the last call to #poll,
96
100
  # keyed by handle.
97
- attr_reader :pendingEvents
101
+ attr_reader :pending_events
102
+ alias_method :pendingEvents, :pending_events
98
103
 
99
104
 
100
105
  ### Register the specified IO object with the reactor for events given as
@@ -114,15 +119,15 @@ class Reactor
114
119
  ### Registering a handle will unregister any previously registered
115
120
  ### event/handler+arguments pairs associated with the handle.
116
121
  def register( io, *args, &handler )
117
- events = [:read, :write, :error] & args
122
+ events = VALID_EVENTS & args
118
123
  args -= events
119
124
 
120
125
  self.unregister( io )
121
- self.enableEvents( io, *events )
126
+ self.enable_events( io, *events )
122
127
  if handler
123
- self.setHandler( io, *args, &handler )
128
+ self.set_handler( io, *args, &handler )
124
129
  else
125
- self.setArgs( io, *args )
130
+ self.set_args( io, *args )
126
131
  end
127
132
 
128
133
  return self
@@ -130,81 +135,95 @@ class Reactor
130
135
  alias_method :add, :register
131
136
 
132
137
 
138
+ ### Returns +true+ if the given +io+ handle is registered with the reactor.
139
+ def registered?( io )
140
+ return @handles.key?( io )
141
+ end
142
+
143
+
133
144
  ### Add the specified +events+ to the list that will be polled for on the
134
145
  ### given +io+ handle.
135
- def enableEvents( io, *events )
146
+ def enable_events( io, *events )
136
147
  @handles[ io ][:events] |= events
137
148
  end
149
+ alias_method :enableEvents, :enable_events
138
150
 
139
151
 
140
152
  ### Remove the specified +events+ from the list that will be polled for on
141
153
  ### the given +io+ handle.
142
- def disableEvents( io, *events )
154
+ def disable_events( io, *events )
143
155
  raise RuntimeError, "Cannot disable the :error event" if
144
156
  events.include?( :error )
145
157
  @handles[ io ][:events] -= events
146
158
  end
159
+ alias_method :disableEvents, :disable_events
147
160
 
148
161
 
162
+ ### Returns +true+ if the specified +event+ is enabled for the given +io+.
163
+ def event_enabled?( io, event )
164
+ return false unless @handles.key?( io )
165
+ return true if event == :error # Error is always enabled for all handles
166
+ return @handles[ io ][ :events ].include?( event )
167
+ end
168
+ alias_method :has_event_enabled?, :event_enabled?
169
+
170
+
149
171
  ### Set the handler for events on the given +io+ handle to the specified
150
172
  ### +handler+. If any +args+ are present, they will be passed as an exploded
151
173
  ### array to the handler for each event. Returns the previously-registered
152
174
  ### handler, if any.
153
- def setHandler( io, *args, &handler )
175
+ def set_handler( io, *args, &handler )
154
176
  rval = @handles[ io ][:handler]
155
177
  @handles[ io ][:handler] = handler
156
- self.setArgs( io, *args )
178
+ self.set_args( io, *args )
157
179
  return rval
158
180
  end
181
+ alias_method :setHandler, :set_handler
159
182
 
160
183
 
161
184
  ### Remove and return the handler for events on the given +io+ handle.
162
- def removeHandler( io )
185
+ def remove_handler( io )
163
186
  rval = @handles[ io ][:handler]
164
187
  @handles[ io ][:handler] = nil
165
- self.removeArgs( io )
188
+ self.remove_args( io )
166
189
  return rval
167
190
  end
191
+ alias_method :removeHandler, :remove_handler
168
192
 
169
193
 
170
194
  ### Set the additional arguments to pass to the handler for the given +io+
171
195
  ### handle on each event to the given +args+.
172
- def setArgs( io, *args )
196
+ def set_args( io, *args )
173
197
  rval = @handles[ io ][:args]
174
198
  @handles[ io ][:args] = args
175
199
  return rval
176
200
  end
201
+ alias_method :setArgs, :set_args
177
202
 
178
203
 
179
204
  ### Remove the arguments for the given handle to the given +args+.
180
- def removeArgs( io )
205
+ def remove_args( io )
181
206
  return @handles[ io ][:args].clear
182
207
  end
208
+ alias_method :removeArgs, :remove_args
183
209
 
184
210
 
185
211
  ### Remove the specified <tt>io</tt> from the receiver's list of registered
186
212
  ### handles, if present. Returns the handle if it was registered, or
187
213
  ### <tt>nil</tt> if it was not.
188
214
  def unregister( io )
189
- @pendingEvents.delete( io )
215
+ @pending_events.delete( io )
190
216
  @handles.delete( io )
191
217
  end
192
218
  alias_method :remove, :unregister
193
219
 
194
220
 
195
- ### Returns true if the specified <tt>io</tt> is registered with the poll
196
- ### object.
197
- def registered?( io )
198
- return @handles.has_key?( io )
199
- end
200
-
201
-
202
221
  ### Clear all registered handles from the poll object. Returns the handles
203
222
  ### that were cleared.
204
223
  def clear
205
224
  rv = @handles.keys
206
225
 
207
- @pendingEvents.clear
226
+ @pending_events.clear
208
227
  @handles.clear
209
228
 
210
229
  return rv
@@ -228,7 +247,7 @@ class Reactor
228
247
  ### those handles with events. If a block is given, it will be invoked once
229
248
  ### for each handle which doesn't have an explicit handler. If no block is
230
249
  ### given, events without explicit handlers are inserted into the reactor's
231
- ### <tt>pendingEvents</tt> attribute.
250
+ ### <tt>pending_events</tt> attribute.
232
251
  ###
233
252
  ### The <tt>timeout</tt> argument is the number of floating-point seconds to
234
253
  ### wait for an event before returning (ie., fourth argument to the
@@ -239,19 +258,22 @@ class Reactor
239
258
  ### occurred.
240
259
  def poll( timeout=-1 ) # :yields: io, eventMask
241
260
  timeout = timeout.to_f
242
- @pendingEvents.clear
261
+ @pending_events.clear
243
262
  count = 0
244
263
 
245
264
  unless @handles.empty?
246
265
  timeout = nil if timeout < 0
247
- eventedHandles = self.getPendingEvents( timeout )
266
+ evented_handles = self.get_pending_events( timeout )
248
267
 
249
268
  # For each event of each io that had an event happen, call any
250
269
  # associated callback, or any provided block, or failing both of
251
270
  # those, add the event to the hash of unhandled pending events.
252
- eventedHandles.each {|io,events|
271
+ evented_handles.each do |io,events|
253
272
  count += 1
254
- events.each {|ev|
273
+ events.each do |ev|
274
+ # Don't continue if the io was unregistered by an earlier handler
275
+ break unless @handles.key?( io )
276
+
255
277
  args = @handles[ io ][:args]
256
278
 
257
279
  if @handles[ io ][:handler]
@@ -259,10 +281,10 @@ class Reactor
259
281
  elsif block_given?
260
282
  yield( io, ev, *args )
261
283
  else
262
- @pendingEvents[io].push( ev )
284
+ @pending_events[io].push( ev )
263
285
  end
264
- }
265
- }
286
+ end
287
+ end
266
288
  end
267
289
 
268
290
  return count
@@ -281,35 +303,40 @@ class Reactor
281
303
 
282
304
  ### Select on the registered handles, returning a Hash of handles => events
283
305
  ### for handles which had events occur.
284
- def getPendingEvents( timeout )
285
- eventHandles = IO::select( self.getReadHandles, self.getWriteHandles,
306
+ def get_pending_events( timeout )
307
+ @handles.delete_if {|io,_| io.closed? }
308
+
309
+ eventHandles = select( self.get_read_handles, self.get_write_handles,
286
310
  @handles.keys, timeout ) or return {}
287
- eventHash = Hash::new {|hsh,io| hsh[io] = []}
311
+ eventHash = Hash.new {|hsh,io| hsh[io] = []}
288
312
 
289
313
  # Fill in the hash with pending events of each type
290
- [:read, :write, :error].each_with_index {|event,i|
314
+ VALID_EVENTS.each_with_index do |event,i|
291
315
  eventHandles[i].each {|io| eventHash[io].push( event )}
292
- }
316
+ end
293
317
  return eventHash
294
318
  end
319
+ alias_method :getPendingEvents, :get_pending_events
295
320
 
296
321
 
297
322
  ### Return an Array of handles which have handlers for the <tt>:read</tt>
298
323
  ### event.
299
- def getReadHandles
324
+ def get_read_handles
300
325
  @handles.
301
326
  find_all {|io,hsh| hsh[:events].include?( :read )}.
302
- collect {|io,hsh| io}
327
+ collect {|io,_| io }
303
328
  end
329
+ alias_method :getReadHandles, :get_read_handles
304
330
 
305
331
 
306
332
  ### Return an Array of handles which have handlers for the <tt>:write</tt>
307
333
  ### event.
308
- def getWriteHandles
334
+ def get_write_handles
309
335
  @handles.
310
336
  find_all {|io,hsh| hsh[:events].include?( :write )}.
311
- collect {|io,hsh| io}
337
+ collect {|io,_| io }
312
338
  end
339
+ alias_method :getWriteHandles, :get_write_handles
313
340
 
314
341
 
315
342
  end # class Reactor