Author: nornagon
Date: 2005-07-05 03:48:19 -0400 (Tue, 05 Jul 2005)
New Revision: 830

Added:
   trunk/clients/ravish/eventmanager.rb
   trunk/clients/ravish/haver.rb
   trunk/clients/ravish/haver/
   trunk/clients/ravish/haver/net.rb
   trunk/clients/ravish/ravish2.rb
Modified:
   trunk/clients/ravish/ravish.rb
Log:
Ravish broken; redone as Ravish2. Ruby haver lib much better and cleaner. 
Ravish2 has no UI yet.

Added: trunk/clients/ravish/eventmanager.rb
===================================================================
--- trunk/clients/ravish/eventmanager.rb        2005-06-26 04:37:26 UTC (rev 
829)
+++ trunk/clients/ravish/eventmanager.rb        2005-07-05 07:48:19 UTC (rev 
830)
@@ -0,0 +1,214 @@
+# EventManager - ruby event loop
+# Copyright (c) 2005 Bryan J. Donlan and Jeremy C. Apthorp
+# This program is FREE SOFTWARE.
+# You may edit and redistribute it under the same terms as ruby.
+
+require 'singleton'
+
+class EventManager
+       include Singleton
+       
+       def initialize
+               # fd watch hashes
+               # @fd{mode}[fd] = { handle -> code }
+               @fdread   = {}
+               @fdwrite  = {}
+               @fderror  = {}
+               # revtok[handle] = fd
+               @revtok   = {}
+               # timers = [[fd, code, handle], ...]
+               @timers   = []
+               # timertok[handle] = ref to [fd, code, handle] in timers
+               @timertok = {}
+               # token is the next handle returned by the watch functions
+               @token  = 0
+               @pipe = IO.pipe
+       end
+
+       def jolt
+               @pipe[1].write 'a'
+       end
+
+       # Check and block for events, once
+       def runonce
+               nexttimer = self.checktimers
+               
+               readhand  = @fdread.keys + [EMAIL PROTECTED]
+               writehand = @fdwrite.keys
+               errorhand = @fderror.keys
+               
+               ret = select(readhand, writehand, errorhand, nexttimer)
+               if !ret then ret = [[],[],[]] end
+
+               ret[0].delete @pipe[0]
+               
+               ret[0].each { |fd| @fdread[fd].values.each  { |c| c.call fd } }
+               ret[1].each { |fd| @fdwrite[fd].values.each { |c| c.call fd } }
+               ret[2].each { |fd| @fderror[fd].values.each { |c| c.call fd } }
+
+               self.checktimers
+       end
+
+       # Check for any timers which should have gone off by now.
+       # Dispatch the ones that have, then return the time remaining
+       # until the next timer, or nil if no timers remain
+       def checktimers
+               now = Time.now.to_f
+               while (@timers.length > 0)
+                       if @timers[0][0] < now
+                               unless @timers[0][1].nil?
+                                       # pass it the time...
+                                       @timers[0][1].call @timers[0][0]
+                               end
+                               @timertok.delete @timers[0][2]
+                               @timers.shift
+                       else
+                               break
+                       end
+               end
+               if (@timers.length > 0)
+                       return @timers[0][0].to_f - now
+               else
+                       return nil
+               end
+       end
+
+       # Schedule lambda f to be called in t seconds. Return an opaque value
+       # which may be passed to canceltimer.
+       def in t, f
+               self.at(Time.now.to_f + t.to_f, f)
+       end
+
+       # Schedule lambda f to be called at the absolute time t. Return
+       # an opaque value which may be passed to canceltimer.
+       def at t, f
+               mytok = @token
+               @token = @token + 1
+               pair = [t, f, mytok]
+               if @timers.empty?
+                       @timers.push pair
+               else
+                       # Hopefully, this will usually be faster than push/sort
+                       idx = nil
+                       @timers.each_index { |i|
+                               t2 = @timers[i][0]
+                               if idx.nil? && t2 > t
+                                       idx = i
+                               end
+                       }
+                       if idx.nil?
+                               @timers.push pair
+                       else
+                               @timers.insert idx, pair
+                       end
+               end
+               @timertok[mytok] = pair
+               jolt
+               return mytok
+       end
+
+       # Cancel a timer indicated by tok, which should be a value returned by
+       # at or in
+       def canceltimer tok
+               pair = @timertok[tok]
+               unless pair.nil?
+                       pair[1] = nil
+                       @timertok.delete tok
+                       jolt
+               end
+       end
+
+       def watchgeneral arr, fd, code
+               if (!arr[fd])
+                       # We can't generate empty values here
+                       # as that'd create spurious fds for the select
+                       # in runonce
+                       arr[fd] = Hash.new 
+               end
+               mytok = @token
+               @token = @token + 1
+               arr[fd][mytok] = code
+               @revtok[mytok] = [arr, fd]
+               jolt
+               return mytok
+       end
+
+       # Watch a file descriptor for a readable condition. When it is
+       # readable call the lambda code. Returns an opaque value suitable
+       # for cancelwatch.
+       def watchread fd, code
+               watchgeneral @fdread, fd, code
+       end
+       
+       # Watch a file descriptor for a writable condition. When it is
+       # writable call the lambda code. Returns an opaque value suitable
+       # for cancelwatch.
+       def watchwrite fd, code
+               watchgeneral @fdwrite, fd, code
+       end
+       
+       # Watch a file descriptor for an error condition. When an error
+       # condition exists call the lambda code. Returns an opaque value
+       # suitable for cancelwatch.
+       def watcherror fd, code
+               watchgeneral @fderror, fd, code
+       end
+
+       def unwatchgeneral arr, fd
+               unless arr[fd].nil?
+                       arr[fd].keys.each { |h| @revtok.delete h }
+               end
+               arr.delete fd
+       end
+       
+       # Cancel all readable condition watches on fd
+       def unwatchread fd
+               unwatchgeneral @fdread, fd
+       end
+       # Cancel all writable condition watches on fd
+       def unwatchwrite fd
+               unwatchgeneral @fdwrite, fd
+       end
+       # Cancel all error condition watches on fd
+       def unwatcherror fd
+               unwatchgeneral @fderror, fd
+       end
+
+       # Cancel all watches on fd
+       def unwatch fd
+               unwatchread fd
+               unwatchwrite fd
+               unwatcherror fd
+       end
+
+       # Given handle, a value returned from one of the watch* functions,
+       # cancel only that watch, leaving other watches on that fd
+       # unaffected
+       def cancelwatch handle
+               pair = @revtok.delete handle
+               unless pair.nil?
+                       arr = pair[0]
+                       fd = pair[1]
+                       arr[fd].delete handle
+                       if (arr[fd].empty?)
+                               arr.delete fd
+                       end
+               end
+       end
+
+       # Run the event loop until stop is called
+       def run
+               @stopme = false
+               while ([EMAIL PROTECTED])
+                       self.runonce
+               end
+       end
+
+       # Exits the loop started with run.
+       def stop
+               @stopme = true
+       end
+end
+
+# vim: set noet ts=4 sw=4 :
+

