Reputation: 5015
I have set up a couple of modules that provide similar functionality, and I have another module that collects all of these, so that at runtime it is possible to determine what functionality is available.
This works fine, but whenever the code gets reloaded, Rails' autoloading functionality clobbers the instance variables on the collection module.
The code looks something like this:
module ServiceCollection
def self.available_services
@available_services ||= []
end
end
module ServiceProvider
extend ActiveSupport::Concern
included do |includer|
ServiceCollection.available_services.push(includer)
end
end
module MyService
include ServiceProvider
#some functionality here
end
Calling ServiceCollection.available_services
will return the list of modules that include ServiceProvider
, however on reload, the instance variable @available_services
will be reset, and subsequent calls return an empty array.
Is there an easy way to get around this?
Upvotes: 2
Views: 84
Reputation: 5015
Thanks to @thaleshcv for the answer, but I decided to go a different route, partially inspired by his answer.
I changed the implementation of ServiceCollection
to something like this:
module ServiceCollection
def self.available_services
Dir[Rails.root.join("app", "services", "*.rb")].each do |filename|
model_name = Pathname.new(filename.to_s).basename.to_s.chomp('.rb').camelcase
begin
Module.const_get(model_name)
rescue Exception => e
#Log something or whatever
end
end
@available_services ||= []
end
end
Calling Module.const_get
triggers Rails' autoloading functionality. Assuming that I follow the module/class/filename naming convention, then this should work fine. Here's a gist that extracts this functionality into a module
Upvotes: 0
Reputation: 752
You can force the load of the modules putting a require
call in your application.rb file.
Something like:
Dir["#{File.expand_path('../..', __FILE__)}/extras/*.rb"].each { |rb| require rb }
Upvotes: 1