ActiveSupport::Dependencies#load_missing_constant algorithm

Hi all! Recently I’ve noticed an issues with autoloading of a specific classes in my app. I’ve created a small project which reproduces the issue: https://github.com/knovoselic/autoload_repro/

All changes required for this repro are part of this commit: https://github.com/knovoselic/autoload_repro/commit/a8cecda742d6d41876abd94f6e0aef4c9758c588

You can just pull the code and run it. The following URL: http://localhost:3000/api/graph/results/crash should crash with uninitialized constant Api::Graph::ResultsController::Graph error.

Calling http://localhost:3000/api/graph/results/success will work fine, and any calls to crash URL after calling success will work fine, until results_controller.rb is modified. Then again crash URL fails.

I’ve been looking into this and I understand that Rails doesn’t know nesting at the point where unknown constant is referenced. This is because Ruby doesn’t provide that info in const_missing method. I’ve been looking at Rails code (specifically lib/active_support/dependencies.rb#load_missing_constant):

def load_missing_constant(from_mod, const_name)

  unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)

    raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"

  end

  qualified_name = qualified_name_for from_mod, const_name

  path_suffix = qualified_name.underscore

  file_path = search_for_file(path_suffix)

  if file_path

    expanded = File.expand_path(file_path)

    expanded.sub!(/\.rb\z/, "".freeze)

    if loading.include?(expanded)

      raise "Circular dependency detected while autoloading constant #{qualified_name}"

    else

      require_or_load(expanded, qualified_name)

      raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)

      return from_mod.const_get(const_name)

    end

  elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)

    return mod

  elsif (parent = from_mod.parent) && parent != from_mod &&

! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }

    # If our parents do not have a constant named +const_name+ then we are free

    # to attempt to load upwards. If they do have such a constant, then this

    # const_missing must be due to from_mod::const_name, which should not

    # return constants from from_mod's parents.

    begin

      # Since Ruby does not pass the nesting at the point the unknown

      # constant triggered the callback we cannot fully emulate constant

      # name lookup and need to make a trade-off: we are going to assume

      # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even

      # though it might not be. Counterexamples are