class Rails::Service

Abstract base class for all 3rd party services

Provides:

- Configuration (automatically loaded from `config/services/[service_name].yml`, available as `config`)
- Logging (to separate log file `log/services/[service_name].log`, call via `logger.info(msg)`)
- Call style invocation (like `PDFGenerationService.(some, params)`)

@example

class Services::PDFGenerationService < Service
  def initialize
    super('pdf_generation')
  end

  def call(action, *args)
     ... here happens the magic! ...
  end
end

PDFGenerationService.(some, params)

@abstract

Attributes

config[R]
logger[R]
service_name[R]

Public Class Methods

call(...) click to toggle source

Allows call syntax on class level: SomeService.(some, args)

# File lib/rails/service.rb, line 124
def self.call(...)
  new.(...)
end
new(service_name = nil) click to toggle source

Constructor. Call this from the subclass with the service name, like `super 'pdf_generator'`. After that you can access the config and logger.

@param [String] service_name Name of the service, like 'pdf_generator'. @raise [NotImplementedError] When this class is tried to be instantiated without subclass.

@raise [RuntimeError] When no service_name is given

# File lib/rails/service.rb, line 40
def initialize(service_name = nil)
  raise NotImplementedError if self.class == Service
  raise 'Please provide a service name!' if service_name.nil?

  @service_name = service_name

  setup_logger
  setup_configuration
end

Public Instance Methods

call(options) click to toggle source

Abstract method for instance call. Implement this in the subclass! @raise [NotImplementedError] When this is not overwritten in the subclass

# File lib/rails/service.rb, line 120
def call(options); end
helpers() click to toggle source

Allows to use rails view helpers

# File lib/rails/service.rb, line 129
def helpers
  ApplicationController.new.helpers
end

Private Instance Methods

load_config_file(path) click to toggle source
# File lib/rails/service.rb, line 96
        def load_config_file(path)
  erb = File.read(path)
  yaml = ERB.new(erb).result.strip

  return {} if yaml.blank?

  YAML.safe_load(yaml) || {}
end
secret(key) click to toggle source

Convenience method to get a secret. It looks for the key `services.<service_name>.<key>`

# File lib/rails/service.rb, line 107
        def secret(key)
  key = key.to_sym
  base = Rails.application.secrets.services[@service_name.to_sym]

  raise "No secrets entry found for 'services.#{@service_name}'" unless base
  raise "No secrets entry found for 'services.#{@service_name}.#{key}'" unless base[key]

  base[key]
end
setup_configuration() click to toggle source

Loads the configuration for that service and saves it to @config @raise [RuntimeError] When the config file doesn't exist

# File lib/rails/service.rb, line 80
        def setup_configuration
  shared_config_path = Rails.root.join('config', 'services', 'shared.yml')
  config_path = Rails.root.join('config', 'services', "#{@service_name}.yml")
  raise "Couldn't find the shared config file '#{shared_config_path}'." unless File.exist?(shared_config_path)

  shared_config = load_config_file(shared_config_path)

  if File.exist?(config_path)
    service_config = load_config_file(config_path) || {}
    @config = shared_config.merge(service_config)
  else
    @config = shared_config
  end
end
setup_logger() click to toggle source

Create the log file and sets @logger

# File lib/rails/service.rb, line 52
        def setup_logger
  if ENV['SERVICE_LOGGER_STDOUT']
    setup_stdout_logger
  else
    log_path = Rails.root.join('log', 'services')
    FileUtils.mkdir_p(log_path) unless Dir.exist?(log_path)

    log_file = log_path.join("#{@service_name}.log").to_s

    FileUtils.touch log_file
    @logger = Logger.new(log_file)
  end
end
setup_stdout_logger() click to toggle source

Will setup the logger for logging to STDOUT. This can be useful for Heroku for example.

# File lib/rails/service.rb, line 69
        def setup_stdout_logger
  @logger = Logger.new(STDOUT)

  @logger.formatter = proc do |severity, datetime, progname, msg|
    "[#{@service_name}] #{msg}"
  end
end