ctran/annotate_models

Annotate gem does not support Rails autoloader collapsed directories

bazay opened this issue · 4 comments

bazay commented

Annotate gem does not support Rails autoloader collapsed directories i.e.

pry(AnnotateModels)> Rails.autoloaders.main
=> #<Zeitwerk::Loader:0x00007f6305898b58
 @autoloaded_dirs=[],
 @autoloads={},
 @collapse_dirs=
  #<Set: {"/home/developer/my_app/app/components/my_namespace/models"}>,
 @collapse_glob_patterns=#<Set: {/home/developer/my_app/app/components/*/models", "/home/developer/my_app/app/components/my_namespace/jobs"}>,
 @eager_load_exclusions=#<Set: {}>,
 @eager_loaded=true,
 @ignored_glob_patterns=
  #<Set: {"/home/developer/my_app/lib/rails",
   "/home/developer/my_app/lib/patches",
   "//home/developer/my_app/lib/rails/generators/job",
...
pry(AnnotateModels)> Rails.autoloaders.main.collapsed_dirs
=> #<Set: {"/home/developer/my_app/app/components/my_namespace/models"}>

In config/application.rb:

Rails.autoloaders.main.collapse("app/components/*/models")

I've obfuscated the model names and namespace, but the end result is still the same.

Commands

$ bundle exec annotate
Unable to annotate app/components/my_namespace/models/my_model.rb: file doesn't contain a valid model class
Unable to annotate app/components/my_namespace/models/my_second_model.rb: file doesn't contain a valid model class
Unable to annotate app/components/my_namespace/models/my_third_model.rb: file doesn't contain a valid model class
$

Version

  • annotate (3.2.0 96831c1)
  • rails (7.0.3.1)
  • ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
guigs commented

Here's a monkey patch we are using that find classes based on zeitwerk loader.
Our use case is for loading classes within namespaces. I'm not sure if it works with collapsed dirs, but maybe you can modify it to work.

Put in an initializer:

# frozen_string_literal: true

if Rails.env.development?
  require 'annotate'

  module AnnotateModelsWithZeitwerk
    def get_model_class(file)
      loader = Rails.autoloaders.main
      root_dirs = loader.dirs(namespaces: true) # or `root_dirs = loader.root_dirs` with zeitwerk < 2.6.1
      expanded_file = File.expand_path(file)
      root_dir, namespace = root_dirs.find do |dir, _|
        expanded_file.start_with?(dir)
      end
      _, filepath_relative_to_root_dir = expanded_file.split(root_dir)
      filepath_relative_to_root_dir = filepath_relative_to_root_dir[1..].sub(/\.rb$/, '')
      camelize = loader.inflector.camelize(filepath_relative_to_root_dir, nil)
      namespace.const_get(camelize)
    end
  end

  module AnnotateModels
    class << self
      prepend AnnotateModelsWithZeitwerk
    end
  end
end