Added: trunk/clients/ravish/haver/net.rb
===================================================================
--- trunk/clients/ravish/haver/net.rb   2005-06-26 04:37:26 UTC (rev 829)
+++ trunk/clients/ravish/haver/net.rb   2005-07-05 07:48:19 UTC (rev 830)
@@ -0,0 +1,121 @@
+# ROOM::Net
+# Copyright (c) 2005 Jeremy Campbell Apthorp
+# This program is FREE SOFTWARE.
+# You may edit and redistribute it under the same terms as ruby.
+
+require 'socket'
+require 'io/nonblock'
+require 'observer'
+
+module Haver
+       class Net
+               include Observable
+               BufSize = 64
+
+               attr_reader :socket
+
+               def initialize socket, observers=[]
+                       if !socket.kind_of?(TCPSocket)
+                               raise ArgumentError,
+                                       "#{self.class}.new takes a TCPSocket as 
an argument."
+                       end
+                       if observers.kind_of?(Array)
+                               observers.each { |observer|
+                                       if observer.kind_of? Array
+                                               self.add_observer observer[0], 
observer[1]
+                                       else
+                                               self.add_observer observer
+                                       end
+                               }
+                       end
+                       @socket = socket
+                       socket.nonblock = true
+
+                       @buffer = ''
+                       @outbox = Queue.new
+            @closed = false
+
+                       @rthread = Thread.new { rthread }
+                       @wthread = Thread.new { wthread }
+               end
+               
+               def put string
+            if @closed
+                return
+            end
+                       @outbox.push string
+                       self
+               end
+               alias_method :<<, :put
+
+               def rthread
+                       loop {
+                               select([EMAIL PROTECTED])
+                               doread
+                       }
+               end
+
+               def wthread
+                       loop {
+                               m = @outbox.pop
+                               select(nil, [EMAIL PROTECTED])
+                               dowrite m
+                       }
+               end
+
+               def doread
+                       if @socket.closed? || @closed
+                               raise "Tried to read from closed socket."
+                       end
+                       if b = @socket.gets
+                               b.each("\r\n") { |l|
+                                       if l =~ /\r\n$/
+                                               if @buffer.empty?
+                                                       event :data, l.chomp
+                                               else
+                                                       event :data, @buffer + 
l.chomp
+                                                       @buffer = ''
+                                               end
+                                       else
+                                               @buffer += l
+                                       end
+                               }
+                       end
+               end
+
+               def dowrite m
+                       if @socket.closed? or @closed
+                               return
+                       end
+                       b = 0
+                       begin
+                               b += @socket.write(m)
+                       end while b < m.length
+               end
+
+               def event type, *args
+                       changed
+                       notify_observers type, *args
+               end
+               private :event
+
+               def close
+            # TODO: defer until flush maybe?
+            unless write_ready?
+                EventManager.instance.unwatch self
+                @socket.close
+            end
+            @closed = true
+        end
+               def closed?; @socket.closed?; end
+               def eof?; @socket.eof?; end
+
+               def write_ready?
+                       [EMAIL PROTECTED]
+               end
+
+               def to_io
+                       @socket
+               end
+       end
+end

