commit 7e9520c0a06d4516ffef0594c6e2a2a8b708edbc
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Fri Nov 5 00:33:10 2010 +0100

    [evol] conversation/bot protocol rework §7 (refs #30)

diff --git a/lib/cyborghood/cyborg/conversation.rb b/lib/cyborghood/cyborg/conversation.rb
index d75be37..8e01b03 100644
--- a/lib/cyborghood/cyborg/conversation.rb
+++ b/lib/cyborghood/cyborg/conversation.rb
@@ -18,178 +18,10 @@
 
 require 'yaml'
 require 'cyborghood/cyborg/session'
+require 'cyborghood/cyborg/protocol'
 
-# Note: Event machine core is mono-thread, so this code is not threadsafe.
-#       Only long tasks will be run in threads, which do not care about all this.
 
 module CyborgHood
-
-  module BotProtocol
-    VERSION = "0.1"
-    CAPABILITIES = []
-
-    @@request_callback = proc{|result| process_request_result(result) }
-
-    # TODO:
-    #   - check for request/reply couples (reply to wrong of non-existent request)
-    #   - check for negociation wip/done
-
-    def initialize(conversation)
-      @conversation = conversation
-
-      @negociation_received = false
-      @negociation_sent = false
-      @negociation_ok = false
-    end
-
-    def negociation_ok?
-      @negociation_ok
-    end
-
-    def process_received_message(message)
-      method = "receive_" + message.action_code.downcase.tr(" ", "_")
-      if respond_to? method
-        send(method, message)
-      else
-        send_error_protocol "unknown action"
-      end
-    end
-
-    def process_request_result(result)
-      if result[:error]
-        send_error_action(result[:reply_message], result[:error])
-      else
-        send_reply_result(result[:reply_message], result[:action_result])
-      end
-    end
-
-    def receive_announce_helo(message)
-      unless message.conv_thread.id == 0
-        return send_quit_decline "bad negociation"
-      end
-      unless message.parameters[:bot_name] =~ Conversation::BOT_ID_PATTERN
-        return send_quit_decline "bad bot name"
-      end
-      unless message.parameters[:protocol_version] == VERSION
-        return send_quit_decline "protocol version does not match"
-      end
-      @negociation_received = true
-      @conversation.set_peer_info(message.parameters[:bot_name], message.parameters[:capabilities])
-
-      if @negociation_sent
-        send_announce_ok(message)
-        @negociation_ok = true
-      else
-        send_announce_helo(message)
-        @negociation_sent = true
-      end
-    end
-
-    def receive_announce_ok(message)
-      unless @negociation_sent and @negociation_received
-        send_quit_decline "bad negociation"
-      end
-      @negociation_ok = true
-    end
-
-    def receive_request_capabilities(message)
-      send_reply_result(message, @conversation.capabilities + CAPABILITIES)
-    end
-
-    def receive_request_call(message)
-      unless @conversation.bot.interface.is_node? message.parameters[:node]
-        return send_error_action(message, "bad node")
-      end
-      send_reply_ack(message)
-      @conversation.bot.schedule_task(@@request_callback) do
-        result = {
-          :reply_message => message
-        }
-        begin
-          result[:action_result] = @conversation.bot.interface.call(message.conv_thread.session,
-                                                                    message.parameters[:node],
-                                                                    message.parameters[:data])
-        rescue
-          result[:error] = $!
-        end
-      end
-    end
-
-    def receive_request_exists(message)
-      unless @conversation.bot.interface.is_node? message.parameters[:node]
-        return send_error_action(message, "bad node")
-      end
-      send_reply_ack(message)
-      @conversation.bot.schedule_task(@@request_callback) do
-        {
-          :reply_message => message,
-          :action_result => @conversation.bot.interface.has_node? message.parameters[:node]
-        }
-      end
-    end
-
-    def receive_request_describe(message)
-      # TODO: implement when ready in the interface
-      send_quit_decline(message, "not implemented")
-    end
-
-    def send_announce_helo(recv_message = nil)
-      action_code = "ANNOUNCE HELO"
-      message = (recv_message.nil? ? @conversation.thread('system').new_message(action_code) :
-                 recv_message.create_reply(action_code))
-      message.send
-    end
-
-    def send_announce_ok(recv_message)
-      recv_message.create_reply("ANNOUNCE OK").send
-    end
-
-    def send_request_capabilities
-      @conversation.thread('system').new_message("REQUEST EXISTS", { :node => node }).send
-    end
-
-    def send_request_call(conv_thread, node)
-      conv_thread.new_message("REQUEST CALL", { :node => node }).send
-    end
-
-    def send_request_exists(conv_thread, node)
-      conv_thread.new_message("REQUEST EXISTS", { :node => node }).send
-    end
-
-    def send_request_describe(conv_thread, node)
-      conv_thread.new_message("REQUEST DESCRIBE", { :node => node }).send
-    end
-
-    def send_error_protocol(error, fatal = false)
-      @conversation.thread('system').new_message("ERROR PROTOCOL", { :error => error }).send
-      @conversation.set_error_status(fatal)
-    end
-
-    def send_error_action(recv_message, error)
-      recv_message.create_reply("ERROR ACTION", { :error => error }).send
-    end
-
-    def send_reply_ack(recv_message)
-      recv_message.create_reply("REPLY ACK").send
-    end
-
-    def send_reply_decline(recv_message, reason)
-      recv_message.create_reply("REPLY DECLINE", { :reason => reason }).send
-    end
-
-    def send_reply_result(recv_message, result)
-      recv_message.create_reply("REPLY ACK", { :result => result }).send
-    end
-
-    def send_quit_decline(reason)
-      @conversation.thread('system').new_message("QUIT LEAVING", { :reason => reason }).send
-    end
-
-    def send_quit_leaving
-      @conversation.thread('system').new_message("QUIT LEAVING").send
-    end
-  end
-
   class ConversationThread
     attr_reader :conversation, :name, :id, :session
 
diff --git a/lib/cyborghood/cyborg/protocol.rb b/lib/cyborghood/cyborg/protocol.rb
new file mode 100644
index 0000000..3ed7155
--- /dev/null
+++ b/lib/cyborghood/cyborg/protocol.rb
@@ -0,0 +1,188 @@
+#--
+# CyborgHood, a distributed system management software.
+# Copyright (c) 2009-2010 Marc Dequènes (Duck) <Duck@DuckCorp.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#++
+
+# Note: Event machine core is mono-thread, so this code is not threadsafe.
+#       Only long tasks will be run in threads, which do not care about all this.
+
+
+module CyborgHood
+  module BotProtocol
+    VERSION = "0.1"
+    CAPABILITIES = []
+
+    @@request_callback = proc{|result| process_request_result(result) }
+
+    # TODO:
+    #   - check for request/reply couples (reply to wrong of non-existent request)
+
+    def initialize(conversation)
+      @conversation = conversation
+
+      @negociation_received = false
+      @negociation_sent = false
+      @negociation_ok = false
+    end
+
+    def self.process_request_result(result)
+      if result[:error]
+        send_error_action(result[:reply_message], result[:error])
+      else
+        send_reply_result(result[:reply_message], result[:action_result])
+      end
+    end
+
+    def negociation_ok?
+      @negociation_ok
+    end
+
+    def process_received_message(message)
+      method = "receive_" + message.action_code.downcase.tr(" ", "_")
+      if respond_to? method
+        send(method, message)
+      else
+        send_error_protocol "unknown action"
+      end
+    end
+
+    def receive_announce_helo(message)
+      unless message.conv_thread.id == 0
+        return send_quit_decline "bad negociation"
+      end
+      unless message.parameters[:bot_name] =~ Conversation::BOT_ID_PATTERN
+        return send_quit_decline "bad bot name"
+      end
+      unless message.parameters[:protocol_version] == VERSION
+        return send_quit_decline "protocol version does not match"
+      end
+      @negociation_received = true
+      @conversation.set_peer_info(message.parameters[:bot_name], message.parameters[:capabilities])
+
+      if @negociation_sent
+        send_announce_ok(message)
+        @negociation_ok = true
+      else
+        send_announce_helo(message)
+        @negociation_sent = true
+      end
+    end
+
+    def receive_announce_ok(message)
+      unless @negociation_sent and @negociation_received
+        send_quit_decline "bad negociation"
+      end
+      @negociation_ok = true
+    end
+
+    def receive_request_capabilities(message)
+      send_reply_result(message, @conversation.capabilities + CAPABILITIES)
+    end
+
+    def receive_request_call(message)
+      unless @conversation.bot.interface.is_node? message.parameters[:node]
+        return send_error_action(message, "bad node")
+      end
+      send_reply_ack(message)
+      @conversation.bot.schedule_task(@@request_callback) do
+        result = {
+          :reply_message => message
+        }
+        begin
+          result[:action_result] = @conversation.bot.interface.call(message.conv_thread.session,
+                                                                    message.parameters[:node],
+                                                                    message.parameters[:data])
+        rescue
+          result[:error] = $!
+        end
+      end
+    end
+
+    def receive_request_exists(message)
+      unless @conversation.bot.interface.is_node? message.parameters[:node]
+        return send_error_action(message, "bad node")
+      end
+      send_reply_ack(message)
+      @conversation.bot.schedule_task(@@request_callback) do
+        {
+          :reply_message => message,
+          :action_result => @conversation.bot.interface.has_node? message.parameters[:node]
+        }
+      end
+    end
+
+    def receive_request_describe(message)
+      # TODO: implement when ready in the interface
+      send_quit_decline(message, "not implemented")
+    end
+
+    def send_announce_helo(recv_message = nil)
+      action_code = "ANNOUNCE HELO"
+      message = (recv_message.nil? ? @conversation.thread('system').new_message(action_code) :
+                 recv_message.create_reply(action_code))
+      message.send
+    end
+
+    def send_announce_ok(recv_message)
+      recv_message.create_reply("ANNOUNCE OK").send
+    end
+
+    def send_request_capabilities
+      @conversation.thread('system').new_message("REQUEST EXISTS", { :node => node }).send
+    end
+
+    def send_request_call(conv_thread, node)
+      conv_thread.new_message("REQUEST CALL", { :node => node }).send
+    end
+
+    def send_request_exists(conv_thread, node)
+      conv_thread.new_message("REQUEST EXISTS", { :node => node }).send
+    end
+
+    def send_request_describe(conv_thread, node)
+      conv_thread.new_message("REQUEST DESCRIBE", { :node => node }).send
+    end
+
+    def send_error_protocol(error, fatal = false)
+      @conversation.thread('system').new_message("ERROR PROTOCOL", { :error => error }).send
+      @conversation.set_error_status(fatal)
+    end
+
+    def send_error_action(recv_message, error)
+      recv_message.create_reply("ERROR ACTION", { :error => error }).send
+    end
+
+    def send_reply_ack(recv_message)
+      recv_message.create_reply("REPLY ACK").send
+    end
+
+    def send_reply_decline(recv_message, reason)
+      recv_message.create_reply("REPLY DECLINE", { :reason => reason }).send
+    end
+
+    def send_reply_result(recv_message, result)
+      recv_message.create_reply("REPLY ACK", { :result => result }).send
+    end
+
+    def send_quit_decline(reason)
+      @conversation.thread('system').new_message("QUIT LEAVING", { :reason => reason }).send
+    end
+
+    def send_quit_leaving
+      @conversation.thread('system').new_message("QUIT LEAVING").send
+    end
+  end
+end
