Module: Otto::Core::Configuration

Includes:
Freezable
Included in:
Otto
Defined in:
lib/otto/core/configuration.rb

Overview

Configuration module providing locale and application configuration methods

Instance Method Summary collapse

Methods included from Freezable

#deep_freeze!

Instance Method Details

#configure(available_locales: nil, default_locale: nil) ⇒ Object

Configure locale settings for the application

Examples:

otto.configure(
  available_locales: { 'en' => 'English', 'es' => 'Spanish', 'fr' => 'French' },
  default_locale: 'en'
)

Parameters:

  • available_locales (Hash) (defaults to: nil)

    Hash of available locales (e.g., { ‘en’ => ‘English’, ‘es’ => ‘Spanish’ })

  • default_locale (String) (defaults to: nil)

    Default locale to use as fallback



100
101
102
103
104
105
106
107
108
109
# File 'lib/otto/core/configuration.rb', line 100

def configure(available_locales: nil, default_locale: nil)
  ensure_not_frozen!

  # Initialize locale_config if not already set
  @locale_config ||= Otto::Locale::Config.new

  # Update configuration
  @locale_config.available_locales = available_locales if available_locales
  @locale_config.default_locale = default_locale if default_locale
end

#configure_auth_strategies(strategies, default_strategy: 'noauth') ⇒ Object

Configure authentication strategies for route-level access control.

Examples:

otto.configure_auth_strategies({
  'noauth' => Otto::Security::Authentication::Strategies::NoAuthStrategy.new,
  'authenticated' => Otto::Security::Authentication::Strategies::SessionStrategy.new(session_key: 'user_id'),
  'role:admin' => Otto::Security::Authentication::Strategies::RoleStrategy.new(['admin']),
  'api_key' => Otto::Security::Authentication::Strategies::APIKeyStrategy.new(api_keys: ['secret123'])
})

Parameters:

  • strategies (Hash)

    Hash mapping strategy names to strategy instances

  • default_strategy (String) (defaults to: 'noauth')

    Default strategy to use when none specified



140
141
142
143
144
145
# File 'lib/otto/core/configuration.rb', line 140

def configure_auth_strategies(strategies, default_strategy: 'noauth')
  ensure_not_frozen!
  # Update existing @auth_config rather than creating a new one
  @auth_config[:auth_strategies] = strategies
  @auth_config[:default_auth_strategy] = default_strategy
end

#configure_authentication(opts) ⇒ Object



66
67
68
69
70
71
72
73
# File 'lib/otto/core/configuration.rb', line 66

def configure_authentication(opts)
  # Update existing @auth_config rather than creating a new one
  # to maintain synchronization with the configurator
  @auth_config[:auth_strategies] = opts[:auth_strategies] if opts[:auth_strategies]
  @auth_config[:default_auth_strategy] = opts[:default_auth_strategy] if opts[:default_auth_strategy]

  # No-op: authentication strategies are configured via @auth_config above
end

#configure_locale(opts) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/otto/core/configuration.rb', line 18

def configure_locale(opts)
  # Check if we have any locale configuration
  has_direct_options = opts[:available_locales] || opts[:default_locale]
  has_legacy_config = opts[:locale_config]

  # Only create locale_config if we have configuration
  return unless has_direct_options || has_legacy_config

  # Initialize with direct options
  available_locales = opts[:available_locales]
  default_locale = opts[:default_locale]

  # Legacy support: Configure locale if provided via locale_config hash
  if opts[:locale_config]
    locale_opts = opts[:locale_config]
    available_locales ||= locale_opts[:available_locales] || locale_opts[:available]
    default_locale ||= locale_opts[:default_locale] || locale_opts[:default]
  end

  # Create Otto::Locale::Config instance
  @locale_config = Otto::Locale::Config.new(
    available_locales: available_locales,
    default_locale: default_locale
  )
end

#configure_mcp(opts) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/otto/core/configuration.rb', line 75

def configure_mcp(opts)
  @mcp_server = nil

  # Enable MCP if requested in options
  return unless opts[:mcp_enabled] || opts[:mcp_http] || opts[:mcp_stdio]

  @mcp_server = Otto::MCP::Server.new(self)

  mcp_options = {}
  mcp_options[:http_endpoint] = opts[:mcp_endpoint] if opts[:mcp_endpoint]

  return unless opts[:mcp_http] != false # Default to true unless explicitly disabled

  @mcp_server.enable!(mcp_options)
