Rails fails to find models in subfolders

I have my models and controllers set up in sub-folders, so, for example,
sample.rb is in app/models/sample_log, and samples_controller.rb is in
app/controllers/sample_log. The controllers are defined to be within a
namespace just though the class name, eg like this:

class SampleLog::SamplesController < SampleLog::SuperController

However, the models are not.

class Sample < ActiveRecord::Base

Instead, I have added the relevant directories to config.load_paths in
environment.rb. Unit testing, functional testing and integration testing
all pass without a problem, and I can use the Rails console normally
too.

However, trying to access the web pages leads to some problems...

Putting a print statement in sample.rb, I can see that it is only when I
try to access a page with a sample on it that sample.rb gets loaded,
which makes perfect sense. However, it then throws an error:

Expected R:/samplelog/app/models/sample_log/sample.rb to define
SampleLog::Sample

This seems to be thrown after the file has been completely loaded, I am
guessing another model is loaded (as the page also uses a subclass of
Sample), and this references the Sample class, and Rails objects because
it cannot find the class.

Even though it has just loaded it...

I have experimented with forcing a load or require for each file without
success. Using load generates a "superclass mismatch for class
BatchSample" (BatchSample being the subclass of Sample). Using require
leads to the original problem, albeit on reloading the page or going to
another page in the section (the first show is good, however).
Curiously, even when using require, Rails still loads the file twice for
each page view. It seems not to realise the file is already loaded.

I have also tried adding the path to eager_load_paths, in the hope that
the models would get loaded at boot up, but that made no difference (and
the files were not loaded any earlier).

I wonder if anyone can shed any light, or give any suggestions.

I appreciate using a SampleLog namespace is one option, however,
experiments in that direction turned up other problems.

Using Rails 2.2.2 with JRuby 1.5 by the way.

When you use subfolders - you have to use namespaces. And Rails(even
2.3.5), especially ActiveRecord, have a whole bunch of bugs with
namespaces. I've fixed a couple, but there was more and more and more.
I've jsut got rid of namespaces and subfolders then.

I have my models and controllers set up in sub-folders, so, for example,
sample.rb is in app/models/sample_log, and samples_controller.rb is in
app/controllers/sample_log. The controllers are defined to be within a
namespace just though the class name, eg like this:

class SampleLog::SamplesController < SampleLog::SuperController

However, the models are not.

class Sample < ActiveRecord::Base

Where is this file? I'm guessing that it's in app/models/sample_log/sample.rb ?

Instead, I have added the relevant directories to config.load_paths in
environment.rb.

Which may be part of the problem. You might well be fighting
convention over configuration.

Unit testing, functional testing and integration testing
all pass without a problem, and I can use the Rails console normally
too.

However, trying to access the web pages leads to some problems...

Putting a print statement in sample.rb, I can see that it is only when I
try to access a page with a sample on it that sample.rb gets loaded,
which makes perfect sense. However, it then throws an error:

Expected R:/samplelog/app/models/sample_log/sample.rb to define
SampleLog::Sample

What this is saying is that Rails tried to find a class or module
SampleLog::Sample

What rails does here is to look for samplelog/sample.rb somewhere in
the various load paths. It's finding app/models/sample_log/sample.rb
which if it indeed contains:

class Sample < ActiveRecord::Base

outside of a class or module block is definining ::Sample, not
::SampleLog::Sample.

I'm guessing that moving app/models/sample_log/sample.rb to
app/models/sample.rb will fix this.

If not then show us the walkback you get on the error, it should point
to some piece of code which is referring to some constant
  (name_space::)*Sample

Thanks for the reply

I have my models and controllers set up in sub-folders, so, for example,
sample.rb is in app/models/sample_log, and samples_controller.rb is in
app/controllers/sample_log. The controllers are defined to be within a
namespace just though the class name, eg like this:

class SampleLog::SamplesController < SampleLog::SuperController

However, the models are not.

class Sample < ActiveRecord::Base

Where is this file? I'm guessing that it's in
app/models/sample_log/sample.rb ?

Yes

Instead, I have added the relevant directories to config.load_paths in
environment.rb.

Which may be part of the problem. You might well be fighting
convention over configuration.

Expected R:/samplelog/app/models/sample_log/sample.rb to define
SampleLog::Sample

What this is saying is that Rails tried to find a class or module
SampleLog::Sample

