From: "Firestone, Mark - Technical Support" Date: 2002-03-14T23:46:06+09:00 Subject: RE: Newbie -- Please help!! Well. That puts me back in the same boat I was before. I have started to put the peice of code into the main telnet app (which I am rapidly turning into a total mess anyway...) the problem is, it still is making the user data into a LOCAL object... so, it isn't accessable from any other place. If I make it a global object... then I violate the multi-threading rules. The problem is, none of the resources I have found (except you kind people) tell you how to actually correctly structure things. see the following code (which is bad, I know...) Right now...my the object is only accessable from whatever one def at a time. Am I missing something? Mark #!/usr/bin/ruby # This version has mixin classes: TelnetIO and ConsoleIO, # which are used to create local and network versions of the # Session class. #CONST BS = 8 CR = 13 LF = 10 NOECHOCHAR = 36 PRINTABLE = 32..126 def pick_one(sequence) sequence[rand(sequence.length)] end module ConsoleIO def hangup print "NO CARRIER\n" end # Write line without CR def write(x) print x STDOUT.flush end # Write line with CR def putstr(*lines) if lines.length == 0 print "\n" else lines.each { |line| print line print "\n" } end end def getstr gets end end module TelnetIO # hangup def hangup @socket.flush @socket.close end # Write line without CR def write(line) @socket.write line end # Write line with CR def putstr(*lines) crlf = "\r\n" if lines.length == 0 @socket.write crlf else lines.each { |line| @socket.write line @socket.write crlf } end end def getstr(echo) # Input a line a character at a time. whole = "" fred = "" @socket.each_byte do | fred| case fred when 255 puts "Command Mode Request" when PRINTABLE whole = whole + fred.chr if echo @socket.write fred.chr else @socket.write NOECHOCHAR.chr end when CR @socket.write CR.chr+LF.chr break when BS whole.chop! #the user pressed backspace, so get rid @socket.write fred.chr end end @socket.flush getstr = whole end end class User def initialize(name,phone,citystate,address,password) @deleted = FALSE @locked = TRUE @name = name @alais = '' @alaisOn = FALSE @phone = phone @citystate = citystate @address = address @password = password @width = 80 end attr_reader :name, :password # short and neat def show print "User: #@name #@phone #@citystate #@address #@password\n" end end class User_list include Enumerable def initialize @users = Array.new end def append(aUser) @users.push(aUser) self end def [](key) if key.kind_of?(Integer) result = @users[key] else result = @users.find { |user| key == user.name } end return result end ## end #of Class User_list ## class method of User_list, so we don't need instance. def self.loadusers list = nil File.open("users.dat") do |f| list = Marshal.load(f) ## print "- Loading User Object...\n" end return list ## end # save list def saveusers File.open("users.dat", "w+") do |f| Marshal.dump( self, f) ## end print "- Saving User Object...\n" end def checkpassword (username,password) result = FALSE if self[username] != nil ## if self[username].password == password ## result = TRUE end else print "\n-Bad username passed to def:checkpassword. Please tell sysop!\n" end return result end ## new end... end #of Class User_list class Session require 'thread' @@busy = Mutex.new # Perhaps we'll find another way to pass the socket. # For now, a default value of "nil" will make it OK # for ConsoleIO. def initialize(socket = nil) @socket = socket end def login if File.exists?("users.dat") ##this shouldn't be here. this is a stupid place for it. blarg! list = User_list.loadusers else list = User_list.new list.append(User.new('SYSOP','000-000-0000','Tempe, AZ','600 E. Solana Drive','STUPID')) list.append(User.new('TEST','000-000-0000','Mesa, AZ','123 Sample Street','HAPPY')) print "- User File not Found. Creating new User File\n" end write 255.chr #this seems to stop telnet clients that want to echo write 254.chr #from echoing. move this to it's own def later write 1.chr putstr putstr "TOPquark 1.2r (c) 1993 by TOPsoftware" putstr IO.foreach("welcome1.txt") { |line| putstr line } write "Enter your name: " userName = getstr(true).strip.upcase if list[userName] != nil write "Password: " password = getstr(false).strip.upcase if list.checkpassword(userName,password) == TRUE done = TRUE end else putstr "User not found" end putstr putstr "Congratulations. You have been granted the privilege" putstr "of entry into the system by Microsoft's* certified," putstr "better-than-yours-because-we-said-so security." putstr putstr "* Microsoft(r) is a registered trademark of a company" putstr "with way more lawyers than me." putstr end def readmsgs users = ["BIG BEN", "SHO KOSUGI", "GLENN", "CAPTAIN CRUNCH", "PEE-WEE HERMAN", "SUE WIDEMARK", "TINA"] subjects = ["Ev Mecham", "Atari Roolz!", "Call my board!", "Please don't delete me!", "Where's the file board?"] putstr "From: " + pick_one(users) putstr "To: ALL" putstr "Subj: " + pick_one(subjects) putstr putstr "Hello, world!" putstr putstr "END OF MESSAGES" end def commandLoop putstr "Q=Quit E=Error R=Read Messages" while true write "Command? " case getstr(true).strip when "q" break when "e" raise "The sky is falling!" when "r" readmsgs else putstr "Nope, can't do that" end end end def doSession if @@busy.try_lock begin login commandLoop putstr "Bye" ensure @@busy.unlock end else putstr "BUSY" sleep 0.1 end end def run begin doSession rescue Exception oops = "Oops: #{$!}" puts oops, $@ putstr oops ensure hangup end end end # Create net session class that adds TelnetIO's methods to Session. class TelnetSession 1) This thing will eventually be multiuser -- so more than one thread will > have to be able to access those objects. How can I plan for this now -- so > I don't have to "pay" later? 1. If you're going to write multi-thread application you MUST, yes MUST! forget about global objects. This is a good practice in general and it is obligatory in multi-threding. 2. All methods that do not need to change object, must not do that, let's call them const methods. 3. If you have common resource used from multiple threads, those threads must call only const methods. BR, Sergei NOTICE: This e-mail and any attachment(s) may contain confidential and proprietary information of Goss International Corporation and/or its subsidiaries and may be legally privileged. This e-mail is intended solely for the addressee. If you are not the addressee, dissemination, copying or other use of this e-mail or any of its content is strictly prohibited and may be unlawful. If you are not the intended recipient please inform the sender immediately and destroy the e-mail and any copies. All liability for viruses is excluded to the fullest extent permitted by law. Any views expressed in this message are those of the individual sender. No contract may be construed by this e-mail.