问题
I implemented STI in one of my models, using some tips from Alex Reisner's blog post. I already had all my subclasses use the superclass's controller, with serialize/store to hold the extra attributes. I added the model_name
and self.select_options
methods to the superclass, and the preload initializer from Alex's blog. I also changed my collection_select
in the _form view helper and attribute validation to use the self.select_options
method. All my subclasses are in individual files in a app/models/subfolder, though they're not namespaced like SubFolder::Subclass.
Then I start running into problems. Upon changing any code, self.select_options
stops returning all of the subclasses. It only returns a small subset or none. Thus due to the validation and _form tie-in, I can't edit/update my models after a code change. From what I could tell, when I change any code, Rails reloads the environment, but not the models in the subfolder.
I tried adding the routes to config.autoload_paths like many suggest, but ultimately that didn't work.
So ultimately, I want:
- Something to fix the autoloading, so I won't have to restart the server after every change
- Base it off the subdirectory containing all the children, to avoid manually maintaining an array
- Rails 3.2.11, ruby 1.9.3p125, ubuntu 12.04.01, rvm
回答1:
I ended up combining the code from this answer and this one and knowledge gleaned from the wondible blog posts at the bottom. The config.autoload_paths
never seemed to help anything, but I kept them in there. The key part is the initializer that requires all the files in the subdirectory on startup and then at each reload. I tried load
over require_dependency
, which didn't work. It's definitely been nice not having to reload all the time.
In application.rb
config.autoload_paths += %W(#{config.root}/app/models/configuration)
In development.rb
config.autoload_paths += Dir["#{config.root}/app/models/configuration/**"]
In preload_sti_models.rb
if Rails.env.development?
Dir.entries("#{Rails.root}/app/models/subfolder").each do |c|
require_dependency File.join("app","models", "subfolder", "#{c}") if c =~ /.rb$/
end
ActionDispatch::Reloader.to_prepare do
Dir.entries("#{Rails.root}/app/models/subfolder").each do |c|
require_dependency File.join("app","models", "subfolder", "#{c}") if c =~ /.rb$/
end
end
end
Some blog posts with useful information
- http://wondible.com/2012/01/13/rails-3-2-autoloading-in-theory/
- http://wondible.com/2011/12/30/rails-autoloading-cleaning-up-the-mess/
- http://wondible.com/2011/12/23/give-rails-autoloading-a-boot-to-the-head/
- http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/
Edit: it's a known thing.
回答2:
This solution is similar to undefinedvariable's above, but a bit more DRY.
# organize your sti classes in directories named after the parent
sti_directory_paths = [
Rails.root.join("app","models","subfolder_one"),
Rails.root.join("app","models","subfolder_two"),
]
def require_sti_model(path, filename)
require_dependency (path + filename).to_s
end
# if you use something like guard, just exclude from production
unless Rails.env.production?
sti_directory_paths.each do |sti_directory_path|
Dir[sti_directory_path + "*.rb"].each do |filename|
require_sti_model(sti_directory_path, filename)
ActionDispatch::Reloader.to_prepare do
require_sti_model(sti_directory_path, filename)
end
end
end
end
来源:https://stackoverflow.com/questions/14711505/how-to-reload-files-in-app-models-subdirectory-in-dev-environment-for-sti