是否可以呈现"源代码"对于包含任何包含模型的Ruby类?

时间:2016-11-30 09:30:06

标签: ruby abstract-syntax-tree

说,我有以下Ruby:

module Nameable
  def name
    "John"
  end
end

class User
  include Nameable
  def email
    "john@example.com"
  end
end

有没有办法显示,打印或查看扩展的源代码"整个"用户"?我不确定该术语是什么,但是使用"扩展源代码"我的意思是Ruby中的代码(所以不是AST),其中包含的部分包括:

class User
  def name
    "John"
  end

  def email
    "john@example.com"
  end
end

当解决方案还可以显示继承行为的代码时的加值点。

1 个答案:

答案 0 :(得分:2)

您可以使用method_source gem

require 'method_source'

def flatten_source_code(klass)
  excluded_parents = [Kernel, Object, BasicObject]
  type = klass.instance_of?(Module) ? "module" : "class"
  puts "#{type} #{klass}"

  (klass.ancestors-excluded_parents).reverse.each do |ancestor|
    ancestor.instance_methods(false).each do |method_name|
      method = ancestor.instance_method(method_name)
      script, line = method.source_location
      if script then
        puts
        puts "  # #{script} (line #{line})"
        puts method.source
      else
        puts
        puts "  #  def #{method_name}"
        puts "  #  end"
      end
    end
  end
  puts "end"
end

module Nameable
  def name
    "John"
  end
end

class User
  include Nameable
  def email
    "john@example.com"
  end
end


flatten_source_code(User)

puts 

flatten_source_code(Nameable)

#=>
# class User
#
#   # flatten_source_code.rb (line 27)
#   def name
#     "John"
#   end
#
#   # flatten_source_code.rb (line 34)
#   def email
#     "john@example.com"
#   end
# end

# module Nameable
#
#   # flatten_source_code.rb (line 27)
#   def name
#     "John"
#   end
# end

对于更复杂的情况,它不会完美地运行,但它可以为您的示例做好准备。

只是为了好玩,用:

module Nameable
  def name
    "John"
  end

  def email
    "included john@example.com"
  end
end

module AnotherModule
  def email
    "prepended john@example.com"
  end
end

class User
  include Nameable
  prepend AnotherModule
  def email
    "john@example.com"
  end
end

flatten_source_code(User)
puts "u = User.new"
puts "puts u.name"
puts "puts u.email"

启动ruby flatten_source_code.rb | ruby返回:

John
prepended john@example.com

因此显示的Ruby代码有效并且遵循继承顺序。 但它会多次定义相同的方法。

你可以保留|method_name,method_source|的哈希值。如果某个方法覆盖旧方法并调用super,则可以将旧方法定义为私有old_method_name_from_Module_blabla并相应地替换super