class Puma::Configuration

The main configuration class of Puma.

It can be initialized with a set of “user” options and “default” options. Defaults will be merged with ‘Configuration.puma_default_options`.

This class works together with 2 main other classes the ‘UserFileDefaultOptions` which stores configuration options in order so the precedence is that user set configuration wins over “file” based configuration wins over “default” configuration. These configurations are set via the `DSL` class. This class powers the Puma config file syntax and does double duty as a configuration DSL used by the `Puma::CLI` and Puma rack handler.

It also handles loading plugins.

Note:

‘:port` and `:host` are not valid keys. By the time they make it to the configuration options they are expected to be incorporated into a `:binds` key. Under the hood the DSL maps `port` and `host` calls to `:binds`

config = Configuration.new({}) do |user_config, file_config, default_config|
  user_config.port 3003
end
config.clamp
puts config.options[:port]
# => 3003

It is expected that ‘load` is called on the configuration instance after setting config. This method expands any values in `config_file` and puts them into the correct configuration option hash.

Once all configuration is complete it is expected that ‘clamp` will be called on the instance. This will expand any procs stored under “default” values. This is done because an environment variable may have been modified while loading configuration files.

Constants

DEFAULTS

Attributes

events[R]
hooks[R]
plugins[R]

Public Class Methods

new(user_options={}, default_options = {}, env = ENV, &block) click to toggle source
# File lib/puma/configuration.rb, line 179
def initialize(user_options={}, default_options = {}, env = ENV, &block)
  default_options = self.puma_default_options(env).merge(default_options)

  @_options    = UserFileDefaultOptions.new(user_options, default_options)
  @plugins     = PluginLoader.new
  @events      = @_options[:events] || Events.new
  @hooks       = {}
  @user_dsl    = DSL.new(@_options.user_options, self)
  @file_dsl    = DSL.new(@_options.file_options, self)
  @default_dsl = DSL.new(@_options.default_options, self)

  @puma_bundler_pruned = env.key? 'PUMA_BUNDLER_PRUNED'

  if block
    configure(&block)
  end

  @loaded = false
  @clamped = false
end
random_token() click to toggle source
# File lib/puma/configuration.rb, line 371
def self.random_token
  require 'securerandom' unless defined?(SecureRandom)

  SecureRandom.hex(16)
end
temp_path() click to toggle source
# File lib/puma/configuration.rb, line 364
def self.temp_path
  require 'tmpdir'

  t = (Time.now.to_f * 1000).to_i
  "#{Dir.tmpdir}/puma-status-#{t}-#{$$}"
end

Public Instance Methods

app() click to toggle source

Load the specified rackup file, pull options from the rackup file, and set @app.

# File lib/puma/configuration.rb, line 317
def app
  found = options[:app] || load_rackup

  if options[:log_requests]
    require_relative 'commonlogger'
    logger = options[:custom_logger] ? options[:custom_logger] : options[:logger]
    found = CommonLogger.new(found, logger)
  end

  ConfigMiddleware.new(self, found)
end
app_configured?() click to toggle source

Indicate if there is a properly configured app

# File lib/puma/configuration.rb, line 306
def app_configured?
  options[:app] || File.exist?(rackup)
end
clamp() click to toggle source

Call once all configuration (included from rackup files) is loaded to finalize defaults and lock in the configuration.

This also calls load if it hasn’t been called yet.

# File lib/puma/configuration.rb, line 282
def clamp
  load unless @loaded
  set_conditional_default_options
  @_options.finalize_values
  @clamped = true
  warn_hooks
  options
end
config_files() click to toggle source
# File lib/puma/configuration.rb, line 263
def config_files
  raise NotLoadedError, "ensure load is called before accessing config_files" unless @loaded

  files = @_options.all_of(:config_files)

  return [] if files == ['-']
  return files if files.any?

  first_default_file = %W(config/puma/#{@_options[:environment]}.rb config/puma.rb).find do |f|
    File.exist?(f)
  end

  [first_default_file]
end
configure() { |user_dsl, file_dsl, default_dsl| ... } click to toggle source
# File lib/puma/configuration.rb, line 208
def configure
  yield @user_dsl, @file_dsl, @default_dsl
ensure
  @user_dsl._offer_plugins
  @file_dsl._offer_plugins
  @default_dsl._offer_plugins
end
environment() click to toggle source

Return which environment we’re running in

# File lib/puma/configuration.rb, line 330
def environment
  options[:environment]
end
final_options() click to toggle source
# File lib/puma/configuration.rb, line 360
def final_options
  options.final_options
end
flatten() click to toggle source
# File lib/puma/configuration.rb, line 222
def flatten
  dup.flatten!
end
flatten!() click to toggle source
# File lib/puma/configuration.rb, line 226
def flatten!
  @_options = @_options.flatten
  self
end
initialize_copy(other) click to toggle source
# File lib/puma/configuration.rb, line 216
def initialize_copy(other)
  @conf        = nil
  @cli_options = nil
  @_options     = @_options.dup
end
load() click to toggle source
# File lib/puma/configuration.rb, line 257
def load
  @loaded = true
  config_files.each { |config_file| @file_dsl._load_from(config_file) }
  @_options
end
load_plugin(name) click to toggle source
# File lib/puma/configuration.rb, line 334
def load_plugin(name)
  @plugins.create name
end
options() click to toggle source
# File lib/puma/configuration.rb, line 202
def options
  raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped

  @_options
end
puma_default_options(env = ENV) click to toggle source
# File lib/puma/configuration.rb, line 231
def puma_default_options(env = ENV)
  defaults = DEFAULTS.dup
  puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
  defaults
end
puma_options_from_env(env = ENV) click to toggle source
# File lib/puma/configuration.rb, line 237
def puma_options_from_env(env = ENV)
  min = env['PUMA_MIN_THREADS'] || env['MIN_THREADS']
  max = env['PUMA_MAX_THREADS'] || env['MAX_THREADS']
  persistent_timeout = env['PUMA_PERSISTENT_TIMEOUT']
  workers = if env['WEB_CONCURRENCY'] == 'auto'
    require_processor_counter
    ::Concurrent.available_processor_count
  else
    env['WEB_CONCURRENCY']
  end

  {
    min_threads: min && min != "" && Integer(min),
    max_threads: max && max != "" && Integer(max),
    persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout),
    workers: workers && workers != "" && Integer(workers),
    environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'],
  }
end
rackup() click to toggle source
# File lib/puma/configuration.rb, line 310
def rackup
  options[:rackup]
end
run_hooks(key, arg, log_writer, hook_data = nil) click to toggle source

@param key [:Symbol] hook to run @param arg [Launcher, Int] ‘:before_restart` passes Launcher

