Forráskód Böngészése

Some basics implemented.

Beoran 9 éve
szülő
commit
ee8fadf822
5 módosított fájl, 101 hozzáadás és 215 törlés
  1. 1 1
      lib/monolog.rb
  2. 70 19
      lib/sitef.rb
  3. 5 3
      lib/woe/client.rb
  4. 0 182
      lib/woe/serdes.rb
  5. 25 10
      lib/woe/server.rb

+ 1 - 1
lib/monolog.rb

@@ -25,7 +25,7 @@ module Monolog
     end
     
     def log(file, line, name, format, *args)
-      @data.printf("%s: %s: %d: ", name, file, line)
+      @data.printf("%s: %s: %s: %d: ", Time.now.to_s, name, file, line)
       @data.printf(format, *args)
       @data.printf("\n")
     end

+ 70 - 19
lib/woe/sitef.rb → lib/sitef.rb

@@ -10,7 +10,7 @@ module Sitef
   # with a : and is followed by a : the value starts after the second :
   # A multiline key / value needs a key that starts with . and ends with .
   # the end of the value is a  pair of dots .. by itself 
-  # Keys may not be nested, however, une could use spaces or dots, 
+  # Keys may not be nested, however, you could use spaces or dots, 
   # or array indexes to emulate nexted keys. 
   # A # at the start optionally after whitespace is a comment
   # 
@@ -25,14 +25,13 @@ module Sitef
     until file.eof?
       lineno     += 1 
       line        = file.gets(256)
-      # XXX does eof? even work???
       break if line.nil?
       next if line.empty? 
       # new record
       if line[0,2] == '--' 
         # Store last key used if any.
-        if key
-          record[key.downcase] = value.chomp
+        if key          
+          record[key] = value.chomp
           key = nil
         end  
         results << record
@@ -41,19 +40,25 @@ module Sitef
       # Comments start with #
       elsif line[0] == ':'
       # a key/value pair
-      key, value = line[1,line.size].split(':', 2)
-      record[key.downcase] = value.chomp
+      key, value  = line[1,line.size].split(':', 2)
+      record[key] = value.chomp
       key = value = nil
       elsif line[0, 2] == '..'
       # end of multiline value 
-      record[key.downcase] = value.chomp
+      record[key] = value.chomp
       key = value = nil
       elsif (line[0] == '.') && key.nil?
       # Multiline key/value starts here (but is ignored 
       # until .. is encountered)
       key   = line[1, line.size]
+      key.chomp!
       value = ""
+      # multiline value
       elsif key
+          if line[0] == '\\'
+            # remove any escapes
+            line.slice!(0)
+          end
           # continue the value
           value << line
       else
@@ -62,8 +67,8 @@ module Sitef
       end      
     end
     # Store last key used if any.
-    if key
-      record[key.downcase] = value.chomp
+    if key      
+      record[key] = value.chomp
     end  
     # store last record 
     results << record unless record.empty?
@@ -71,7 +76,7 @@ module Sitef
   end  
   
   def self.load_filename(filename)
-    results , errors, warnings = nil, nil, nil;
+    results, errors = nil, nil, nil;
     file = File.open(filename, 'rt')
     return nil, ["Could not open #{filename}"] unless file
     begin 
@@ -82,36 +87,82 @@ module Sitef
     return results, errors
   end
   
+  # Loads a Sitef fileas obejcts. Uses the ruby_klass atribute to load the object
+  # If that is missing, uses defklass
+  def self.load_objects(filename, defklass=nil)
+    results, errors = load_filename(filename)
+    p filename, results, errors
+    unless errors.nil? || errors.empty?
+      return nil, errors 
+    end
+    
+    objres = [] 
+    results.each do | result |
+      klassname = result['ruby_class'] || defklass
+      return nil unless klassname
+      klass = klassname.split('::').inject(Kernel) { |klass, name| klass.const_get(name) rescue nil } 
+      return nil unless klass
+      if klass.respond_to? :from_sitef
+        objres << klass.from_sitef(result)
+      else
+        objres << klass.new(result)
+      end      
+    end
+    return objres, errors    
+  end
+  
+  
+  # Saves a single field to a file in Sitef format.
   def self.save_field(file, key, value)
-    sval = value.to_s
+    if value.is_a? String
+      sval = value.dup
+    else
+      sval = value.to_s
+    end
     if sval["\n"]
       file.puts(".#{key}\n")
