Slicedpan
Slicedpan

Reputation: 5015

Rails module autoloading preserve state

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

Answers (2)

Slicedpan
Slicedpan

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

thaleshcv
thaleshcv

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

Related Questions