# File lib/puma/configuration.rb, line 341
def run_hooks(key, arg, log_writer, hook_data = nil)
  log_writer.debug "Running #{key} hooks"

  options.all_of(key).each do |hook_options|
    begin
      block = hook_options[:block]
      if id = hook_options[:id]
        hook_data[id] ||= Hash.new
        block.call arg, hook_data[id]
      else
        block.call arg
      end
    rescue => e
      log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
      log_writer.debug e.backtrace.join("\n")
    end
  end
end

Private Instance Methods

load_rackup() click to toggle source
# File lib/puma/configuration.rb, line 411
def load_rackup
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)

  rack_app, rack_options = rack_builder.parse_file(rackup)
  rack_options = rack_options || {}

  options.file_options.merge!(rack_options)

  config_ru_binds = []
  rack_options.each do |k, v|
    config_ru_binds << v if k.to_s.start_with?("bind")
  end

  options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?

  rack_app
end
rack_builder() click to toggle source

Load and use the normal Rack builder if we can, otherwise fallback to our minimal version.

# File lib/puma/configuration.rb, line 391
def rack_builder
  # Load bundler now if we can so that we can pickup rack from
  # a Gemfile
  if @puma_bundler_pruned
    begin
      require 'bundler/setup'
    rescue LoadError
    end
  end

  begin
    require 'rack'
    require 'rack/builder'
    ::Rack::Builder
  rescue LoadError
    require_relative 'rack/builder'
    Puma::Rack::Builder
  end
end
require_processor_counter() click to toggle source
# File lib/puma/configuration.rb, line 379
    def require_processor_counter
      require 'concurrent/utility/processor_counter'
    rescue LoadError
      warn <<~MESSAGE
        WEB_CONCURRENCY=auto requires the "concurrent-ruby" gem to be installed.
        Please add "concurrent-ruby" to your Gemfile.
      MESSAGE
      raise
    end
set_conditional_default_options() click to toggle source
# File lib/puma/configuration.rb, line 429
def set_conditional_default_options
  @_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
    (@_options[:workers] > 1) && Puma.forkable?
end
warn_hooks() click to toggle source
# File lib/puma/configuration.rb, line 434
    def warn_hooks
      return if options[:workers] > 0
      return if options[:silence_fork_callback_warning]

      log_writer = LogWriter.stdio
      @hooks.each_key do |hook|
        options.all_of(hook).each do |hook_options|
          next unless hook_options[:cluster_only]

          log_writer.log(<<~MSG.tr("\n", " "))
            Warning: The code in the `#{hook}` block will not execute
            in the current Puma configuration. The `#{hook}` block only
            executes in Puma's cluster mode. To fix this, either remove the
            `#{hook}` call or increase Puma's worker count above zero.
          MSG
        end
      end
    end