module Invoker module Event class Manager attr_accessor :scheduled_events, :triggered_events def initialize @scheduled_events = Hash.new {|h,k| h[k] = [] } @triggered_events = [] @trigger_mutex = Mutex.new() end # Trigger an event. The event is not triggered immediately, but is just scheduled to be # triggered. # # @param command_label [String] Command for which event should be triggered # @param event_name [Symbol, nil] The optional event name def trigger(command_label, event_name = nil) @trigger_mutex.synchronize do triggered_events << OpenStruct.new( :command_label => command_label, :event_name => event_name) end end # Schedule an Event. The event will only trigger when a scheduled event matches # a triggered event. # # @param command_label [String] Command for which the event should be triggered # @param event_name [String, nil] Optional event name # @param block The block to execute when event actually triggers def schedule_event(command_label, event_name = nil, &block) @trigger_mutex.synchronize do scheduled_events[command_label] << OpenStruct.new(:event_name => event_name, :block => block) end end # On next iteration of event loop, this method is called and we try to match # scheduled events with events that were triggered. def run_scheduled_events filtered_events_by_name_and_command = [] triggered_events.each_with_index do |triggered_event, index| matched_events = scheduled_events[triggered_event.command_label] if matched_events && !matched_events.empty? filtered_events_by_name_and_command, unmatched_events = filter_matched_events(matched_events, triggered_event) triggered_events[index] = nil remove_scheduled_event(unmatched_events, triggered_event.command_label) end end triggered_events.compact! filtered_events_by_name_and_command.each {|event| yield event } end private def filter_matched_events(matched_events, event) matched_filtered_events = [] matched_events.each_with_index do |matched_event, index| if !event.event_name || event.event_name == matched_event.event_name matched_filtered_events << matched_event matched_events[index] = nil end end [matched_filtered_events, matched_events.compact] end def remove_scheduled_event(matched_events, command_label) if !matched_events || matched_events.empty? scheduled_events.delete(command_label) else scheduled_events[command_label] = matched_events end end end end end