class Hanami::Settings
Provides user-defined settings for an Hanami
app or slice.
Define your own settings by inheriting from this class in ‘config/settings.rb` within an app or slice. Your settings will be loaded from matching ENV vars (with upper-cased names) and be registered as a component as part of the Hanami
app {Hanami::Slice::ClassMethods#prepare prepare} step.
The settings instance is registered in your app and slice containers as a ‘“settings”` component. You can use the `Deps` mixin to inject this dependency and make settings available to your other components as required.
@example
# config/settings.rb # frozen_string_literal: true module MyApp class Settings < Hanami::Settings Secret = Types::String.constrained(min_size: 20) setting :database_url, constructor: Types::String setting :session_secret, constructor: Secret setting :some_flag, default: false, constructor: Types::Params::Bool end end
Settings
are defined with [dry-configurable]‘s `setting` method. You may likely want to provide `default:` and `constructor:` options for your settings.
If you have [dry-types] bundled, then a nested ‘Types` module will be available for type checking your setting values. Pass type objects to the setting `constructor:` options to ensure their values meet your type expectations. You can use dry-types’ default type objects or define your own.
When the settings are initialized, all type errors will be collected and presented together for correction. Settings
are loaded early, as part of the Hanami
app’s {Hanami::Slice::ClassMethods#prepare prepare} step, to ensure that the app boots only when valid settings are present.
Setting values are loaded from a configurable store, which defaults to {Hanami::Settings::EnvStore}, which fetches the values from equivalent upper-cased keys in ‘ENV`. You can configure an alternative store via {Hanami::Config#settings_store}. Setting stores must implement a `#fetch` method with the same signature as `Hash#fetch`.
[dry-c]: dry-rb.org/gems/dry-configurable/ [dry-t]: dry-rb.org/gems/dry-types/
@see Hanami::Settings::DotenvStore
@api public @since 2.0.0
Constants
- EMPTY_STORE
@api private
- Undefined
@api private
Public Class Methods
Defines a nested ‘Types` constant in `Settings` subclasses if dry-types is bundled.
@see dry-rb.org/gems/dry-types
@api private
# File lib/hanami/settings.rb, line 93 def inherited(subclass) super if Hanami.bundled?("dry-types") require "dry/types" subclass.const_set(:Types, Dry.Types()) end end
Loads the settings for a slice.
Returns nil if no settings class is defined.
@return [Settings, nil]
@api private
# File lib/hanami/settings.rb, line 109 def load_for_slice(slice) return unless settings_defined?(slice) require_slice_settings(slice) unless slice_settings_class?(slice) slice_settings_class(slice).new(slice.config.settings_store) end
@api private
# File lib/hanami/settings.rb, line 158 def initialize(store = EMPTY_STORE) errors = config._settings.map(&:name).each_with_object({}) do |name, errs| value = store.fetch(name, Undefined) if value.eql?(Undefined) # When a key is missing entirely from the store, _read_ its value from the config instead. # This ensures its setting constructor runs (with a `nil` argument given) and raises any # necessary errors. public_send(name) else public_send("#{name}=", value) end rescue => e # rubocop:disable Style/RescueStandardError errs[name] = e end raise InvalidSettingsError, errors if errors.any? config.finalize! end
Private Class Methods
# File lib/hanami/settings.rb, line 136 def require_slice_settings(slice) require "hanami/settings" slice_settings_require_path = File.join(slice.root, SETTINGS_PATH) begin require slice_settings_require_path rescue LoadError => e raise e unless e.path == slice_settings_require_path end end
Returns true if settings are defined for the slice.
Settings
are considered defined if a ‘Settings` class is already defined in the slice namespace, or a `config/settings.rb` exists under the slice root.
# File lib/hanami/settings.rb, line 123 def settings_defined?(slice) slice.namespace.const_defined?(SETTINGS_CLASS_NAME) || slice.root.join("#{SETTINGS_PATH}#{RB_EXT}").file? end
# File lib/hanami/settings.rb, line 132 def slice_settings_class(slice) slice.namespace.const_get(SETTINGS_CLASS_NAME) end
# File lib/hanami/settings.rb, line 128 def slice_settings_class?(slice) slice.namespace.const_defined?(SETTINGS_CLASS_NAME) end
Public Instance Methods
Returns a string containing a human-readable representation of the settings.
This includes setting names only, not any values, to ensure that sensitive values do not inadvertently leak.
Use {#inspect_values} to inspect settings with their values.
@example
settings.inspect # => #<MyApp::Settings [database_url, session_secret, some_flag]>
@return [String]
@see inspect_values
@api public @since 2.0.0
# File lib/hanami/settings.rb, line 196 def inspect "#<#{self.class} [#{config._settings.map(&:name).join(", ")}]>" end
Returns a string containing a human-readable representation of the settings and their values.
@example
settings.inspect_values # => #<MyApp::Settings database_url="postgres://localhost/my_db", session_secret="xxx", some_flag=true]>
@return [String]
@see inspect
@api public @since 2.0.0
# File lib/hanami/settings.rb, line 214 def inspect_values "#<#{self.class} #{config._settings.map { |setting| "#{setting.name}=#{config[setting.name].inspect}" }.join(" ")}>" end
Private Instance Methods
rubocop:enable Layout/LineLength
# File lib/hanami/settings.rb, line 222 def method_missing(name, *args, &block) if config.respond_to?(name) config.send(name, *args, &block) else super end end
# File lib/hanami/settings.rb, line 230 def respond_to_missing?(name, _include_all = false) config.respond_to?(name) || super end