io-reactor 0.05 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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