列出所有Ruby类和方法

时间:2018-07-02 14:18:02

标签: ruby metaprogramming

一段时间以来,我一直希望有一种方法可以检查在加载特定模型时对给定红宝石环境所做的所有更改。此外,还可以比较和对比不同版本的ruby和不同的Ruby VM中可用的方法和类。

我创建了一些使用元编程生成这样的列表的代码:

arr = []
arr << "Ruby version " + ::RUBY_VERSION
arr << ""
Module.constants.each do |const|
  if ::Module.const_defined? const
    #If for whatever reason const_get fails, then rescue.
    begin
      obj = Module.const_get(const)
      if obj
        if obj.is_a? Class
        # Class methods
        arr << (obj.singleton_methods).sort.map do |method_sym|
          obj.to_s + "::" + method_sym.to_s
        end

        # Instance methods
        arr << (obj.instance_methods - (obj.superclass ? obj.superclass.instance_methods : []) - Object.methods).sort.map do |method_sym|
          "#<" + obj.to_s + ">." + method_sym.to_s
        end
        elsif obj.is_a? Module
          arr << (obj.methods - Module.methods).sort.map do |method_sym|
            obj.to_s + "::" + method_sym.to_s
          end
        else
          # Methods
          arr << "::" + const.to_s
        end
      end
    rescue
    end
  end
end

File.new("D:\\methods_#{::RUBY_VERSION}.txt","w").write(arr.flatten.sort.join("\n"))

该列表的标准是它应列出所有非继承的实例和类方法。常量用前缀::表示,类方法表示为MyClass::someMethod,实例方法表示为:#<MyClass>.someMethod

上面的脚本大部分都起作用,但是它错过了ObjectBasicObject。即在创建的列表中,没有以#<Object>.Object::为前缀的行。我缺少明显的东西吗?

1 个答案:

答案 0 :(得分:3)

我建议您为每个Ruby版本创建一个哈希,然后将该哈希或其内容保存到名称指示Ruby版本的文件中(例如v_2-5-1.jsonv_2-5-1.txt

尽管我不完全理解这个问题,但您可能会发现这里的方法ObjectSpace::each_object很有帮助。例如,

h = ObjectSpace.each_object(Class).with_object({}) do |c,h|
  h[c] = { cm: c.methods(false).sort, im: c.instance_methods(false).sort,
           con: c.constants.sort }
end

h.keys && [Object, BasicObject]
  # [Object, BasicObject]

您看到ObjectBasicObject是此哈希中的两个键(类)。让我们看一下其中一个键的值:Dir

h[Dir]
  #=> {:cm=>[:[], :chdir, :children, :chroot, :delete, :each_child, :empty?,
  #          :entries, :exist?, :exists?, :foreach, :getwd, :glob, :home,
  #          :mkdir, :open, :pwd, :rmdir, :unlink],
  #    :im=>[:close, :each, :fileno, :inspect, :path, :pos, :pos=, :read,
  #          :rewind, :seek, :tell, :to_path],
  #    :con=>[]}

这是

RUBY_VERSION
  #=> "2.5.1"

将此哈希的内容与v2.5.1的Dir的文档进行比较,我们发现结果对于类Dir是正确的。

获得在类C中定义的常量是有问题的。我上面使用的C.constants包含继承的常量。我是在写C.constants - C.superclass.constants时(谨记BasicObject.superclass #=> nil)不会报告继承的常量,而是在C中进行了重新定义(并且重新定义继承的常量不会产生警告消息)。

您可能希望检查所有模块(ObjectSpace.each_object(Module)),然后将键:is_class添加到具有值truefalse的哈希中,而不是仅查看类:

您可能希望将其他键添加到哈希中。 :superclass:included_modules(对于class)是我想到的两个。 :nesting(对于非类模块)是另一个。参见Module::nesting。我怀疑这些观察只是表面上的事情,两个Ruby版本的有意义的比较会非常复杂。