Is that right? I think it is the other way around; it is trying to find
Sample, but objects because it thinks it should be in sample.rb, not
sample_log/sample.rb.

"SampleLog::Sample" is not used anywhere in the project (and I have done
a search to confirm this).

What rails does here is to look for samplelog/sample.rb somewhere in
the various load paths. It's finding app/models/sample_log/sample.rb
which if it indeed contains:

class Sample < ActiveRecord::Base

outside of a class or module block is definining ::Sample, not
::SampleLog::Sample.

I'm guessing that moving app/models/sample_log/sample.rb to
app/models/sample.rb will fix this.

Yes, I think this is true (indeed, this is how it was originally set
up). However, I was HOPING to be able to split the models into sections.

If not then show us the walkback you get on the error, it should point
to some piece of code which is referring to some constant
  (name_space::)*Sample

R:/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:428:in
`load_missing_constant'
R:/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:77:in
`const_missing_with_dependencies'
R:/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:439:in
`load_missing_constant'
R:/lib/ruby/gems/1.8/gems/activesupport-2.2.2/lib/active_support/dependencies.rb:93:in
`const_missing'
app/controllers/sample_log/samples_controller.rb:11:in `home'

Here are lines 10 to 12

  def home
    @samples = Sample.find :all
  end

I still do not know exactly what was happening (and would be interested
to know), however...

I did think that maybe the problem is that I have models that inherit
from Sample and/or that I have controllers/models referencing constants
in other models, and it is this that is confusing Rails. However, I have
moved all the constants to a file in config/initializers (with a new
module), and it still happens with classes that do not inherit from
custom models.

The issue is related to how Rails caches models. If I set
config.cache_classes = true in config/environment/development.rb, then
it works fine (conversely, setting config.cache_classes = false in
config/environment/test.rb causes tests to fail). Alternatively, I can
require the relevant models in a file in config/initializers. Either
way, Rails keeps those models loaded, and it works okay. When caching is
turned off, Rails seems to load the file, use it, and forget it. When
another file tries to reference that class, Rails ignores load_paths,
and looks in the wrong place.

Anyway, a way around it is to require the files in a file in
config/initializers, so at least I am up and running now.

Andy Joel wrote:

Basic Ruby lesson.

You have

class SampleLog::SamplesController << ....

   def home
       @samples = Sample.find :all
   end
end

When Ruby needs to figure out what Sample means in that home action,
it first looks for a constant in the current namespace so it looks for

   SampleLog::SamplesController::Sample

Rails catches this as a missing constant and tries to find a file
under one of the load paths sample_log/samples_controller/sample.rb
but doesn't find one
so it hands the problem back to Ruby, which then tries to find the
constant Sample one step up the namespace chain and looks for
SampleLog::Sample.
Now rails looks for sample_log/sample.rb and finds it, and loads it,
and then checks to see whether SampleLog::Sample is now defined, which
it isn't because that file defined ::Sample instead.

Did you try just moving app/models/sample_log/sample.rb to
app/models/sample.rb as I suggested, I'm pretty sure that doing this,
and possibly removing the the stuff you did to monkey with the load
paths behind Rails back should fix your problem.

Rick Denatale wrote:

Basic Ruby lesson.

You have

class SampleLog::SamplesController << ....

   def home
       @samples = Sample.find :all
   end
end

When Ruby needs to figure out what Sample means in that home action,
it first looks for a constant in the current namespace so it looks for

   SampleLog::SamplesController::Sample

Rails catches this as a missing constant and tries to find a file
under one of the load paths sample_log/samples_controller/sample.rb
but doesn't find one
so it hands the problem back to Ruby, which then tries to find the
constant Sample one step up the namespace chain and looks for
SampleLog::Sample.
Now rails looks for sample_log/sample.rb and finds it, and loads it,
and then checks to see whether SampleLog::Sample is now defined, which
it isn't because that file defined ::Sample instead.

Rails is looking for ::Sample, not SampleLog::Sample (or so I would
assume). If it finds (and loads) sample_log/sample.rb then all would be
well. The problem seems to be that it is trying to be too clever. It
finds the file, which defines the right class (::Sample), but decides
that that file should be defining something else.

Did you try just moving app/models/sample_log/sample.rb to
app/models/sample.rb as I suggested, I'm pretty sure that doing this,
and possibly removing the the stuff you did to monkey with the load
paths behind Rails back should fix your problem.

I am sure this will work. This is where I started from. It does not keep
my models organised though!