Search code examples
ruby-on-railsrubyruby-on-rails-5rails-console

Rails reload! resets class variables, need to rerun some initializers


Suppose I need to parse some configuration to instanciate some Service Singletons (that could be used with or without Rails).

A sample code example:

#services/my_service.rb
module MyService
    @config = nil

    def self.load_config(config)
      @config = config
    end

When using with Rail (or Capistrano, SInatra, etc.) I would use an initializer to boot up the service

#initializers/svc.rb
MyService.load_config(Rails.application.secrets.my_service.credentials)

But when used specifically with Rails, on every rails console restart!, this @config variable is cleared which is a problem...

Are there

  • after-reload! hooks that I could use to re-run the initializer ?
  • other types of variables that would be preserved during a restart! that I could use here ?

Solution

  • Seeing that people are still reading this, here is implementation I ended up with

    # config/initializers/0_service_activation.rb
    # Activation done at the end of file
    
    module ServiceActivation
      def self.with_reload
        ActiveSupport::Reloader.to_prepare do
          yield
        end
      end
    
      module Slack
        def self.service
          ::SlackConnector
        end
    
        def self.should_be_activated?
          Utility.production? ||
          Utility.staging? ||
          (
            Utility.development? &&
            ENV['ENABLE_SLACK'] == 'true'
          )
        end
    
        def self.activate
          slack = service
          slack.webhook = Rails.application.secrets.slack&.dig(:slack_webhooks)
          Rails.application.secrets&.dig(:slack, :intercept_channel).try do |channel|
            slack.intercept_channel = channel if channel.present?
          end
          slack.activate
          slack
        end
      end
    
    [
      ServiceActivation::Intercom,
      ServiceActivation::Slack,
      ServiceActivation::Google
    ] .each do |activator|
      ServiceActivation.with_reload do
        activator.activate if activator.should_be_activated?
        activator.service.status_report
      end
    end
    

    I am not showing my connector class SlackConnector, but basically you can probably guess the interface from the way it is called. You need to set the webhook url, and do other stuff. The implementation is decoupled, so it's possible to use the same SlackConnector in Rails and in Capistrano for deployment, so it's basically in the lib/ folder