commit 050eca4311568af6c8919040c5ea547f99bf6f41
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sat Aug 14 05:45:59 2010 +0200

    [evol] first raw cyborg API exposure implementation for MapMaker (refs #31)

diff --git a/bin/mapmaker b/bin/mapmaker
index 2309070..60e0f00 100755
--- a/bin/mapmaker
+++ b/bin/mapmaker
@@ -44,6 +44,16 @@ module CyborgHood
     bindtextdomain("cyborghood_mapmaker", {:path => Config::L10N_DIR, :charset => "UTF-8"})
 
     class MapMaker < Cyborg
+      include SimpleServer
+
+      def interface
+        MapMakerInterface
+      end
+    end
+
+    class MapMakerInterface < CyborgServerDefaultInterface
+      class DNS < CyborgServerInterface
+      end
     end
   end
 end
diff --git a/lib/cyborghood/cyborg.rb b/lib/cyborghood/cyborg.rb
index e2b6786..bb9a696 100644
--- a/lib/cyborghood/cyborg.rb
+++ b/lib/cyborghood/cyborg.rb
@@ -43,19 +43,110 @@ module CyborgHood
         end
       end
 
-      @server = BotServer.build
+      setup
 
       logger.info "Bot '#{self.human_name}' loaded"
     end
 
+    def setup
+    end
+
     def run
       logger.info "Bot starting"
+    end
+
+    def ask_to_stop
+      logger.info "Bot was asked to stop..."
+    end
+  end
+
+  module SimpleServer
+    def setup
+      super
+      @server = BotServer.build(self.interface)
+    end
+
+    def run
+      super
       @server.run
     end
 
     def ask_to_stop
       @server.stop
-      logger.info "Bot was asked to stop..."
+      super
+    end
+
+    def interface
+      CyborgServerInterface
+    end
+  end
+
+  class CyborgServerInterface
+    include Singleton
+
+    def initialize
+      @config = Config.instance
+    end
+
+    def api_modules
+      @modules ||= self.class.constants.select do |c|
+        cc = self.class.const_get(c)
+        cc.class == Class and cc.ancestors.include? CyborgServerInterface
+      end
+    end
+
+    def api_methods
+      @methods ||= self.methods.collect{|m| $1 if m =~ /^api_(.*)$/ }.compact
+    end
+
+    # preliminary incoming message handling
+    def call(cmd)
+      object, method = cmd.split('.', 2)
+
+      if method
+        begin
+          klass = object.split('::').inject(self.class) {|scope, const_name| scope.const_get(const_name)}
+        rescue
+          return "551 unknown object"
+        end
+      else
+        klass = self.class
+        method = cmd
+      end
+
+      inst = klass.instance
+      real_method = "api_#{method}"
+
+      unless inst.api_methods.include?(method)
+        return "551 this object has no such method"
+      end
+
+      begin
+        # preliminary outgoing message handling
+        return inst.send(real_method).to_yaml
+      rescue
+        return "550 method call failed"
+      end
+    end
+  end
+
+  class CyborgServerDefaultInterface < CyborgServerInterface
+    API_VERSION = "0.1~"
+
+    def api_product
+      PRODUCT
+    end
+
+    def api_version
+      VERSION
+    end
+
+    def api_api_version
+      API_VERSION
+    end
+
+    def api_bot_name
+      @config.bot_name
     end
   end
 end
diff --git a/lib/cyborghood/cyborg/server.rb b/lib/cyborghood/cyborg/server.rb
index 5c02e43..9c179f3 100644
--- a/lib/cyborghood/cyborg/server.rb
+++ b/lib/cyborghood/cyborg/server.rb
@@ -23,16 +23,18 @@ module CyborgHood
   class BotServer
     private_class_method :new
 
-    def self.build
+    def self.build(interface)
       case Config.instance.botnet.connection_type
       when 'unix_socket'
-        return BotServerUNIXSocket.new
+        return BotServerUNIXSocket.new(interface)
       else
         raise CyberError.new(:unrecoverable, "config", "Unknown botnet connection type")
       end
     end
 
-    def initialize
+    def initialize(interface)
+      @interface = interface
+
       @config = Config.instance
     end
 
@@ -50,8 +52,8 @@ module CyborgHood
   class BotServerUNIXSocket < BotServer
     public_class_method :new
 
-    def initialize
-      super
+    def initialize(interface)
+      super(interface)
 
       @socket = File.join(Config::RUN_DIR, @config.bot_name.downcase + ".sock")
       at_exit { remove_socket_file }
@@ -59,7 +61,7 @@ module CyborgHood
 
     def run
       super do
-        EventMachine.start_unix_domain_server(@socket, ConversationUNIXSocket)
+        EventMachine.start_unix_domain_server(@socket, ConversationUNIXSocket, @interface)
       end
     end
 
@@ -73,7 +75,9 @@ module CyborgHood
   class Conversation < EventMachine::Protocols::LineAndTextProtocol
     private_class_method :new
 
-    def initialize(*args)
+    def initialize(interface)
+      @interface = interface
+
       super
 
       @config = Config.instance
@@ -90,6 +94,7 @@ module CyborgHood
 
     def receive_line(data)
       logger.debug "Received [#{identifier}]: #{data}"
+      send_line @interface.instance.call(data)
     end
 
     def receive_error(msg)