Added: trunk/clients/ravish/haver.rb
===================================================================
--- trunk/clients/ravish/haver.rb       2005-06-26 04:37:26 UTC (rev 829)
+++ trunk/clients/ravish/haver.rb       2005-07-05 07:48:19 UTC (rev 830)
@@ -0,0 +1,61 @@
+require 'observer'
+require 'eventmanager'
+
+DEBUG = 2
+
+module Haver
+       class Client
+               include Observable
+
+               def initialize server, port=7070, hash={}
+                       @server = server.to_s
+                       @port = port.to_i
+                       @conf = hash.to_hash
+               end
+
+               def start
+                       event :connecting
+                       Thread.new {
+                               sock = TCPSocket.new(@server, @port)
+                               connect sock
+                       }
+               end
+
+               def connect sock
+                       begin
+                               @sock = Haver::Net.new sock, self
+                       rescue StandardError, SocketError => e
+                               event :link_failed, e
+                       end
+                       @sock.add_observer self
+                       event :connected
+                       msg 'HAVER', 'ravish/0.1.0'
+               end
+
+               def msg *args
+                       m = args.join("\t") + "\r\n"
+                       puts "C: #{m}" if DEBUG >= 2
+                       @sock.put m
+               end
+
+               def update type, *args
+                       if type == :data
+                               handle_line args[0]
+                       end
+               end
+
+               def handle_line line
+                       puts "S: #{line}" if DEBUG >= 2
+                       message = line.split(/\t/)
+                       cmd = message.shift
+                       event ("ev_" + cmd).to_sym, message
+               end
+
+               def event type, *args
+                       changed
+                       notify_observers type, *args
+               end
+       end
+end
+
+require 'haver/net'

Modified: trunk/clients/ravish/ravish.rb
===================================================================
--- trunk/clients/ravish/ravish.rb      2005-06-26 04:37:26 UTC (rev 829)
+++ trunk/clients/ravish/ravish.rb      2005-07-05 07:48:19 UTC (rev 830)
@@ -84,14 +84,6 @@
 $vt.bind('A-O', switch_window)
 
 module Haver
-       module Event
-               CONNECTING = 0
-               CONNECTED = 1
-               MESG_RECV = 2
-               DISCONNECTING = 3
-               DISCONNECTED = 4
-       end
-
        class Connector
                include Observable
 
@@ -119,11 +111,11 @@
                                return false
                        end
 
-                       notify Event::CONNECTING
+                       notify :CONNECTING
                        begin
                                @socket = TCPSocket.new(@server, @port)
                        rescue StandardError, SocketError
-                               notify Event::LINK_FAILED, $!
+                               notify :LINK_FAILED, $!
                                return false
                        end
                        @socket.nonblock = true
@@ -136,12 +128,12 @@
                                self.writetosocket
                        end
 
-                       notify Event::CONNECTED
+                       notify :CONNECTED
                        return true
                end
 
                def close
-                       notify Event::DISCONNECTING
+                       notify :DISCONNECTING
 
                        if [EMAIL PROTECTED] and @recvthread.alive?
                                @recvthread.exit
@@ -153,7 +145,7 @@
 
                        @socket.close unless @socket.closed?
 
-                       notify Event::DISCONNECTED
+                       notify :DISCONNECTED
                end
 
                def alive?
@@ -176,14 +168,14 @@
                end
 
                def read
-                       @recvqueue.pop
+                       r = @recvqueue.pop
                end
 
                protected
 
-               def notify event
+               def notify event, *args
                        changed
