commit 4d9f2962db882142ce63530d9b8844890bb557be
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Wed Nov 24 02:52:31 2010 +0100

    [evol] Task-based client DSL §4 (many fixes, helper, bot stop condition, more logging)

diff --git a/bin/test_client b/bin/test_client
index 31f99d5..2f8d94e 100755
--- a/bin/test_client
+++ b/bin/test_client
@@ -22,6 +22,7 @@
 $: << File.join(File.dirname(__FILE__), "..", "lib")
 
 require 'cyborghood/cyborg'
+require 'cyborghood/cyborg/dsl'
 
 
 module CyborgHood
@@ -39,19 +40,33 @@ module CyborgHood
         # to test the closure through the inner instance_eval calls
         toto = true
 
-#        task "compare stuff" do
+        c = 0
+        task "compare stuff" do
 #          ask :MapMaker, :ver1, "/api_version"
 #          ask :Librarian, :ver2, "/api_version"
-#          on_error do
-#            puts "PLOUF"
-#            pp reply
-#          end
-#          on_success do
-#            puts "OK"
-#            pp reply
+          wait_notification :thread, {:topic => "HELOz"}, 5 do |subtask, msg|
+            puts "POUET NOTIF"
+            subtask.finish
+          end
+          send_notification :thread, {:topic => "HELO"}
+          wait_timer 2, true do |subtask|
+            c += 1
+            puts "TIMER !!!"
+            STDOUT.flush
+            subtask.finish if c == 10
+          end
+          on_error do
+            puts "PLOUF"
+            pp errors
+            pp results
+          end
+          on_success do
+            puts "OK"
+            pp errors
+            pp results
 #            puts "Tadam: " + (ver1 == ver2 ? "same" : "different")
-#          end
-#        end
+          end
+        end
 
         conversation_with "MapMaker" do
           on_error do
diff --git a/lib/cyborghood/cyborg.rb b/lib/cyborghood/cyborg.rb
index 2fa982c..4f81a53 100644
--- a/lib/cyborghood/cyborg.rb
+++ b/lib/cyborghood/cyborg.rb
@@ -75,6 +75,7 @@ module CyborgHood
       logger.info "Bot starting"
       EventMachine.run do
         start_work
+        try_stop
       end
     rescue EventMachine::ConnectionNotBound
       logger.fatal "Internal error (EventMachine::ConnectionNotBound)"
@@ -90,7 +91,7 @@ module CyborgHood
     end
 
     def ready_to_stop?
-      true
+      DSL::Task.idle?
     end
 
     def stop
@@ -115,6 +116,10 @@ module CyborgHood
       []
     end
 
+    def task(name, &block)
+      DSL::Task.new(self, name, &block)
+    end
+
     def schedule_task(callback, &task)
       EventMachine.defer(task, callback)
     end
diff --git a/lib/cyborghood/cyborg/botnet.rb b/lib/cyborghood/cyborg/botnet.rb
index 0bfd83b..3799681 100644
--- a/lib/cyborghood/cyborg/botnet.rb
+++ b/lib/cyborghood/cyborg/botnet.rb
@@ -115,7 +115,7 @@ module CyborgHood
     end
 
     def ready_to_stop?
-      @comm_list.empty?
+      @comm_list.empty? and super
     end
   end
 end
diff --git a/lib/cyborghood/cyborg/dsl.rb b/lib/cyborghood/cyborg/dsl.rb
index f14f5ed..04019ac 100644
--- a/lib/cyborghood/cyborg/dsl.rb
+++ b/lib/cyborghood/cyborg/dsl.rb
@@ -20,26 +20,31 @@ require 'active_support/basic_object'
 
 
 module CyborgHood
-  module BotnetDSL
+  module DSL
     class BaseDSL < ActiveSupport::BasicObject
       def initialize(&block)
         self.instance_eval(&block)
         _start_dsl
       end
 
-      reveal logger
+      reveal :class
+      reveal :logger
     end
 
     class Task < BaseDSL
