icyleaf/wechat-bot

View on GitHub
lib/wechat/bot/handler_list.rb

Summary

Maintainability
A
55 mins
Test Coverage
module WeChat::Bot
  # Handler 列表
  class HandlerList
    include Enumerable

    def initialize
      @handlers = Hash.new {|h,k| h[k] = []}
      @mutex = Mutex.new
    end

    # 注册 Handler
    #
    # @param [Handler] handler
    # @return [void]
    def register(handler)
      @mutex.synchronize do
        handler.bot.logger.debug "[on handler] Registering handler with pattern `#{handler.pattern}`, reacting on `#{handler.event}`"
        @handlers[handler.event].push(handler)
      end
    end

    # 取消注册 Handler
    #
    # @param [Array<Handler>] handlers
    # @return [void]
    def unregister(*handlers)
      @mutex.synchronize do
        handlers.each do |handler|
          @handlers[handler.event].delete(handler)
        end
      end
    end

    # 分派执行 Handler
    #
    # @param [Symbol] event
    # @param [String] message
    # @param [Array<Object>] args
    # @return [Array<Thread>]
    def dispatch(event, message = nil, *args)
      threads = []

      if handlers = find(event, message)
        already_run = Set.new
        handlers.each do |handler|
          next if already_run.include?(handler.block)
          already_run.add(handler.block)

          if message
            captures = message.match(handler.pattern.to_r(message), event).captures
          else
            captures = []
          end

          threads.push(handler.call(message, captures, args))
        end
      end

      threads
    end

    # 查找匹配 Handler
    #
    # @param [Symbol] type
    # @param [String] message
    # @return [Hander]
    def find(type, message = nil)
      if handlers = @handlers[type]
        if message.nil?
          return handlers
        end

        handlers = handlers.select { |handler|
          message.match(handler.pattern.to_r(message), type)
        }.group_by {|handler| handler.group}

        handlers.values_at(*(handlers.keys - [nil])).map(&:first) + (handlers[nil] || [])
      end
    end

    def each(&block)
      @handlers.values.flatten.each(&block)
    end

    # 停止运行所有 Handler
    #
    # @return [void]
    def stop_all
      each { |h| h.stop }
    end
  end
end