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!