HMCFletch
HMCFletch

Reputation: 1076

Load Subclases on Rails Initialization

I am having problems with getting Rails (3.1.3) to load some of my ActiveRecord subclasses on application initialization. Here is the directory structure I have:

- models
  - class1.rb # Class1 < ActiveRecord::Base
  - class1s
    - subclass1.rb # Subclass1 < Class1
    - subclass2.rb # Subclass2 < Class1

I am trying to make use of Class1.subclasses, but it keeps returning []. Adding #{config.root}/app/models/class1s to config.autoload_paths doesn't help because those clases are lazy loaded and nothing references the subclasses before I make a call to subclasses, so they are loaded yet. Adding the path to config.eager_load_paths doesn't seem to work either, and its behavior is based off the config.cache_classes setting which is usually different in development and production.

I'd like to be able to start up the rails console and see:

> Class1.subclasses
 => [Subclass1, Subclass2]

Right now this is the behavior I see:

> Class1.subclasses
 => []
> Subclass1
 => Subclass1
> Class1.subclasses
 => [Subclass1]
> Subclass2
 => Subclass2
> Class1.subclasses
 => [Subclass1, Subclass2]

Right now I am reduced to putting this at the bottom of my Class1 definition to get all of the subclasses to load on when I access the Class1 class:

ruby_files_pattern = File.join(Rails.application.config.root, "app", "models", "class1s", "**", "*.rb")
Dir.glob(ruby_files_pattern).each do |file|
  ActiveSupport::Dependencies.require_or_load(file)
end

It gets the job done, but I feel dirty doing it. Any thoughts would be much appreciated.

Upvotes: 10

Views: 1866

Answers (3)

Manas Chaudhari
Manas Chaudhari

Reputation: 276

Eager Loading is disabled in development environment by default.

Enable it from config/environments/development.rb

config.eager_load = true

Upvotes: 0

ymmyk
ymmyk

Reputation: 101

Problem with eager loading is that once a class changes then the subclasses will no longer be loaded. I use a constantizer in my development.rb that constantizes all my sub-classes (e.i. sub-directories) by file name on each request. I put it in lib as some tests need to contantize things as well.

lib/constantizer.rb

class Constantizer
  def self.path(path, include_current_path=true, namespace_prefix='')
    dirs = Dir[Rails.root.join(path, '**', '**')].select { |f| File.directory?(f) && File.basename(f) != 'concerns'}
    dirs << Rails.root.join(path) if include_current_path
    dirs.each do |dir|
      Dir["#{dir}/*.rb"].each do |file|
        (namespace_prefix + file.chomp(".rb").split("/").last.camelize).constantize
      end
    end
  end
end

app/environment/development.rb

class ConstantizeAllSubclasses
  def initialize(app)
    @app = app
  end

  def call(env)
    Constantizer.path('app/models', false)
    @app.call(env)
  end
end

Project::Application.configure do
  # Usual config items
  config.middleware.insert_after Warden::Manager, ConstantizeAllSubclasses
end

Upvotes: 2

mtfk
mtfk

Reputation: 890

The simplest way to do that, is just load all classes using ( >= 3.0 ):

Rails.application.eager_load!

it will load all classes from Your app, engines, plugins. Is the same result as turn of lazy loading in config. But in this approach You do not need to do that just use this method.

Upvotes: 4

Related Questions