Class: Apartment::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/apartment/config.rb

Overview

Configuration object for Apartment v4. Created via Apartment.configure block; validated after the block yields.

Constant Summary collapse

VALID_STRATEGIES =

rubocop:disable Metrics/ClassLength

%i[schema database_name shard database_config].freeze
VALID_ENVIRONMENTIFY_STRATEGIES =
[nil, :prepend, :append].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfig

rubocop:disable Metrics/AbcSize



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/apartment/config.rb', line 30

def initialize # rubocop:disable Metrics/AbcSize
  @tenant_strategy = nil
  @tenants_provider = nil
  @default_tenant = nil
  @default_tenant_switch_allowed = true
  @excluded_models = []
  @tenant_pool_size = 5
  @pool_idle_timeout = 300
  @max_total_connections = nil
  @seed_after_create = false
  @seed_data_file = nil
  @schema_load_strategy = nil
  @schema_file = nil
  @parallel_migration_threads = 0
  @environmentify_strategy = nil
  @elevator = nil
  @elevator_options = {}
  @tenant_not_found_handler = nil
  @tenant_validator = nil
  @active_record_log = false
  @sql_query_tags = false
  @postgres_config = nil
  @mysql_config = nil
  @shard_key_prefix = 'apartment'
  @migration_role = nil
  @app_role = nil
  @schema_cache_per_tenant = false
  @check_pending_migrations = true
  @force_separate_pinned_pool = false
  @test_fixture_cleanup = true
end

Instance Attribute Details

#active_record_logObject

Returns the value of attribute active_record_log.



17
18
19
# File 'lib/apartment/config.rb', line 17

def active_record_log
  @active_record_log
end

#app_roleObject

Returns the value of attribute app_role.



17
18
19
# File 'lib/apartment/config.rb', line 17

def app_role
  @app_role
end

#check_pending_migrationsObject

Returns the value of attribute check_pending_migrations.



17
18
19
# File 'lib/apartment/config.rb', line 17

def check_pending_migrations
  @check_pending_migrations
end

#default_tenantObject

Returns the value of attribute default_tenant.



17
18
19
# File 'lib/apartment/config.rb', line 17

def default_tenant
  @default_tenant
end

#default_tenant_switch_allowedObject

Returns the value of attribute default_tenant_switch_allowed.



17
18
19
# File 'lib/apartment/config.rb', line 17

def default_tenant_switch_allowed
  @default_tenant_switch_allowed
end

#elevatorObject

Returns the value of attribute elevator.



17
18
19
# File 'lib/apartment/config.rb', line 17

def elevator
  @elevator
end

#elevator_optionsObject

Returns the value of attribute elevator_options.



17
18
19
# File 'lib/apartment/config.rb', line 17

def elevator_options
  @elevator_options
end

#environmentify_strategyObject

Returns the value of attribute environmentify_strategy.



14
15
16
# File 'lib/apartment/config.rb', line 14

def environmentify_strategy
  @environmentify_strategy
end

#excluded_modelsObject

Returns the value of attribute excluded_models.



14
15
16
# File 'lib/apartment/config.rb', line 14

def excluded_models
  @excluded_models
end

#force_separate_pinned_poolObject

Returns the value of attribute force_separate_pinned_pool.



17
18
19
# File 'lib/apartment/config.rb', line 17

def force_separate_pinned_pool
  @force_separate_pinned_pool
end

#max_total_connectionsObject

Returns the value of attribute max_total_connections.



17
18
19
# File 'lib/apartment/config.rb', line 17

def max_total_connections
  @max_total_connections
end

#migration_roleObject

Returns the value of attribute migration_role.



17
18
19
# File 'lib/apartment/config.rb', line 17

def migration_role
  @migration_role
end

#mysql_configObject (readonly)

Returns the value of attribute mysql_config.



14
15
16
# File 'lib/apartment/config.rb', line 14

def mysql_config
  @mysql_config
end

#parallel_migration_threadsObject

Returns the value of attribute parallel_migration_threads.



17
18
19
# File 'lib/apartment/config.rb', line 17

def parallel_migration_threads
  @parallel_migration_threads
end

#pool_idle_timeoutObject

Returns the value of attribute pool_idle_timeout.



17
18
19
# File 'lib/apartment/config.rb', line 17

def pool_idle_timeout
  @pool_idle_timeout
