]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/commitdiff
plugins, improved how we lookup plugins filename
authorMatthias Hecker <apoc@geekosphere.org>
Sat, 13 Jun 2015 17:54:18 +0000 (19:54 +0200)
committerMatthias Hecker <apoc@geekosphere.org>
Sat, 13 Jun 2015 17:54:18 +0000 (19:54 +0200)
lib/rbot/core/utils/where_is.rb [new file with mode: 0644]
lib/rbot/plugins.rb

diff --git a/lib/rbot/core/utils/where_is.rb b/lib/rbot/core/utils/where_is.rb
new file mode 100644 (file)
index 0000000..bbf6b76
--- /dev/null
@@ -0,0 +1,106 @@
+# A little Ruby module for finding the source location where class and methods are defined.
+# https://gist.github.com/wtaysom/1236979
+module Where
+  class <<self
+    def is_proc(proc)
+      source_location(proc)
+    end
+
+    def is_method(klass, method_name)
+      source_location(klass.method(method_name))
+    end
+
+    def is_instance_method(klass, method_name)
+      source_location(klass.instance_method(method_name))
+    end
+
+    def are_methods(klass, method_name)
+      are_via_extractor(:method, klass, method_name)
+    end
+
+    def are_instance_methods(klass, method_name)
+      are_via_extractor(:method, klass, method_name)
+    end
+
+    def is_class(klass)
+      methods = defined_methods(klass)
+      file_groups = methods.group_by{|sl| sl[0]}
+      file_counts = file_groups.map do |file, sls|
+        lines = sls.map{|sl| sl[1]}
+        count = lines.size
+        line = lines.min
+        {file: file, count: count, line: line}
+      end
+      file_counts.sort_by!{|fc| fc[:count]}
+      source_locations = file_counts.map{|fc| [fc[:file], fc[:line]]}
+      source_locations
+    end
+
+    # Raises ArgumentError if klass does not have any Ruby methods defined in it.
+    def is_class_primarily(klass)
+      source_locations = is_class(klass)
+      if source_locations.empty?
+        methods = defined_methods(klass)
+        raise ArgumentError, (methods.empty? ?
+                              "#{klass} has no methods" :
+                              "#{klass} only has built-in methods (#{methods.size} in total)"
+                             )
+      end
+      source_locations[0]
+    end
+
+    def edit(location)
+      unless location.kind_of?(Array)
+        raise TypeError,
+          "only accepts a [file, line_number] array"
+      end
+      location
+    end
+
+    private
+
+    def source_location(method)
+      method.source_location || (
+        method.to_s =~ /: (.*)>/
+        $1
+      )
+    end
+
+    def are_via_extractor(extractor, klass, method_name)
+      methods = klass.ancestors.map do |ancestor|
+        method = ancestor.send(extractor, method_name)
+        if method.owner == ancestor
+          source_location(method)
+        else
+          nil
+        end
+      end
+      methods.compact!      
+      methods
+    end
+
+    def defined_methods(klass)
+      methods = klass.methods(false).map{|m| klass.method(m)} +
+        klass.instance_methods(false).map{|m| klass.instance_method(m)}
+      methods.map!(&:source_location)
+      methods.compact!
+      methods
+    end
+  end
+end
+
+def where_is(klass, method = nil)
+  begin
+    Where.edit(
+      if method
+        begin
+          Where.is_instance_method(klass, method)
+        rescue NameError
+          Where.is_method(klass, method)
+        end
+      else
+        Where.is_class_primarily(klass)
+      end).first
+  end
+end
+
index cf145c83efadeb9aaffd99e60461d372c1063f85..e40cfcc445a9a03abd46f24699e3e410d2dfbbc9 100644 (file)
@@ -4,6 +4,7 @@
 # :title: rbot plugin management
 
 require 'singleton'
+require_relative './core/utils/where_is.rb'
 
 module Irc
 class Bot
@@ -603,17 +604,6 @@ module Plugins
         debug "loading #{desc}#{fname}"
         plugin_module.module_eval(plugin_string, fname)
 
-        # this sets a BOTMODULE_FNAME constant in all BotModule
-        # classes defined in the module. This allows us to know
-        # the filename the plugin was declared in from outside
-        # the plugin itself (from within, a __FILE__ would work.)
-        plugin_module.constants.each do |const|
-          cls = plugin_module.const_get(const)
-          if cls.is_a? Class and cls < BotModule
-            cls.const_set("BOTMODULE_FNAME", fname)
-          end
-        end
-
         return :loaded
       rescue Exception => err
         # rescue TimeoutError, StandardError, NameError, LoadError, SyntaxError => err
@@ -800,7 +790,7 @@ module Plugins
       if botmodule
         @failed.clear
         @ignored.clear
-        filename = botmodule.class::BOTMODULE_FNAME
+        filename = where_is(botmodule.class)
         err = load_botmodule_file(filename, "plugin")
         if err.is_a? Exception
           @failed << { :name => botmodule.to_s,