commit 612572f9c30120c90f48b9ba3ddd71288c258d2a
Author: Marc Dequènes (Duck) <Duck@DuckCorp.org>
Date:   Tue Nov 23 13:29:50 2010 +0100

    [evol] Task-based client DSL §2 (Subtask and logger)

diff --git a/lib/cyborghood/cyborg/dsl.rb b/lib/cyborghood/cyborg/dsl.rb
index 3b3d706..528e99a 100644
--- a/lib/cyborghood/cyborg/dsl.rb
+++ b/lib/cyborghood/cyborg/dsl.rb
@@ -26,6 +26,8 @@ module CyborgHood
         self.instance_eval(&block)
         _start_dsl
       end
+
+      reveal logger
     end
 
     class Task < BaseDSL
@@ -46,24 +48,23 @@ module CyborgHood
         @error_cb = nil
         @success_cb = nil
 
-        @logguer = Logger.instance
         @notification_name = "task/#{@name}"
 
         begin
           super(&block)
         rescue
-          @logger.error "Task '#{@name}': could not initialize DSL: " + $!.to_s
-          @logger.debug $!.backtrace.join("\n")
+          logger.error "Task '#{@name}': could not initialize DSL: " + $!.to_s
+          logger.debug $!.backtrace.join("\n")
           @cancel_run = true
         end
       end
 
       # may return a Hash of results
       def schedule(&job)
-        _add_subtask("job/#{job.hash}") do |subtask_cb|
-          @logger.debug "Task '#{@name}': Scheduling job"
+        _add_subtask("job/#{job.hash}") do |subtask|
+          logger.debug "Task '#{@name}': Scheduling job"
           cb = Proc.new do
-            subtask_cb true
+            subtask.finish
           end
           @bot.schedule_task(cb, job)
         end
@@ -72,11 +73,11 @@ module CyborgHood
       def send_notification(name, data)
         name = @notification_name if name == :thread
 
-        _add_subtask("notification/#{name}/out") do |subtask_cb|
-          @logger.debug "Task '#{@name}': Sending notification to '#{name}'"
+        _add_subtask("notification/#{name}/out") do |subtask|
+          logger.debug "Task '#{@name}': Sending notification to '#{name}'"
           chan = @bot.get_channel(name)
           chan << data
-          subtask_cb.call(true)
+          subtask.finish
         end
       end
 
@@ -85,38 +86,36 @@ module CyborgHood
 
         full_results = {}
 
-        _add_subtask("notification/#{name}/in") do |subtask_cb|
-          @logger.debug "Task '#{@name}': Waiting notification on '#{name}'"
+        _add_subtask("notification/#{name}/in") do |subtask|
+          logger.debug "Task '#{@name}': Waiting notification on '#{name}'"
           subcription_id = @bot.get_channel(name).subscribe do |msg|
             if _notification_criterias_match(msg, criterias)
-              stop, results = cb.call(msg)
-              @bot.get_channel(name).unsubscribe(subcription_id) if stop
-              subtask_cb.call(stop, results)
+              cb.call(subtask, msg)
+              @bot.get_channel(name).unsubscribe(subcription_id) if subtask.finished?
             end
           end
 
           if timeout
             EventMachine.add_timer(timeout) do
               @bot.get_channel(name).unsubscribe(subcription_id)
-              subtask_cb.call
+              subtask.finish
             end
           end
         end
       end
 
       def wait_timer(timeout, repeat = false, &cb)
-        _add_subtask("timer/#{timeout}/#{cb.hash}") do |subtask_cb|
+        _add_subtask("timer/#{timeout}/#{cb.hash}") do |subtask|
           periodic_timer_signature = nil
 
           timer_cb = Proc.new do
             if repeat
-              stop, results = cb.call
-              EventMachine.cancel_timer(periodic_timer_signature) if stop
+              cb.call(subtask)
+              EventMachine.cancel_timer(periodic_timer_signature) if subtask.finished?
             else
-              stop = true
-              results = cb.call
+              cb.call(subtask)
+              subtask.finish
             end
-            subtask_cb.call(stop, results)
           end
 
           if repeat
@@ -128,9 +127,9 @@ module CyborgHood
       end
 
       def task(name = nil, &block)
-        _add_subtask("task/#{name}") do |subtask_cb|
+        _add_subtask("task/#{name}") do |subtask|
           self.class.new(name || @name, &block)
-          subtask_cb.call(true)
+          subtask.finish
         end
       end
 
@@ -149,7 +148,7 @@ module CyborgHood
       end
 
       def stop_bot(condition)
-        _add_subtask('stop') do |subtask_cb|
+        _add_subtask('stop') do |subtask|
           case condition
           when :when_finished
             # try to stop gracefully
@@ -159,63 +158,53 @@ module CyborgHood
             @bot.stop
           end
 
-          subtask_cb.call(true)
+          subtask.finish
         end
       end
 
       protected
 
-      def _add_subtask(name, cb = nil, &block)
-        # TODO: create a Subtask object, with the following attributes, plus the subtask_cb
-        #       and pass it to the block instead of the subtask_cb, to allow easier
-        #       manipulation of errors/results/... and being more extensible
-        subtask = {
-          :name     => name,
-          :done     => false,
-          :results  => {},
-          :errors   => []
-        }
+      class Subtask
+        attr_reader :name
+        attr_accessor :results, :errors
 
-        # can be called multiple times with stop=false to collect result
-        # and stop=true will consider it stopper
-        subtask_cb = Proc.new do |stop, results|
-          if subtask[:done]
-            subtask[:error] = CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': subtask should have ended, but it lied")
-          else
-            if results.is_a? CyberError
-              subtask[:errors] << results
-            elsif results.is_a? Hash
-              subtask[:results].merge results
-            elsif not results.nil? # nil just mean no result
-              subtask[:errors] << CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': unknown result for subtask")
-            end
+        def initialize(name, &block)
+          @name = name
+          @block = block
 
-            if stop
-              subtask[:done] = true
-              _check_finished
-            end
-          end
+          @finished = false
+          @results = {}
+          @errors = []
         end
 
-        subtask[:block] = Proc.new do
-          begin
-            block.call subtask_cb
-          rescue
-            msg = "Task '#{@name}': error: " + $!.to_s
-            logger.error msg
-            subtask[:errors] << CyberError.new(:unrecoverable, "botnet/client/dsl", msg)
-            subtask[:done] = true
+        def do_it
+          @block.call self
+        rescue
+          msg = "Task '#{@name}': error: " + $!.to_s
+          logger.error msg
+          @errors << CyberError.new(:unrecoverable, "botnet/client/dsl", msg)
+          finish
+        end
+
+        def finish
+          if @finished
+            raise CyberError.new(:unrecoverable, "botnet/client/dsl", "Task '#{@name}': subtask should have ended, but it lied")
           end
+          @finished = true
         end
 
-        @subtasks << subtask
+        def finished?
+          @finished
+        end
+      end
 
-        nil
+      def _add_subtask(name, cb = nil, &block)
+        @subtasks << Subtask.new(name, &block)
       end
 
       def _subtasks_finished?
         @subtasks.each do |subtask|
-          return false unless subtask[:done]
+          return false unless subtask.finished?
         end
         return true
       end
@@ -251,8 +240,8 @@ module CyborgHood
 
         @subtasks.each do |subtask|
           # skip broken or canceled subtasks
-          next if subtask[:done]
-          subtask[:block].call
+          next if subtask.finished?
+          subtask.do_it
         end
 
         true
