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
DSLmaps `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
Public Class Methods
# 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
# File lib/puma/configuration.rb, line 367 def self.random_token require 'securerandom' unless defined?(SecureRandom) SecureRandom.hex(16) end
# File lib/puma/configuration.rb, line 360 def self.temp_path require 'tmpdir' t = (Time.now.to_f * 1000).to_i "#{Dir.tmpdir}/puma-status-#{t}-#{$$}" end
Public Instance Methods
Load the specified rackup file, pull options from the rackup file, and set @app.
# File lib/puma/configuration.rb, line 313 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
Indicate if there is a properly configured app
# File lib/puma/configuration.rb, line 302 def app_configured? options[:app] || File.exist?(rackup) end
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 278 def clamp load unless @loaded set_conditional_default_options @_options.finalize_values @clamped = true warn_hooks options end
# File lib/puma/configuration.rb, line 259 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
# 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
Return which environment we’re running in
# File lib/puma/configuration.rb, line 326 def environment options[:environment] end
# File lib/puma/configuration.rb, line 356 def final_options options.final_options end
# File lib/puma/configuration.rb, line 222 def flatten dup.flatten! end
# File lib/puma/configuration.rb, line 226 def flatten! @_options = @_options.flatten self end
# File lib/puma/configuration.rb, line 216 def initialize_copy(other) @conf = nil @cli_options = nil @_options = @_options.dup end
# File lib/puma/configuration.rb, line 253 def load @loaded = true config_files.each { |config_file| @file_dsl._load_from(config_file) } @_options end
# File lib/puma/configuration.rb, line 330 def load_plugin(name) @plugins.create name end
# File lib/puma/configuration.rb, line 202 def options raise NotClampedError, "ensure clamp is called before accessing options" unless @clamped @_options end
# 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
# 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_env = env['WEB_CONCURRENCY'] workers = workers_env && workers_env.strip != "" ? parse_workers(workers_env.strip) : nil { min_threads: min && min != "" && Integer(min), max_threads: max && max != "" && Integer(max), persistent_timeout: persistent_timeout && persistent_timeout != "" && Integer(persistent_timeout), workers: workers, environment: env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV'], } end
# File lib/puma/configuration.rb, line 306 def rackup options[:rackup] end
@param key [:Symbol] hook to run @param arg [Launcher, Int] ‘:before_restart` passes Launcher
# File lib/puma/configuration.rb, line 337 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
# File lib/puma/configuration.rb, line 418 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
# File lib/puma/configuration.rb, line 385 def parse_workers(value) if value == :auto || value == 'auto' require_processor_counter Integer(::Concurrent.available_processor_count) else Integer(value) end rescue ArgumentError, TypeError raise ArgumentError, "workers must be an Integer or :auto" end
Load and use the normal Rack builder if we can, otherwise fallback to our minimal version.
# File lib/puma/configuration.rb, line 398 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
# File lib/puma/configuration.rb, line 375 def require_processor_counter require 'concurrent/utility/processor_counter' rescue LoadError warn <<~MESSAGE WEB_CONCURRENCY=auto or workers(:auto) requires the "concurrent-ruby" gem to be installed. Please add "concurrent-ruby" to your Gemfile. MESSAGE raise end
# File lib/puma/configuration.rb, line 436 def set_conditional_default_options @_options.default_options[:preload_app] = !@_options[:prune_bundler] && (@_options[:workers] > 1) && Puma.forkable? end
# File lib/puma/configuration.rb, line 441 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