commit feb16bd04a7ec187c0b34b06c5680c51197af84f
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Sat Mar 5 23:00:40 2011 +0100

    [evol] Cyborg/Interface: support returning API call replies asynchronously

diff --git a/IDEAS b/IDEAS
index 52c5061..002a2e7 100644
--- a/IDEAS
+++ b/IDEAS
@@ -25,6 +25,9 @@ all bots:
       ordered_list.empty? ? nil : ordered_list.first.range
     end
   - leaf node feature: declare a node as leaf and pass all other node elements as parameters
+  - list node detection: when no on_request is specified, list of children nodes are given,
+    back, which could be very useful for auto-completion on the future CLI interface, but
+    we need to know when a real action is to be run, to avoid completion triggering them
 
 Clerk:
   - API:
@@ -107,6 +110,8 @@ Threads:
     + I18n parameters (list of translation prefs)
 
 Tasks:
+  - default error handler to avoid repeating on_error blocks with the
+    same code
   - inheritance: thread data (auth token, I18n prefs…)
   - wait notif on multiple channels:
     by registering the same callback on multiple channels,
diff --git a/lib/cyborghood-librarian/interface/0_base.rb b/lib/cyborghood-librarian/interface/0_base.rb
index da00e77..acf0f4c 100644
--- a/lib/cyborghood-librarian/interface/0_base.rb
+++ b/lib/cyborghood-librarian/interface/0_base.rb
@@ -13,6 +13,7 @@ node 'Records' do
 
     on_request do |request|
       request.reply.results = object_list.call
+      request.send_reply
     end
 
     attr_search_node do |criterias|
@@ -25,6 +26,7 @@ node 'Records' do
     node object_list do
       on_request do |request|
         request.reply.results = object_list.call(node_name, :format => :data_hash)
+        request.send_reply
       end
     end
   end
diff --git a/lib/cyborghood-mapmaker/interface/_zone/0_base.rb b/lib/cyborghood-mapmaker/interface/_zone/0_base.rb
index bca9f76..f4e0ae5 100644
--- a/lib/cyborghood-mapmaker/interface/_zone/0_base.rb
+++ b/lib/cyborghood-mapmaker/interface/_zone/0_base.rb
@@ -8,6 +8,7 @@ end
 node 'content' do
   on_request do |request|
     request.reply.results = {:content => zone.content}
+    request.send_reply
   end
 end
 
@@ -15,14 +16,15 @@ node 'content=' do
   on_request do |request|
     content = request.args.shift
     if content.empty?
-      request.errors << "Zone content missing"
-      return
+      request.reply.errors << "Zone content missing"
+    else
+      begin
+        zone.content = content
+      rescue CyberError => e
+        request.reply.errors << e.message
+      end
     end
 
-    begin
-      zone.content = content
-    rescue CyberError => e
-      request.reply.errors << e.message
-    end
+    request.send_reply
   end
 end
diff --git a/lib/cyborghood-mapmaker/interface/services/dns.rb b/lib/cyborghood-mapmaker/interface/services/dns.rb
index 30ddbca..36e5c30 100644
--- a/lib/cyborghood-mapmaker/interface/services/dns.rb
+++ b/lib/cyborghood-mapmaker/interface/services/dns.rb
@@ -1,11 +1,13 @@
 node 'DNS' do
   on_request do |request|
     request.reply.results = store.dns.info
+    request.send_reply
   end
 
   node 'check_config' do
     on_request do |request|
       request.reply.results = store.dns.check_config
+      request.send_reply
     end
   end
 end
diff --git a/lib/cyborghood-mapmaker/interface/services/dnssec.rb b/lib/cyborghood-mapmaker/interface/services/dnssec.rb
index 8ec89ca..13fce72 100644
--- a/lib/cyborghood-mapmaker/interface/services/dnssec.rb
+++ b/lib/cyborghood-mapmaker/interface/services/dnssec.rb
@@ -1,5 +1,6 @@
 node 'DNSSEC' do
   on_request do |request|
     request.reply.results = bot.services.dnssec.info
+    request.send_reply
   end
 end
diff --git a/lib/cyborghood/cyborg/botnet.rb b/lib/cyborghood/cyborg/botnet.rb
index 58f2d27..6cde0da 100644
--- a/lib/cyborghood/cyborg/botnet.rb
+++ b/lib/cyborghood/cyborg/botnet.rb
@@ -59,6 +59,7 @@ module CyborgHood
                 :core_api_version    => CORE_API_VERSION,
                 :cyborg_api_version  => version
               }
+              request.send_reply
             end
           end
         end
