Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 435
HMCFletch Avatar asked Nov 29 '11 05:11

HMCFletch


2 Answers

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.

like image 155
mtfk Avatar answered Nov 06 '22 11:11

mtfk


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
like image 21
ymmyk Avatar answered Nov 06 '22 11:11

ymmyk