end

#postgres_configObject (readonly)

Returns the value of attribute postgres_config.



14
15
16
# File 'lib/apartment/config.rb', line 14

def postgres_config
  @postgres_config
end

#schema_cache_per_tenantObject

Returns the value of attribute schema_cache_per_tenant.



17
18
19
# File 'lib/apartment/config.rb', line 17

def schema_cache_per_tenant
  @schema_cache_per_tenant
end

#schema_fileObject

Returns the value of attribute schema_file.



17
18
19
# File 'lib/apartment/config.rb', line 17

def schema_file
  @schema_file
end

#schema_load_strategyObject

Returns the value of attribute schema_load_strategy.



17
18
19
# File 'lib/apartment/config.rb', line 17

def schema_load_strategy
  @schema_load_strategy
end

#seed_after_createObject

Returns the value of attribute seed_after_create.



17
18
19
# File 'lib/apartment/config.rb', line 17

def seed_after_create
  @seed_after_create
end

#seed_data_fileObject

Returns the value of attribute seed_data_file.



17
18
19
# File 'lib/apartment/config.rb', line 17

def seed_data_file
  @seed_data_file
end

#shard_key_prefixObject

Returns the value of attribute shard_key_prefix.



17
18
19
# File 'lib/apartment/config.rb', line 17

def shard_key_prefix
  @shard_key_prefix
end

#sql_query_tagsObject

Returns the value of attribute sql_query_tags.



17
18
19
# File 'lib/apartment/config.rb', line 17

def sql_query_tags
  @sql_query_tags
end

#tenant_not_found_handlerObject

Returns the value of attribute tenant_not_found_handler.



17
18
19
# File 'lib/apartment/config.rb', line 17

def tenant_not_found_handler
  @tenant_not_found_handler
end

#tenant_pool_sizeObject

Returns the value of attribute tenant_pool_size.



17
18
19
# File 'lib/apartment/config.rb', line 17

def tenant_pool_size
  @tenant_pool_size
end

#tenant_strategyObject

Returns the value of attribute tenant_strategy.



14
15
16
# File 'lib/apartment/config.rb', line 14

def tenant_strategy
  @tenant_strategy
end

#tenant_validatorObject

Returns the value of attribute tenant_validator.



17
18
19
# File 'lib/apartment/config.rb', line 17

def tenant_validator
  @tenant_validator
end

#tenants_providerObject

Returns the value of attribute tenants_provider.



17
18
19
# File 'lib/apartment/config.rb', line 17

def tenants_provider
  @tenants_provider
end

#test_fixture_cleanupObject

Returns the value of attribute test_fixture_cleanup.



17
18
19
# File 'lib/apartment/config.rb', line 17

def test_fixture_cleanup
  @test_fixture_cleanup
end

Instance Method Details

#apply_defaults!Object

Apply derived defaults that depend on user-set values. Runs after the configure block yields and before validate!, so validate! stays read-only.



117
118
119
120
# File 'lib/apartment/config.rb', line 117

def apply_defaults!
  # PostgreSQL's default schema is 'public'; avoid forcing every user to set it.
  @default_tenant ||= 'public' if @tenant_strategy == :schema
end

#configure_mysql {|@mysql_config| ... } ⇒ Object

Configure MySQL-specific options via block.

Yields:



97
98
99
100
101
# File 'lib/apartment/config.rb', line 97

def configure_mysql
  @mysql_config = Configs::MysqlConfig.new
  yield(@mysql_config) if block_given?
  @mysql_config
end

#configure_postgres {|@postgres_config| ... } ⇒ Object

Configure PostgreSQL-specific options via block.

Yields:



90
91
92
93
94
# File 'lib/apartment/config.rb', line 90

def configure_postgres
  @postgres_config = Configs::PostgresqlConfig.new
  yield(@postgres_config) if block_given?
  @postgres_config
end

#freeze!Object

Deep-freeze the config after validation to prevent post-boot mutation. Freezes mutable collections and sub-configs, then freezes self.



105
106
107
108
109
110
111
112
113
# File 'lib/apartment/config.rb', line 105

def freeze!
  @excluded_models.freeze
  @elevator_options.freeze
  @postgres_config&.freeze!
  @mysql_config&.freeze!
  # schema_file is a simple string, no deep freeze needed
  @app_role.freeze if @app_role.is_a?(String)
  freeze
