meskyanichi/msngr

View on GitHub
lib/msngr/messenger.rb

Summary

Maintainability
A
0 mins
Test Coverage
require "thread"

class Msngr::Messenger

  # A client object interface to receive messages from.
  #
  # @return [Msngr::Clients::*]
  #
  attr_reader :client

  # An Array of Receiver objects to dispatch callbacks/messages to.
  #
  # @return [Array<Receiver>]
  #
  attr_reader :receivers

  # Initializes a new Messenger.
  #
  # @param [Msngr::Clients::*] client the to receive messages from.
  #
  # @return [Receiver]
  #
  def initialize(client)
    @client = client
    @receivers = []
    @mutex = Mutex.new
  end

  # Creates and returns a new Receiver and adds it to @receivers so
  # it'll receive messages matching the provided pattern.
  #
  # @param [Regexp] pattern
  #
  # @return [Receiver]
  #
  def subscribe(pattern)
    Msngr::Receiver.new(pattern).tap do |receiver|
      @mutex.synchronize { @receivers << receiver }
    end
  end

  # Removes the Receiver from @receivers and invokes the receiver's
  # @on_unsubscribe_callbacks.
  #
  # @param [Receiver] receiver
  #
  def unsubscribe(receiver)
    @mutex.synchronize { @receivers.delete(receiver) }
    dispatch(receiver.on_unsubscribe_callbacks, self)
  end

  # Listens on a new thread. Will auto-restart when crashed
  # in an attempt to recover from exceptions.
  #
  def listen!
    Thread.new do
      loop do
        begin
          listen
        rescue => e
          puts "Messenger error occurred:"
          puts "#{e.class.name}"
          puts "#{e.backtrace.join("\n")}"
          puts "Restarting.."
          sleep 1
        end
      end
    end
  end

  private

  # Instructs the @client to yield events/messages when it receives them. Received
  # events are matched with each Receiver's pattern and will dispatch the event's message
  # to all subscribing Receiver instances that match the event's pattern.
  #
  def listen
    client.on_message do |event, message|
      subscribing_receivers(event) do |receiver|
        dispatch(receiver.on_message_callbacks, message)
      end
    end
  end

  # Yields Receiver objects who's pattern matches the event.
  #
  # @param [String] event
  # @yield [Receiver]
  #
  def subscribing_receivers(event)
    receivers.each do |receiver|
      yield receiver if receiver.pattern.match(event)
    end
  end

  # Dispatches args to the provided callbacks.
  #
  # @param [Array<Proc>] callbacks
  # @param [Array] *args an Array of arguments to call each Proc with.
  #
  def dispatch(callbacks, *args)
    callbacks.each { |callback| callback.call(*args) }
  end
end