给定Ruby文件my.rb,能否动态获取其中定义的模块名?
如何动态获取Ruby文件中定义的模块名?
当然可行!我给你分享几种实用的方法,根据你的场景需求选择就好:
方法一:对比加载前后的模块集合(适合已加载文件的场景)
这种思路是先记录加载文件前已存在的顶级模块,加载后对比找出新增的模块,非常适合你已经通过require/require_relative加载文件的情况:
# 记录加载前所有顶级模块(排除嵌套模块) existing_top_modules = ObjectSpace.each_object(Module) .select { |mod| mod.name&.split('::').size == 1 } .to_set # 动态加载目标文件 require_relative 'my.rb' # 找出加载后新增的顶级模块 new_modules = ObjectSpace.each_object(Module) .select { |mod| mod.name&.split('::').size == 1 } .to_set - existing_top_modules # 输出结果 puts "动态加载的模块名:#{new_modules.map(&:name).join(', ')}"
如果你的文件里有嵌套模块(比如module MyModule::SubModule),只需要调整筛选条件,把split('::').size == 1改成你需要的层级即可。
方法二:解析文件语法树(无需加载文件,无副作用)
如果你不想实际加载文件(避免文件中的执行代码带来副作用),可以通过解析Ruby的语法树来提取模块定义。这种方法需要用到parser gem,先安装:gem install parser,然后用下面的代码:
require 'parser/current' # 读取目标文件内容 file_content = File.read('my.rb') # 解析生成抽象语法树(AST) ast = Parser::CurrentRuby.parse(file_content) # 遍历AST提取所有模块名 module_names = [] ast.children.each do |node| next unless node.type == :module # 处理嵌套模块的情况,拼接完整模块名 full_name = node.children.first.children.map { |n| n.children.first }.join('::') module_names << full_name end puts "文件中定义的模块名:#{module_names.join(', ')}"
这个方法的优势是完全不会执行文件里的代码,安全可控,还能一次性提取所有模块(包括嵌套的)。
额外提示
- 如果你的
my.rb里只定义了一个模块,且确定是顶级模块,也可以尝试直接遍历ObjectSpace找最近加载的模块,但前两种方法更通用可靠。 - 注意如果模块名是动态生成的(比如用字符串拼接定义模块),语法树解析的方法可能无法准确识别,这种情况只能用第一种加载后对比的方法。
内容的提问来源于stack exchange,提问作者TieDad
相关产品推荐
相关产品推荐