-      attr_reader :bot, :errors, :results
+      attr_reader :bot, :name, :errors, :results
 
-      @@task_list = {}
+      @@task_wip = 0
+
+      def self.idle?
+        @@task_wip == 0
+      end
 
       def initialize(bot, name = nil, previous_step_data = {}, &block)
         @bot = bot
         @name = name
 
-        @@task_list[@name] = self unless @name.nil?
+        @@task_wip += 1
 
         @errors = (previous_step_data[:errors] || {}).freeze
         @results = (previous_step_data[:results] || {}).freeze
@@ -56,7 +61,7 @@ module CyborgHood
         begin
           super(&block)
         rescue
-          logger.error "Task '#{@name}': could not initialize DSL: " + $!.to_s
+          logger.error "Task '#{@name}': error in DSL: " + $!.to_s
           logger.debug $!.backtrace.join("\n")
           @cancel_run = true
         end
@@ -131,7 +136,7 @@ module CyborgHood
 
       def task(name = nil, &block)
         _add_subtask("task/#{name}") do |subtask|
-          self.class.new(name || @name, &block)
+          self.class.new(@bot, name || @name, &block)
           subtask.finish
         end
       end
@@ -171,7 +176,8 @@ module CyborgHood
         attr_reader :name
         attr_accessor :results, :errors
 
-        def initialize(name, &block)
+        def initialize(task, name, &block)
+          @task = task
           @name = name
           @block = block
 
@@ -183,17 +189,20 @@ module CyborgHood
         def do_it
           @block.call self
         rescue
-          msg = "Task '#{@name}': error: " + $!.to_s
+          msg = "Task '#{@task.name}': error: " + $!.to_s
           logger.error msg
           @errors << CyberError.new(:unrecoverable, "botnet/client/dsl", msg)
+          logger.debug $!.backtrace.join("\n")
           finish
         end
 
         def finish
           if @finished
-            raise CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': subtask should have ended, but it lied")
+            raise CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@task.name}': subtask should have ended, but it lied")
           end
           @finished = true
+          logger.debug "Task '#{@task.name}': subtask '#{@name}' finished"
+          @task.__send__(:_check_finished)
         end
 
         def finished?
@@ -202,7 +211,7 @@ module CyborgHood
       end
 
       def _add_subtask(name, cb = nil, &block)
-        @subtasks << Subtask.new(name, &block)
+        @subtasks << Subtask.new(self, name, &block)
       end
 
       def _subtasks_finished?
@@ -216,17 +225,23 @@ module CyborgHood
         return unless _subtasks_finished?
 
         next_step_data = {
-          :errors = {},
-          :results = {}
+          :errors => {},
+          :results => {}
         }
 
         @subtasks.each do |subtask|
-          next_step_data[:errors][subtask.name] = subtask.errors
+          next_step_data[:errors][subtask.name] = subtask.errors unless subtask.errors.empty?
           next_step_data[:results].merge!(subtask.results)
         end
 
         cb = next_step_data[:errors].empty? ? @error_cb : @success_cb
-        self.class.new(name || @name, next_step_data, &cb) if cb
+        if cb
+          self.class.new(@bot, @name, next_step_data, &cb)
+        else
+          logger.debug "Task '#{@name}': finished"
+        end
+
+        @@task_wip -= 1
       end
 
       def _notification_criterias_match(msg, criterias)
@@ -244,10 +259,16 @@ module CyborgHood
       def _start_dsl
         return false if @cancel_run
 
-        @subtasks.each do |subtask|
-          # skip broken or canceled subtasks
-          next if subtask.finished?
-          subtask.do_it
+        logger.debug "Task '#{@name}': begining step"
+
+        if @subtasks.empty?
+          _check_finished
+        else
+          @subtasks.each do |subtask|
+            # skip broken or canceled subtasks
+            next if subtask.finished?
+            subtask.do_it
+          end
         end
 
         true