end

#rails_env_nameObject

Returns the current Rails environment name, falling back to env vars and a safe default.



213
214
215
# File 'lib/apartment/config.rb', line 213

def rails_env_name
  (Rails.env if defined?(Rails.env)) || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default_env'
end

#validate!Object

Validate configuration completeness and consistency. Raises ConfigurationError on invalid state.

Raises:



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/apartment/config.rb', line 124

def validate! # rubocop:disable Metrics/AbcSize
  raise(ConfigurationError, 'tenant_strategy is required') unless @tenant_strategy

  if @default_tenant.is_a?(String) && @default_tenant.strip.empty?
    raise(ConfigurationError, 'default_tenant cannot be an empty string')
  end

  unless [true, false].include?(@default_tenant_switch_allowed)
    raise(ConfigurationError,
          'default_tenant_switch_allowed must be true or false, got: ' \
          "#{@default_tenant_switch_allowed.inspect}")
  end

  unless @tenants_provider.respond_to?(:call)
    raise(ConfigurationError, 'tenants_provider must be a callable (e.g., -> { Tenant.pluck(:name) })')
  end

  if @postgres_config && @mysql_config
    raise(ConfigurationError, 'Cannot configure both Postgres and MySQL at the same time')
  end

  @postgres_config&.validate!

  unless @tenant_pool_size.is_a?(Integer) && @tenant_pool_size.positive?
    raise(ConfigurationError, "tenant_pool_size must be a positive integer, got: #{@tenant_pool_size.inspect}")
  end

  unless @pool_idle_timeout.is_a?(Numeric) && @pool_idle_timeout.positive?
    raise(ConfigurationError, "pool_idle_timeout must be a positive number, got: #{@pool_idle_timeout.inspect}")
  end

  if @max_total_connections && (!@max_total_connections.is_a?(Integer) || @max_total_connections < 1)
    raise(ConfigurationError,
          "max_total_connections must be a positive integer or nil, got: #{@max_total_connections.inspect}")
  end

  unless [nil, :schema_rb, :sql].include?(@schema_load_strategy)
    raise(ConfigurationError, "Invalid schema_load_strategy: #{@schema_load_strategy.inspect}. " \
                              'Must be nil, :schema_rb, or :sql')
  end

  if @migration_role && !@migration_role.is_a?(Symbol)
    raise(ConfigurationError, "migration_role must be nil or a Symbol, got: #{@migration_role.inspect}")
  end

  if @app_role && !@app_role.is_a?(String) && !@app_role.respond_to?(:call)
    raise(ConfigurationError, "app_role must be nil, a String, or a callable, got: #{@app_role.inspect}")
  end

  unless [true, false].include?(@schema_cache_per_tenant)
    raise(ConfigurationError,
          "schema_cache_per_tenant must be true or false, got: #{@schema_cache_per_tenant.inspect}")
  end

  unless [true, false].include?(@check_pending_migrations)
    raise(ConfigurationError,
          "check_pending_migrations must be true or false, got: #{@check_pending_migrations.inspect}")
  end

  unless [true, false].include?(@force_separate_pinned_pool)
    raise(ConfigurationError,
          "force_separate_pinned_pool must be true or false, got: #{@force_separate_pinned_pool.inspect}")
  end

  unless [true, false].include?(@test_fixture_cleanup)
    raise(ConfigurationError,
          "test_fixture_cleanup must be true or false, got: #{@test_fixture_cleanup.inspect}")
  end

  unless @tenant_validator.nil? || @tenant_validator == false || @tenant_validator.respond_to?(:call)
    raise(ConfigurationError,
          'tenant_validator must be nil, false, or a callable, ' \
          "got: #{@tenant_validator.inspect}")
  end

  if @tenant_not_found_handler && !@tenant_not_found_handler.respond_to?(:call)
    raise(ConfigurationError,
          'tenant_not_found_handler must be nil or a callable, ' \
          "got: #{@tenant_not_found_handler.inspect}")
  end

  return if @shard_key_prefix.is_a?(String) && @shard_key_prefix.match?(/\A[a-z_][a-z0-9_]*\z/)

  raise(ConfigurationError,
        'shard_key_prefix must be a lowercase string matching /[a-z_][a-z0-9_]*/, ' \
        "got: #{@shard_key_prefix.inspect}")
end