diff --git a/lib/cyborghood/cyborg/botnet/interface.rb b/lib/cyborghood/cyborg/botnet/interface.rb
index d8eb937..85e60bb 100644
--- a/lib/cyborghood/cyborg/botnet/interface.rb
+++ b/lib/cyborghood/cyborg/botnet/interface.rb
@@ -110,6 +110,7 @@ module CyborgHood
               request.reply.results = list.nil? ? search_among_children(request, lookup_node) :
                 search_in_list(request, lookup_node, list)
             end
+            request.send_reply
           end
         end
       end
@@ -123,7 +124,7 @@ module CyborgHood
         not node.nil?
       end
 
-      def _call(session, node_path, args = nil)
+      def _call(session, node_path, args = nil, &send_result_cb)
         args ||= []
         raise CyberError.new(:unrecoverable, 'api/cyborghood', "wrong format for arguments when calling node '#{node_path}'") unless args.is_a? Array
 
@@ -131,7 +132,7 @@ module CyborgHood
         raise CyberError.new(:unrecoverable, 'api/cyborghood', "unknown node '#{node_path}'") if node.nil?
 
         logger.debug "[Server API] Node '#{node_path}' found"
-        node.__send__(:request, session, args)
+        node.__send__(:request, session, args, &send_result_cb)
       end
 
       protected
@@ -207,9 +208,10 @@ module CyborgHood
       class Request
         attr_reader :session, :args, :reply
 
-        def initialize(session, args = [])
+        def initialize(session, args = [], &send_result_cb)
           @session = session
           @args = args
+          @send_result_cb = send_result_cb
 
           @reply = {
             :results   => {},
@@ -218,22 +220,26 @@ module CyborgHood
             :errors    => []
           }.to_ostruct
         end
+
+        def send_reply
+          @send_result_cb.call @reply.to_hash
+        end
       end
 
-      def request(session, args = [])
+      def request(session, args = [], &send_result_cb)
+        request = Request.new(session, args, &send_result_cb)
+
         if @request_cb
-          request = Request.new(session, args)
           begin
             @request_cb.call(request)
-            # TODO: full reply needed
-            request.reply.results
           rescue
             logger.debug "node request error message: " + $!
             logger.debug "node request error backtrace: " + $!.backtrace.join("\n")
             raise CyberError.new(:unrecoverable, 'api/cyborghood', "call failed on node '#{node_path}': " + $!)
           end
         else
-          visible_nodes_names
+          request.reply.results = visible_nodes_names
+          request.send_reply
         end
       end
 
diff --git a/lib/cyborghood/cyborg/botnet/protocol.rb b/lib/cyborghood/cyborg/botnet/protocol.rb
index b0ffd76..abf0408 100644
--- a/lib/cyborghood/cyborg/botnet/protocol.rb
+++ b/lib/cyborghood/cyborg/botnet/protocol.rb
@@ -52,10 +52,11 @@ module CyborgHood
     end
 
     def process_request_result(result)
+      # TODO: better processing of result (info/warnings/…)
       if result[:error]
-        send_error_action(result[:reply_message], result[:error])
+        send_error_action(result[:reply_message], result[:errors])
       else
-        send_reply_result(result[:reply_message], result[:action_result])
+        send_reply_result(result[:reply_message], result[:results])
       end
     end
 
@@ -103,30 +104,34 @@ module CyborgHood
                                    :message   => "missing parameters"
                                  })
       end
+
       send_reply_ack(message)
-      @conversation.bot.schedule_task(@@request_callback) do
-        result = {
-          :reply_message => message
-        }
+
+      process_result_cb = Proc.new do |reply|
+        reply[:reply_message] = message
+
+        @@request_callback.call reply
+      end
+
+      @conversation.bot.schedule_task do
         begin
-          result[:action_result] = @conversation.bot.interface._call(message.conv_thread.session,
+          @conversation.bot.interface._call(message.conv_thread.session,
                                                                      message.action_parameters[:node],
-                                                                     message.action_parameters[:parameters])
+                                                                     message.action_parameters[:parameters],
+                                                                     &process_result_cb)
         rescue CyberError => e
-          result[:error] = {
+          process_result_cb.call(:errors => [{
             :category  => e.category,
             :severity  => e.severity,
             :message   => e.message
-          }
+          }])
         rescue
-          result[:error] = {
+          process_result_cb.call(:errors => [{
             :category  => 'unknown',
             :severity  => :unrecoverable,
             :message   => $!.to_s
-          }
+          }])
         end
-
-        result
       end
     end
 
@@ -142,7 +147,7 @@ module CyborgHood
       @conversation.bot.schedule_task(@@request_callback) do
         {
           :reply_message => message,
-          :action_result => @conversation.bot.interface._is_node?(message.conv_thread.session, message.action_parameters[:node])
+          :results => @conversation.bot.interface._is_node?(message.conv_thread.session, message.action_parameters[:node])
         }
       end
     end
