rikai/Showbot

View on GitHub
lib/cinch/plugins/devtools.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'objspace'

module Cinch
  module Plugins
    class DevTools
      include Cinch::Plugin

      match /plugins\s*$/i,           :method => :command_list_plugins
      match /resources\s*$/i,         :method => :command_all_resources
      match /resources\s+(.*)\s*$/i,  :method => :command_resources

      def command_list_plugins(m)
        m.reply plugins.join ', '
      end

      def command_all_resources(m)
        stats = {}

        each_plugin do |root_object, plugin_name|
          total_memory = total_memory_for root_object
          stats[plugin_name] = 0 unless stats.has_key? plugin_name
          stats[plugin_name] = stats[plugin_name] + total_memory
        end

        average = average_usage stats
        standard_deviation = standard_deviation_of stats

        m.reply "#{plugin_count} plugins, average: #{average}, std deviation: #{standard_deviation}"

        stats.each_pair do |key, value|
          m.reply "#{Format(:bold, key.to_s)}: #{highlight_value(value, average, standard_deviation)}"
        end
      end

      def command_resources(m, plugin_name)
        each_plugin_named(plugin_name) do |root_object, plugin_name|
          m.reply "#{Format(:bold, plugin_name.to_s)}: #{total_memory_for root_object}"

          objects_in(root_object).select do |obj|
            !(['Class', 'Cinch::Bot'].include? obj.class.name)
          end.each do |obj|
            m.reply "  #{Format(:bold, obj.class.name.to_s)}: #{total_memory_for obj}"
          end
        end
      end

      private

      def plugins
        Cinch::Plugins.constants
      end

      def plugin_count
        plugins.size
      end

      def plugin_for(plugin_name)
        Cinch::Plugins.const_get plugin_name
      end

      def each_plugin
        plugins.each do |plugin_name|
          each_plugin_named(plugin_name) do |root_object, plugin_name|
            yield root_object, plugin_name
          end
        end
      end

      def each_plugin_named(plugin_name)
        ObjectSpace.each_object(plugin_for plugin_name) do |root_object|
          yield root_object, plugin_name
        end
      end

      def objects_in(root_object)
        ObjectSpace.reachable_objects_from root_object
      end

      def total_memory_for(root_object)
        ObjectSpace.memsize_of(root_object) + objects_in(root_object).map do |obj|
          ObjectSpace.memsize_of obj
        end.reduce(0, :+)
      end

      def average_usage(stats)
        stats.values.reduce(0, :+) / stats.size
      end

      def variance_of(stats)
        mean = average_usage stats
        sum = stats.values.map do |value|
          (value - mean) ** 2
        end.reduce(0, :+)

        sum / (stats.size - 1)
      end

      def standard_deviation_of(stats)
        Math.sqrt(variance_of stats).floor
      end

      def highlight_value(value, average, standard_deviation)
        if value > average + 2 * standard_deviation
          Format(:red, value.to_s)
        elsif value > average + standard_deviation
          Format(:orange, value.to_s)
        elsif value > average + standard_deviation / 2
          Format(:yellow, value.to_s)
        else
          Format(:green, value.to_s)
        end
      end
    end
  end
end