Search code examples
metaprogrammingcrystal-lang

crystal-lang : how to fill an array with all modules defined in files in a specific package of the project?


All in the question, I would like to know if it's possible to do something like this in Crystal (pseudo-code) :

modules = Array(Module).new
foreach (file in specific_package) do
  if (file.is_a(crystal_module) do
    modules.push(file.module)
  end
end

Solution

  • Crystal doesn't have packages but modules. If you want to get all modules in a file you can try this:

    require "compiler/crystal/syntax"
    
    Modules = [] of String
    
    class ModuleVisitor < Crystal::Visitor
      def visit(node : Crystal::ModuleDef)
        Modules << node.name.names.join("::")
        true
      end
    
      def visit(node : Crystal::ASTNode)
        true
      end
    end
    
    Dir.glob("./*.cr").each do |file|
      visitor = ModuleVisitor.new
      parser = Crystal::Parser.new(File.read(file))
      parser.filename = file
      node = parser.parse
      node.accept(visitor)
    end
    
    puts Modules.join("\n")
    

    Then you'd get an Array(String) with all modules names

    Try it online!

    If you want to get all included modules in a class try this:

    class Class
      def included_modules
      {% if @type.ancestors.any? { |a| a.class.stringify.ends_with?(":Module") } %}
        {{ @type.ancestors.select { |a| a.class.stringify.ends_with?(":Module") } }}
      {% else %}
        [] of Nil
      {% end %}
      end
    end
    
    module Foo::Bar
    end
    
    class Baz
      include Foo::Bar
    end
    
    puts Baz.included_modules # => [Foo::Bar]
    

    Try it online!