Project

General

Profile

« Previous | Next » 

Revision 612572f9

Added by Marc Dequènes about 14 years ago

  • ID 612572f9c30120c90f48b9ba3ddd71288c258d2a

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

View differences:

lib/cyborghood/cyborg/dsl.rb
self.instance_eval(&block)
_start_dsl
end
reveal logger
end
class Task < BaseDSL
......
@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
......
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
......
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
......
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
......
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
......
@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
......
@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

Also available in: Unified diff