There are a couple of good explanations for Rails autoloading behavior that you can find. This answer provides a useful explanation, which it grabs from this blog post at urbanautomaton.com.
Summary:
Basically, Rails first uses Ruby's constant lookup. If that fails, then it has its own lookup, which will miss files if
- They are not in the autoload paths (Rails won't find app/foo/bar.rb if
config.autoload_paths += %W(#{config.root}/foo)
is not added to config/application.rb)
- The directory name/namespace convention isn't followed
It will find Foo::Bar
in app/foo/bar.rb if it's defined as:
module Foo
class Bar
end
end
but not
module Foo
module Bar
end
end
- A constant in a different namespace is referenced without that namespace indicated in another file that's already been autoloaded
Quote from the blog post describing what I mean:
At this point, we’ve only seen how a single constant name maps to a
single file name. But as we know, a constant reference in Ruby can
resolve to a number of different constant definitions, which vary
depending on the nesting in which the reference was made. How does
Rails handle this?
The answer is, “partially”. As Module#const_missing
passes no nesting
information to the receiver, Rails does not know the nesting in which
the reference was made, and it must make an assumption. For any
reference to a constant Foo::Bar::Baz
, it assumes the following:
module Foo
module Bar
Baz # Module.nesting => [Foo::Bar, Foo]
end
end
In other words, it assumes the maximum nesting possible for a given constant
reference. The example reference is therefore treated exactly the same
as the following:
Foo::Bar::Baz # Module.nesting => []
module Foo::Bar
Baz # Module.nesting => [Foo::Bar]
end
While there’s
been a significant loss of information, Rails does have some extra
information it can use. It knows that Ruby failed to resolve this
particular constant reference using its regular lookup, meaning that
whatever constant it should refer to cannot be already loaded.
When Foo::Bar::Baz
is referred to, then, Rails will attempt to load
the following constants in turn, until it finds one that is already
loaded:
Foo::Bar::Baz
Foo::Baz
Baz
As soon as an already-loaded constant Baz
is encountered, Rails knows this cannot be the Baz
it is looking for,
and the algorithm raises a NameError
.
It's difficult to say what exactly is causing your problem(s) without more specifics about how your code is set up, but hopefully understanding what's going on with autoloading in Rails might help a little. I would highly recommend reading through the blog post and answer linked above.
Edited for possible solution:
Make sure 'app/models/foo' is added to your autoload_load paths in config/application.rb - or move 'foo' to app/models/concerns, which should be there by default.
Often adding explicit require
statements helps - especially if things are loading properly in development but not in test. I recently solved this problem by adding a file that required all of my nested files (I had a directory structure that did not mirror my namespace structure) and then requiring that file in my test helper.
Second edit
Including information relevant to the question, why a constant might load successfully only once.
From the same blog post:
If constants are loaded only when they’re first encountered at
runtime, then by necessity their load order depends on the individual
execution path. This can mean that the same constant reference
resolves to different constant definitions in two runs of the same
code. Worse still, the same constant reference twice in a row can give
different results.
Let’s go back to our last example. What happens if we call .print_qux
twice?
> Foo::Bar.print_qux
I'm in Foo!
=> nil
> Foo::Bar.print_qux
NameError: uninitialized constant Foo::Bar::Qux This is disastrous!
First we’ve been given the wrong result, and then we’ve been
incorrectly told that the constant we referred to doesn’t exist. What
on earth led to this?
The first time, as before, is down to the loss of nesting information.
Rails can’t know that Foo::Qux isn’t what we’re after, so once it
realises that Foo::Bar::Qux does not exist, it happily loads it.
The second time, however, Foo::Qux is already loaded. So our reference
can’t have been to that constant, otherwise Ruby would have resolved
it, and autoloading would never have been invoked. So the lookup
terminates with a NameError, even though our reference could (and
should) have resolved to the as-yet-unloaded ::Qux.
We can fix this by referring to ::Qux first, ensuring that it’s loaded
for Ruby to resolve the reference:
> Qux
=> "I'm at the root!"
> Foo::Bar.print_qux
I'm at the root!
=> nil
> Foo::Bar.print_qux
I'm at the root!
=> nil
A funny thing has happened here. In order to get correct behaviour, we
deliberately loaded the constant we needed before we used it (albeit
indirectly, by referring to it, rather than loading the file that
defined it).
But wait; isn’t this suspiciously close to explicitly loading our
dependencies with require, the very thing autoloading was supposed to
save us from?
To be fair, we could also have fixed the issue by fully qualifying all
of our constant references, i.e. making sure that within .print_qux we
referred to ::Qux and not the ambiguous Qux. But this still costs us
our existing intuitions about Ruby’s behaviour. Moreover, without
intimate knowledge of the autoloading process, we would have been hard
pressed to deduce that this was necessary.