end

#configure_rate_limiting(config) ⇒ Object

Configure rate limiting settings.

Examples:

otto.configure_rate_limiting({
  requests_per_minute: 50,
  custom_rules: {
    'api_calls' => { limit: 30, period: 60, condition: ->(req) { req.path.start_with?('/api') }}
  }
})

Parameters:

  • config (Hash)

    Rate limiting configuration

Options Hash (config):

  • :requests_per_minute (Integer)

    Maximum requests per minute per IP

  • :custom_rules (Hash)

    Hash of custom rate limiting rules

  • :cache_store (Object)

    Custom cache store for rate limiting



124
125
126
127
# File 'lib/otto/core/configuration.rb', line 124

def configure_rate_limiting(config)
  ensure_not_frozen!
  @security_config.rate_limiting_config.merge!(config)
end

#configure_security(opts) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/otto/core/configuration.rb', line 44

def configure_security(opts)
  # Enable CSRF protection if requested
  enable_csrf_protection! if opts[:csrf_protection]

  # Enable request validation if requested
  enable_request_validation! if opts[:request_validation]

  # Enable rate limiting if requested
  if opts[:rate_limiting]
    rate_limiting_opts = opts[:rate_limiting].is_a?(Hash) ? opts[:rate_limiting] : {}
    enable_rate_limiting!(rate_limiting_opts)
  end

  # Add trusted proxies if provided
  Array(opts[:trusted_proxies]).each { |proxy| add_trusted_proxy(proxy) } if opts[:trusted_proxies]

  # Set custom security headers
  return unless opts[:security_headers]

  set_security_headers(opts[:security_headers])
end

#ensure_not_frozen!Object

Ensure configuration is not frozen before allowing mutations

Raises:

  • (FrozenError)

    if configuration is frozen



202
203
204
# File 'lib/otto/core/configuration.rb', line 202

def ensure_not_frozen!
  raise FrozenError, 'Cannot modify frozen configuration' if frozen_configuration?
end

#freeze_configuration!self

Freeze the application configuration to prevent runtime modifications. Called automatically at the end of initialization to ensure immutability.

This prevents security-critical configuration from being modified after the application begins handling requests. Uses deep freezing to prevent both direct modification and modification through nested structures.

Returns:

  • (self)

Raises:

  • (RuntimeError)

    if configuration is already frozen



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/otto/core/configuration.rb', line 156

def freeze_configuration!
  if frozen_configuration?
    Otto.structured_log(:debug, 'Configuration already frozen', { status: 'skipped' }) if Otto.debug
    return self
  end

  start_time = Otto::Utils.now_in_μs

  # Deep freeze configuration objects with memoization support
  @security_config.deep_freeze! if @security_config.respond_to?(:deep_freeze!)
  @locale_config.deep_freeze! if @locale_config.respond_to?(:deep_freeze!)
  @middleware.deep_freeze! if @middleware.respond_to?(:deep_freeze!)

  # Deep freeze configuration hashes (recursively freezes nested structures)
  deep_freeze_value(@auth_config) if @auth_config
  deep_freeze_value(@option) if @option

  # Deep freeze route structures (prevent modification of nested hashes/arrays)
  deep_freeze_value(@routes) if @routes
  deep_freeze_value(@routes_literal) if @routes_literal
  deep_freeze_value(@routes_static) if @routes_static
  deep_freeze_value(@route_definitions) if @route_definitions

  @configuration_frozen = true

  duration = Otto::Utils.now_in_μs - start_time
  frozen_objects = %w[security_config locale_config middleware auth_config option routes]
  Otto.structured_log(:info, 'Freezing completed',
    {
            duration: duration,
      frozen_objects: frozen_objects.join(','),
    })

  self
end

#frozen_configuration?Boolean

Check if configuration is frozen

Returns:

  • (Boolean)

    true if configuration is frozen



195
196
197
# File 'lib/otto/core/configuration.rb', line 195

def frozen_configuration?
  @configuration_frozen == true
end

#middleware_enabled?(middleware_class) ⇒ Boolean

Returns:

  • (Boolean)


206
207
208
209
# File 'lib/otto/core/configuration.rb', line 206

def middleware_enabled?(middleware_class)
  # Only check the new middleware stack as the single source of truth
  @middleware&.includes?(middleware_class)
end