-      file.puts(sval)
-      file.puts("\n..\n")
+      # Escape everything that could be misinterpreted with a \\
+      sval.gsub!(/\n([\.\-\:\#\\]+)/, "\n\\\\\\1")
+      sval.gsub!(/\A([\.\-\:\#\\]+)/, "\\\\\\1")
+      file.printf("%s", sval)
+      file.printf("\n..\n")
     else
-      file.puts(":#{key}:#{sval}\n")
+      file.printf(":#{key}:#{sval}\n")
+    end
+  end
+  
+  def self.save_object(file, object, *fields)
+    save_field(file, :ruby_class, object.class.to_s)
+    fields.each do | field |
+      value = object.send(field.to_sym)
+      save_field(file, field, value)
     end
   end
   
-  def self.save_record(file, record)
+  def self.save_record(file, record, *fields)
     record.each do | key, value |
+      next if fields && !fields.empty? && !fields.member?(key)
       save_field(file, key, value)
     end
   end
 
-  def self.save_file(file, records)
+  def self.save_file(file, records, *fields)
     records.each do | record |
-      save_record(file, record)
+      if record.is_a? Hash
+        save_record(file, record, *fields)
+      else 
+        save_object(file, record, *fields)
+      end
       file.puts("--\n")
     end
   end
   
-  def self.save_filename(filename, records)
+  def self.save_filename(filename, records, *fields)
     results , errors = nil, nil
     file = File.open(filename, 'wt')
     return false, ["Could not open #{filename}"] unless file
     begin 
-      save_file(file, records)
+      save_file(file, records, *fields)
     ensure
       file.close
     end

+ 5 - 3
lib/woe/client.rb

@@ -1,6 +1,7 @@
 require 'eventmachine'
 require 'tempfile'
 require 'fiber'
+require_relative '../monolog'
 
 
 module Woe
@@ -36,6 +37,8 @@ class Minifilter
 end
 
 class Client < EventMachine::Connection
+  include Monolog
+
   attr_accessor :id
   attr_accessor :server
   
@@ -57,6 +60,7 @@ class Client < EventMachine::Connection
     @port, @ip  = Socket.unpack_sockaddr_in(pn)
     send_data("You are connecting from #{@ip}:#{@port}\n")
     @connected  = true
+    log_info("Client #{@id} connected from #{@ip}:#{@port}")
     self.send_data("Login:")
   end
       
@@ -152,11 +156,9 @@ class Client < EventMachine::Connection
   
   
   
-  
   def unbind
-    $stderr.puts("Client #{id} has left")
+    log_info("Client #{@id} has left from #{@ip}:#{@port}")
     @server.disconnect(@id)
-    
   end
 end
 

+ 0 - 182
lib/woe/serdes.rb

@@ -1,182 +0,0 @@
-
-class Dir
-  def self.mkdir_p(name)
-    sub   = ""
-    parts = name.split('/').reject { |e| e.empty? }
-    parts.each do | part |
-      sub <<  "/#{part}"
-      mkdir sub
-    end
-  end
-end
-
-
-
-# Module to help with serialization and deserialization of any type of data
-module Serdes
-  
-  module ClassMethods
-    def serdes_add_to_fields(name, type = nil)
-      @serdes_fields ||= []
-      info = { :name => name, :type => type }
-      @serdes_fields << info
-    end
-    
-    def serdes_reader(name, type = nil)
-      serdes_add_to_fields(name, type)
-      attr_reader(name)
-    end
-    
-    def serdes_writer(name)
-      serdes_add_to_fields(name, type = nil)
-      attr_writer(name)
-    end
-    
-    def serdes_accessor(name)
-      serdes_add_to_fields(name, type)
-      attr_accessor(name)
-    end
-    
-    def serdes_fields()
-      @serdes_fields ||= []
-      return @serdes_fields
-    end
-    
-    
-    def serdes_register(obj)
-      @serdes_loaded ||= {}
-      @serdes_loaded[obj.id] = obj
-    end
-    
-    def serdes_forget(id)
-      @serdes_loaded ||= {}
-      @serdes_loaded.delete(id)
-    end
-    
-    def serdes_loaded()
-      @serdes_loaded ||= {}
-      return @serdes_loaded
-    end
-    
-    def serdes_get(id)
-      @serdes_loaded ||= {}
-      return @serdes_loaded[id.to_sym]
-    end
-
-    def serdes_load(id)
-      return nil unless id && !id.empty?
-      
-      full_name = Serdes.serdes_dir_name(self, id) + '/' + Serdes.serdes_file_name(self, id)
-      data, errors  = Sitef.load_filename(full_name)
-      unless data
-        log errors.join(", ")
-        return nil
-      end
-      
-      eldat = data.select do |el|
-        el['id'].to_s == id.to_s
-      end
-      return nil unless eldat
-
-      eldat = eldat.first
-      return nil unless eldat
-      
-      typedat = {}
-      self.serdes_fields.each do |info|
-        name  = info[:name]
-        type  = info[:type]
-        value = eldat[name.to_s]
-        
-        typevalue = nil
-        
-        if type.respond_to?(:serdes_load)
-          typevalue = type.serdes_load(value)
-        elsif type && Kernel.respond_to?(type.to_sym)
-          typevalue = Kernel.send(type.to_sym, value) rescue nil 
-        else
-          typevalue = value
-        end
-      
-        typedat[name] = typevalue
-      end
-      
-      obj = self.new(typedat)
-      return obj
-    end
-    
-    def serdes_fetch(id)
-      res = serdes_get(id)
-      return res if res
-      return serdes_load(id)
-    end
-    
-    alias :fetch :serdes_fetch
-    alias :load  :serdes_load
-    alias :get   :serdes_get
-    
-    def from_serdes(id)
-      return serdes_fetch(id)
-    end
-    
-    def to_serdes(value)
-      return value.id.to_s
-    end  
-  end
-
-  # include callback, be sure to extend the class with the ClassMethods
-  def self.included(klass)
-    klass.extend(ClassMethods)
-  end
-  
-  def self.serdes_dir_name(klass, id)
-    top = klass.to_s.gsub('::', '/').downcase
-    top << '/' 
-    top << id.to_s[0]
-    top << '/' 
-    top << id.to_s    
-    return top
-  end
-  
-  def self.serdes_file_name(klass, id)
-    top = id.to_s.dup    
-    top << '.'
-    top << klass.to_s.gsub('::', '.').downcase
-    return top 
-  end
-
-  def serdes_data
-    data = {}
-    self.class.serdes_fields.each do |info|
-      name  = info[:name]
-      type  = info[:type]
-      type||= String
-      key   = "#{name}" 
-      value = "#{self.send(name.to_sym)}"
-      if type.respond_to?(:to_serdes)
-         wrapvalue = type.to_serdes(value)
-      else 
-         wrapvalue = value.to_s
-      end
-      data[key]    = wrapvalue
-    end
-    return data
-  end
-  
-  def save
-    Dir.mkdir_p Serdes.serdes_dir_name(self.class, self.id)
-    data = serdes_data
-    full_name = Serdes.serdes_dir_name(self.class, self.id) + 
-               '/' + Serdes.serdes_file_name(self.class, self.id)
-    Sitef.save_filename(full_name, [ data ] )
-  end
-  
-  def initialize(fields = {}) 
-    fields.each  do |key, value|
-      p "Setting #{key} #{value}"
-      instance_variable_set("@#{key}", value)
-    end
-    self.class.serdes_register(self)
-  end
-
-end
-

+ 25 - 10
lib/woe/server.rb

@@ -1,11 +1,15 @@
 require 'eventmachine'
 require 'tempfile'
 require 'fiber'
+require_relative '../monolog'
 
 
 module Woe
   class Server 
-    def initialize(port =7000)
+    include Monolog
+  
+    def initialize(port =7000, logname="woe.log")
+      Monolog.setup_all(logname)
       @port      = port
       # Keep an overall record of the client IDs allocated
       # and the lines of chat
@@ -15,7 +19,8 @@ module Woe
       @fiber     = nil
     end
     
-    def start()    
+    def start() 
+      log_info("Server listening on port #@port")
       @signature = EventMachine.start_server("0.0.0.0", @port, Client) do |client|
         @client_id          += 1
         client.id            = @client_id
@@ -28,33 +33,43 @@ module Woe
       end  
     end
     
+    
     def run
+      log_info("Server main loop starts.")
       EventMachine.run do       
         self.start
-      end  
+      end
     end
     
     
     def disconnect(id)
+      log_info("Server disconnecting client #{@id}")
       @clients.delete(id)
     end
     
-    def clients_stopped?    
+    def clients_stopped?
     end
     
     def reload
-      broadcast("Reloading\n")
+      log_info("Server reload")
+      broadcast("Server reload NOW!\n")
       begin 
         load 'lib/woe/server.rb'
-        broadcast("Reloaded\n")
+        broadcast("Server reloaded OK.\n")
       rescue Exception => ex
-        broadcast("Exception #{ex}: #{ex.backtrace.join("\n")}!\n")
+        bt = ex.backtrace.join("\n")
+        log_error("Server reload failed: #{ex}: #{bt}")
+        broadcast("Server reload exception #{ex}: #{bt}!\n")
       end
     end
     
     def stop
+      log_info("Server stop")
       EventMachine.stop_server(@signature)
-      EventMachine.add_timer(1) { EventMachine.stop }
+      EventMachine.add_timer(1) do 
+        EventMachine.stop
+        log_info("Server stop OK.")
+      end
     end
     
    
@@ -65,8 +80,8 @@ module Woe
     end
     
 
-    def self.run(port=7000)    
-      server = Woe::Server.new(port)
+    def self.run(port=7000, logname="woe.log")    
+      server = Woe::Server.new(port, logname)
       server.run
     end