simon.d
simon.d

Reputation: 2531

Subclass models in subfolders are not autoloaded

I have two levels of inheritance with some of my models: (this is all in /app/models)

A.rb
/A/B1.rb
/A/B1/C1.rb
/A/B1/C2.rb
/A/B1/C3.rb
/A/B2.rb
/A/B2/C1.rb
/A/B2/C2.rb
/A/B2/C3.rb
...

A.rb is a simple:

class A
  def self.inherited(subclass)
    puts "New subclass: #{subclass}"
  end

  # some methods
end

B1.rb is:

class A::B1 < A
  # some methods
end

C1.rb is:

class A::B1::C1 < A::B1
  # some methods
end

You can interpolate what the rest of the models look like.

When I load rails console, I see this:

New subclass: A::B1
New subclass: A::B1::C1
New subclass: A::B2

Notice that A::B1::C2, A::B1::C3, and none of the A::B2 subclasses are inherited! Why is that?

It gets even weirder. In rails console, I can then do this:

irb(main)> A::B1::C2
New subclass: A::B1::C2
A::B1::C2 < A::B1
irb(main)> A::B2::C1
New subclass: A::B2::C1
A::B2::C1 < A::B2

So I can type all the subclasses that weren't inherited, and then suddenly it fires the puts message. I think this proves the issue is with the autoloader, rather than my code.

This is happening in my development environment with Ruby 1.9. and Rails 3.2.

Here are my config.autoload_paths statements:

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

Thanks in advance!

Upvotes: 4

Views: 972

Answers (3)

steeling
steeling

Reputation: 183

This may be too late to help you, but I ran into the same issue, so hopefully this answer will help others.

If you are using the Spring gem, your application is actually running in the background. So simply restarting won't re-run the initializers, and won't add those directories to your autoload path.

You want to run bin/spring stop so that when you restart your app the initializers are run.

Upvotes: 0

simon.d
simon.d

Reputation: 2531

I wish I had a better answer but to Sergio's point, my subclasses are never directly mentioned so they aren't autoloaded.

I literally had to just write them out at the top of my class file:

B1.rb:

class A1::B1

  A1::B1::C1 # simply mentioning the class autoloads it
  A1::B2::C2
  ...

  def foo
    ...
  end

end

Upvotes: 0

Richard Peck
Richard Peck

Reputation: 76774

You'll need to include the various models in your config.autoload_paths option:

#config/application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]

This makes use of globbing - basically adding any files within a particular directory to your path. We use the above code for single-level directories (app/models/directory/1.rb); if you wanted to use multiple levels, you'll have to indicate them like this:

Dir[Rails.root.join('app', 'models', '{*}', '{**}')]

Upvotes: 1

Related Questions