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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With