require 'initializer' unless defined? ::Rails::Initializer
require 'action_controller/dispatcher' unless defined? ::ActionController::Dispatcher
module HasManyPolymorphs
=begin rdoc
Searches for models that use has_many_polymorphs or acts_as_double_polymorphic_join and makes sure that they get loaded during app initialization. This ensures that helper methods are injected into the target classes.
Note that you can override DEFAULT_OPTIONS via Rails::Configuration#has_many_polymorphs_options. For example, if you need an application extension to be required before has_many_polymorphs loads your models, add an after_initialize block in config/environment.rb that appends to the 'requirements' key:
Rails::Initializer.run do |config|
# your other configuration here
config.after_initialize do
config.has_many_polymorphs_options['requirements'] << 'lib/my_extension'
end
end
=end
MODELS_ROOT = "#{RAILS_ROOT}/app/models/"
DEFAULT_OPTIONS = {
:file_pattern => "#{MODELS_ROOT}**/*.rb",
:file_exclusions => ['svn', 'CVS', 'bzr'],
:methods => ['has_many_polymorphs', 'acts_as_double_polymorphic_join'],
:requirements => []}
mattr_accessor :options
@@options = HashWithIndifferentAccess.new(DEFAULT_OPTIONS)
# Dispatcher callback to load polymorphic relationships from the top down.
def self.autoload
_logger_debug "autoload hook invoked"
options[:requirements].each do |requirement|
_logger_warn "forcing requirement load of #{requirement}"
require requirement
end
Dir.glob(options[:file_pattern]).each do |filename|
next if filename =~ /#{options[:file_exclusions].join("|")}/
open(filename) do |file|
if file.grep(/#{options[:methods].join("|")}/).any?
begin
modelname = filename[0..-4]
modelname.slice!(MODELS_ROOT)
model = modelname.camelize
_logger_warn "preloading parent model #{model}"
model.constantize
rescue Object => e
_logger_warn "#{model} could not be preloaded: #{e.inspect}"
end
end
end
end
end
end
class Rails::Initializer #:nodoc:
# Make sure it gets loaded in the console, tests, and migrations
def after_initialize_with_autoload
after_initialize_without_autoload
HasManyPolymorphs.autoload
end
alias_method_chain :after_initialize, :autoload
end
ActionController::Dispatcher.to_prepare(:has_many_polymorphs_autoload) do
# Make sure it gets loaded in the app
HasManyPolymorphs.autoload
end