Better initializer structure for Ruby applications

Initializers are one of the popular patterns used in ruby/rails/sinatra applications to configure a globally used service or library. In most cases its a sinle file located under config/initializers directory and hosts a chunk of code that gets executes on application startup.

Here's an example of sendgrid initializer from rails application:

config = YAML.load_file(Rails.root.join("config/sendgrid.yml")).symbolize_keys

ActionMailer::Base.register_interceptor(SendGrid::MailInterceptor)
ActionMailer::Base.smtp_settings = config[Rails.env]

The problem with initializers is not about the code, but mostly about understanding what's going on in there. Also, testing initializers is not necessarily an easy task since its just a chunk of code that gets loaded on startup. When deploying application for the first time it usually takes some time to get everything configured properly and you'll be getting random errors, most likely related to initializers.

So with a little bit of restructuring you can convert code into a class that does the same thing, but could be easily tested and reused. Example:

class SendgridInitializer
  def initialize(env)
    @env = env
  end

  def run
    require_config
    configure_mailer    
  end

  private

  def config_path
    Rails.root.join("config/sendgrid.yml")
  end

  def config
    YAML.load_file(config_path).symbolize_keys
  end

  def configure_mailer
    ActionMailer::Base.register_interceptor(SendGrid::MailInterceptor)
    ActionMailer::Base.smtp_settings = config[@env]
  end

  def require_config
    unless File.exists?(config_path)
      raise "Sendgrid config file was not found at #{config_path}"
    end
  end
end

And then you just invoke it in initializer file config/initializers/sendgrid.rb:

SendgridInitializer.new(Rails.env).run

Of course this example is pretty simple, but the same approach works the same for more complex initializers (fog initializer, payment gateway initializers, etc) and could be tested the same way as your regular application code (models, services, lib, etc)