I'm trying to cache a class list and I'm wondering how it is supposed to be done with zeitwerk.
I obtain the class list like:
def models
return @models if @models
Zeitwerk::Loader.eager_load_all # this is to see all models in dev env, otherwise some may not be yet loaded, hence not present in the list
@models = ActiveRecord::Base.descendants.select { |model| model.include?(SomeModule) }
end
But then any moment zeitwerk may reload the classes and invalidate content of @model
. This is my understanding.
So how am I supposed to have a cache of classes in a Rails dev environment?
Bonus question, how do I eager load a single directory programatically? That is to load only what is needed to be loaded instead of the whole app.
P.S. This class list will only be used in specific rake
tasks and in tests, not in production runtime code. Just saying to avoid complaints that this should not be done.
Update (just illustration of how it's done):
class ModelFoo
annotated
...
end
class ModelBar
annotated
...
end
module Annotating
extend ActiveSupport::Concern
module Model
extend ActiveSupport::Concern
included do
has_many :annotations, as: :annotated, dependent: :destroy, autosave: true
end
...
end
class_methods do
def annotated
class_eval do
include Model
end
end
end
class << self
def models
<see above the body of this method>
end
end
end
ActiveRecord::Base.include(Annotating::Model)
Later use Annotating.models
to generate a trigger for all annotated models and for testing all annotated models with some standard operations.
To eager load a directory in a way that works well after reloads, do this:
# config/initializers/eager_load_foo.rb
# Run when the application boots, and on each reload.
Rails.application.config.to_prepare do
Rails.autoloaders.main.eager_load_dir("#{Rails.root}/my/dir")
end
That needs Zeitwerk 2.6.2 or later (docs). Zeitwerk is also able to eager load namespaces if that would match better your use case (docs).
That won't conflict in production, because eager loading is idempotent (but compatible with reloading, as the example above shows).