-                       notify_observers event, Time.now
+                       notify_observers event, Time.now, args
                end
 
                def readfromsocket
@@ -201,7 +193,7 @@
                                                        Thread.pass
                                                end
                                                
@recvqueue.push(buf.slice!(/^(.+?)\r\n/).chomp)
-                                               notify Event::MESG_RECV
+                                               notify :MESG_RECV
                                        end
                                rescue StandardError, SocketError, IOError
                                        break
@@ -223,6 +215,7 @@
                                                if res then sent += 
@socket.write msg end
                                                Thread.pass
                                        end until sent == msg.length
+                                       p msg
                                rescue StandardError, SocketError, IOError
                                        break
                                end
@@ -246,36 +239,42 @@
                        @conn.nick
                end
 
-               def update event, time
+               def update event, time, args
                        case event
-                       when Event::CONNECTING
+                       when :CONNECTING
                                if self.respond_to?(:on_connecting)
                                        self.on_connecting
                                end
-                       when Event::CONNECTED
+                       when :CONNECTED
                                if self.respond_to?(:on_connected)
                                        self.on_connected
                                end
-                       when Event::DISCONNECTING
+                       when :DISCONNECTING
                                if self.respond_to?(:on_disconnecting)
                                        self.on_disconnecting
                                end
-                       when Event::DISCONNECTED
+                       when :DISCONNECTED
                                if self.respond_to?(:on_disconnected)
                                        self.on_disconnected
                                end
-                       when Event::MESG_RECV
+                       when :LINK_FAILED
+                               if self.respond_to?(:on_link_failed)
+                                       self.on_link_failed args[0]
+                               end
+                       when :MESG_RECV
                                mesg = @conn.read
-                               args = mesg.split("\t")
-                               cmd = args.shift
-                               fun = ("on_cmd_" + cmd.downcase).to_sym
+                               margs = mesg.split("\t")
+                               mcmd = args.shift
+                               p margs
+                               fun = ("on_cmd_" + mcmd.downcase).to_sym
                                if self.respond_to?(fun)
-                                       self.send fun, *args
+                                       p "fun #{fun}"
+                                       self.send fun, *margs
                                elsif self.respond_to?(:on_unhandled_cmd)
-                                       self.send :on_unhandled_cmd, cmd, args
+                                       self.send :on_unhandled_cmd, mcmd, margs
                                end
                                if self.respond_to?(:on_mesg_recv)
-                                       self.send :on_mesg_recv, cmd, args
+                                       self.send :on_mesg_recv, mcmd, margs
                                end
                        end
                end
@@ -284,11 +283,9 @@
                        @conn.write ['HAVER', 'ravish/0.0.1']
                end
 
-               def on_cmd_want cmd
-                       case cmd
-                       when 'IDENT'
-                               @conn.write ['IDENT', @conn.nick]
-                       end
+               def on_cmd_haver *args
+                       p args
+                       @conn.write ['IDENT', @conn.nick]
                end
                
                def on_cmd_fail cmd, reason, *args
@@ -338,6 +335,10 @@
                super
        end
 
+       def on_link_failed e
+               $vt.message "Link failed: #{e}"
+       end
+
        def on_mesg_recv cmd, args
                $vt.message "S: #{cmd} #{args.join(' ')}"
        end
@@ -458,7 +459,7 @@
 
 # event loop!
 
-$haver = HaverConnection.new('hardison.net')
+$haver = HaverConnection.new('localhost')
 
 loop do
        cwin = $windows.find { |w| w[1] == $vt.current_window }

Added: trunk/clients/ravish/ravish2.rb
===================================================================
--- trunk/clients/ravish/ravish2.rb     2005-06-26 04:37:26 UTC (rev 829)
+++ trunk/clients/ravish/ravish2.rb     2005-07-05 07:48:19 UTC (rev 830)
@@ -0,0 +1,32 @@
+# ravish -- Haver client written in ruby. Second attempt.
+
+$:.unshift "/home/nornagon/dev/termvisual/lib"
+
+require 'term/visual'
+require 'haver'
+
+Thread.abort_on_exception = true
+
+class Ravish
+       def initialize server, port=7070
+               @term = Term::Visual.new
+               @haver = Haver::Client.new server
+               @haver.add_observer self
+               @haver.start
+       end
+       
+       def update type, *args
+               case type
+               when :connecting
+                       puts 'connecting'
+               when :connected
+                       puts 'connected'
+               when :ev_HAVER
+                       @haver.msg 'IDENT', 'nornagon'
+               end
+       end
+end
+
+ravish = Ravish.new 'hardison.net'
+
+EventManager.instance.run


Reply via email to