【问题标题】:List all Ruby classes and methods列出所有 Ruby 类和方法
【发布时间】:2018-07-02 14:18:01
【问题描述】:

一段时间以来,我一直想要一种方法来检查在加载特定模型时对给定 ruby​​ 环境所做的所有更改。此外,能够比较和对比不同版本的 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表示,实例方法以#&lt;MyClass&gt;.someMethod表示。

上面的脚本在大多数情况下都有效,但是它忽略了ObjectBasicObject。 IE。在创建的列表中,没有以#&lt;Object&gt;.Object:: 为前缀的行。我错过了什么明显的东西吗?

【问题讨论】:

  • - Object.methods 有效地删除了 ObjectBasicObject 中的剩余方法。
  • 顺便说一句,实例方法通常写成ClassName#method_name,而不是#&lt;ClassName&gt;.method_name
  • 哦,错了...你是绝对正确的... xD我必须抓住这一点。是的,ClassName#methodName 是我考虑过的一种形式。但是我不能像从 ruby​​ 那样执行它,这让我接受了 #&lt;&gt;.someMethod 语法......虽然我可能想恢复那个决定。

标签: ruby metaprogramming


【解决方案1】:

我建议您为每个 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 #=&gt; nil)不会报告继承的常量,则在C 中重新定义(并且重新定义继承的常量不会产生警告消息)。

您可能希望检查所有模块 (ObjectSpace.each_object(Module)) 并将键 :is_class 添加到值为 truefalse 的散列中,而不是只查看类:

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

【讨论】:

  • 感谢您提供非常详细的答案!我实际上并不认为散列会允许对象和类作为键!我猜在内部 ruby​​ 使用类的加密哈希来识别它......但这确实简化了代码。
  • Sancam,很高兴听到您觉得它有帮助。
猜你喜欢
  • 2016-08-30
  • 1970-01-01
  • 1970-01-01
  • 2016-03-25
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多