Skip to content

Rails Admin loads every single file in autoload paths! #2770

@glebm

Description

@glebm

Every single file in autoload_paths is eagerly loaded by Rails admin. Below, I'll explain how this happens, why this shouldn't happen, and will propose a solution.

I came across this when trying to figure a bizarre issue in Thredded + rails_admin compatibility: thredded/thredded#280.

How does this happen?

Here is what RailsAdmin tries to load, lib/rails_admin/config.rb#L322:

def viable_models
  included_models.collect(&:to_s).presence || begin
    @@system_models ||= # memoization for tests
        ([Rails.application] + Rails::Engine.subclasses.collect(&:instance)).flat_map do |app|
          (app.paths['app/models'].to_a + app.config.autoload_paths).collect do |load_path|
            Dir.glob(app.root.join(load_path)).collect do |load_dir|
              Dir.glob(load_dir + '/**/*.rb').collect do |filename|
                # app/models/module/class.rb => module/class.rb => module/class => Module::Class
                lchomp(filename, "#{app.root.join(load_dir)}/").chomp('.rb').camelize
              end
            end
          end
        end.flatten.reject { |m| m.starts_with?('Concerns::') } # rubocop:disable MultilineBlockChain
  end
end

It loads these by calling constantize in lib/rails_admin/abstract_model.rb#L13-L25:

def all(adapter = nil)
  @@all ||= Config.models_pool.collect { |m| new(m) }.compact
  adapter ? @@all.select { |m| m.adapter == adapter } : @@all
end

alias_method :old_new, :new
def new(m)
  m = m.constantize unless m.is_a?(Class)
  (am = old_new(m)).model && am.adapter ? am : nil
rescue LoadError, NameError
  puts "[RailsAdmin] Could not load model #{m}, assuming model is non existing. (#{$ERROR_INFO})" unless Rails.env.test?
  nil
end

Why this shouldn't happen?

If something is in autoload_paths, it doesn't mean it can or should always be loaded. Rails explicitly makes this distinction with eager_load.

E.g. if I'm developing a Rails engine, I might want to add lib to autoload paths (to ease the development of the engine), but that doesn't mean I want to eagerly load ruby files in lib/generators/templates!

Proposed solution

There are a few options here:

  1. Remove app.config.autoload_paths from the files RailsAdmin tries to autoload. Only load things from app/models directories.
  2. Replace app.config.autoload_paths with app.paths.eager_load.
  3. Stop messing with the loading system! Just include a module into the base class!

3 is ideal but is also the most effort and I don't have the time. 1 breaks backwards compatibility.
So I'll send a PR for 2 for now.
Perhaps one day somebody can do 3.

@mshibuya @bbenezech @sferik

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions