Module: Familia::Settings
- Included in:
- Familia, DataType, Horreum::DefinitionMethods
- Defined in:
- lib/familia/settings.rb
Overview
Familia::Settings
Instance Attribute Summary collapse
- #current_key_version(val = nil) ⇒ Object
- #default_expiration(val = nil) ⇒ Object
- #delim(val = nil) ⇒ Object
-
#encryption_hkdf_salt(val = nil) ⇒ String
HKDF salt for the AES-GCM provider's key derivation (RFC 5869).
-
#encryption_hkdf_salt_history(val = nil) ⇒ Array<String>
Previous #encryption_hkdf_salt values, kept so that rotating the AES-GCM HKDF salt stays backward-compatible.
- #encryption_keys(val = nil) ⇒ Object
-
#encryption_personalization(val = nil) ⇒ String
Personalization string for BLAKE2b key derivation in the XChaCha20Poly1305 providers.
-
#encryption_personalization_history(val = nil) ⇒ Array<String>
Previous #encryption_personalization values, kept so that rotating the BLAKE2b personalization used by the XChaCha20 providers stays backward-compatible.
- #logical_database(val = nil) ⇒ Object
- #prefix(val = nil) ⇒ Object
-
#raise_on_unsaved_parent_write(val = nil) ⇒ Boolean
Controls whether a collection write on a DataType raises when the parent Horreum is a new, unsaved object — one whose hash key does not exist in the database yet.
-
#schema_path(val = nil) ⇒ String, ...
Directory containing schema files for JSON Schema validation.
-
#schema_validator(val = nil) ⇒ Symbol, Object
Validator type for JSON Schema validation.
-
#schemas(val = nil) ⇒ Hash
Hash mapping class names to their schema file paths.
-
#strict_write_order(val = nil) ⇒ Boolean
Controls whether collection writes on a DataType raise an error when the parent Horreum object has unsaved scalar field changes.
- #suffix(val = nil) ⇒ Object
-
#transaction_mode(val = nil) ⇒ Symbol
Controls transaction behavior when connection handlers don't support transactions.
Instance Method Summary collapse
-
#configure {|Settings| ... } ⇒ Settings
(also: #config)
Configure Familia settings.
-
#default_suffix ⇒ Object
We define this do-nothing method because it reads better than simply Familia.suffix in some contexts.
-
#dirty_write_warnings(val = nil) ⇒ Symbol
Global default for how collection writes react when the parent Horreum has unsaved scalar field changes.
- #dirty_write_warnings=(val) ⇒ Object
-
#pipelined_mode(val = nil) ⇒ Symbol
Controls pipeline behavior when connection handlers don't support pipelines.
- #pipelined_mode=(val) ⇒ Object
Instance Attribute Details
#current_key_version(val = nil) ⇒ Object
74 75 76 77 |
# File 'lib/familia/settings.rb', line 74 def current_key_version(val = nil) @current_key_version = val if val @current_key_version end |
#default_expiration(val = nil) ⇒ Object
52 53 54 55 |
# File 'lib/familia/settings.rb', line 52 def default_expiration(val = nil) @default_expiration = val unless val.nil? @default_expiration end |
#delim(val = nil) ⇒ Object
37 38 39 40 |
# File 'lib/familia/settings.rb', line 37 def delim(val = nil) @delim = val if val @delim end |
#encryption_hkdf_salt(val = nil) ⇒ String
HKDF salt for the AES-GCM provider's key derivation (RFC 5869). Provides per-deployment domain separation for AES-GCM, mirroring what
encryption_personalization does for the XChaCha20 (BLAKE2b) providers --
but as a SEPARATE input, because HKDF accepts a salt of any length while BLAKE2b personalization is capped at 16 bytes. There is deliberately no length limit here (see issue #311).
The built-in default ('FamilialMatters') is a shared library fallback: it separates Familia's keys from other HKDF users, but NOT one deployment from another. Set a value unique to each deployment to get deployment-level separation.
Encryption always uses this current value; decryption tries it first, then each entry in #encryption_hkdf_salt_history, then the pre-#310 static salt, so existing ciphertext keeps decrypting across rotations and upgrades.
end
158 159 160 161 |
# File 'lib/familia/settings.rb', line 158 def encryption_hkdf_salt(val = nil) @encryption_hkdf_salt = val if val @encryption_hkdf_salt end |
#encryption_hkdf_salt_history(val = nil) ⇒ Array<String>
Previous #encryption_hkdf_salt values, kept so that rotating the AES-GCM HKDF salt stays backward-compatible. When you change the current salt, list the prior value(s) here so existing ciphertext can still be decrypted. Encryption always uses the current value; decryption tries the current value first, then each entry here in order. Keep the list short -- every stale salt adds a decryption attempt on a miss.
178 179 180 181 |
# File 'lib/familia/settings.rb', line 178 def encryption_hkdf_salt_history(val = nil) @encryption_hkdf_salt_history = Array(val) unless val.nil? @encryption_hkdf_salt_history || [] end |
#encryption_keys(val = nil) ⇒ Object
69 70 71 72 |
# File 'lib/familia/settings.rb', line 69 def encryption_keys(val = nil) @encryption_keys = val if val @encryption_keys end |
#encryption_personalization(val = nil) ⇒ String
Personalization string for BLAKE2b key derivation in the XChaCha20Poly1305 providers. Provides cryptographic domain separation so derived keys are unique per application even with identical master keys and contexts.
This knob feeds BLAKE2b's personal parameter ONLY. BLAKE2b caps the
personalization at 16 bytes (the value is null-padded to 16), so it must be
<= 16 bytes. The AES-GCM provider does NOT read this string -- it has its
own #encryption_hkdf_salt, which carries no length limit. Keeping the two
inputs separate avoids constraining one cipher family by the other's rules
(see issue #311).
Encryption always uses this current value; decryption tries it first, then each entry in #encryption_personalization_history, then the built-in default ('FamilialMatters'), so existing ciphertext keeps decrypting across rotations and upgrades (see issue #333).
end
101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/familia/settings.rb', line 101 def encryption_personalization(val = nil) if val if val.bytesize > 16 raise ArgumentError, 'encryption_personalization cannot exceed 16 bytes (BLAKE2b personalization limit). ' \ 'For the AES-GCM HKDF salt, which has no length limit, use encryption_hkdf_salt.' end @encryption_personalization = val end @encryption_personalization end |
#encryption_personalization_history(val = nil) ⇒ Array<String>
Previous #encryption_personalization values, kept so that rotating the BLAKE2b personalization used by the XChaCha20 providers stays backward-compatible. When you change the current value, list the prior value(s) here so existing ciphertext can still be decrypted. Encryption always uses the current value; decryption tries the current value first, then each entry here in order. Keep the list short -- every stale entry adds a decryption attempt on a miss. Entries are subject to the same BLAKE2b constraints as the current value (non-empty String, no null bytes, <= 16 bytes); entries that violate them are skipped on decrypt.
131 132 133 134 |
# File 'lib/familia/settings.rb', line 131 def encryption_personalization_history(val = nil) @encryption_personalization_history = Array(val) unless val.nil? @encryption_personalization_history || [] end |
#logical_database(val = nil) ⇒ Object
57 58 59 60 61 |
# File 'lib/familia/settings.rb', line 57 def logical_database(val = nil) Familia.trace :DB, nil, "#{@logical_database} #{val}" if Familia.debug? @logical_database = val unless val.nil? @logical_database end |
#prefix(val = nil) ⇒ Object
42 43 44 45 |
# File 'lib/familia/settings.rb', line 42 def prefix(val = nil) @prefix = val if val @prefix end |
#raise_on_unsaved_parent_write(val = nil) ⇒ Boolean
Controls whether a collection write on a DataType raises when the parent Horreum is a new, unsaved object — one whose hash key does not exist in the database yet. This is the most dangerous dirty-write scenario: the collection lands in Redis while none of the parent's scalar data exists, orphaning the collection if the parent is never saved.
Defaults to true (raise) independently of #strict_write_order, because an orphaned-record bug is rarely what the caller intends. Set to false to downgrade to a (still distinct, still strong) warning emitted via Familia.warn instead.
Note: #strict_write_order, when true, raises for every dirty write and therefore still raises the new-object case even if this is set to false.
284 285 286 287 288 289 290 291 |
# File 'lib/familia/settings.rb', line 284 def raise_on_unsaved_parent_write(val = nil) @raise_on_unsaved_parent_write = val unless val.nil? # Defaults to true: an unset (nil) value means "raise". Cannot use # `|| true` here -- that would coerce an explicit `false` back to true. return true if @raise_on_unsaved_parent_write.nil? @raise_on_unsaved_parent_write end |
#schema_path(val = nil) ⇒ String, ...
Directory containing schema files for JSON Schema validation. When set, schema files are discovered by convention using the underscored class name (e.g., Customer -> customer.json).
350 351 352 353 |
# File 'lib/familia/settings.rb', line 350 def schema_path(val = nil) @schema_path = val if val @schema_path end |
#schema_validator(val = nil) ⇒ Symbol, Object
Validator type for JSON Schema validation.
Available options:
- :json_schemer (default): Use the json_schemer gem for validation
- :none: Disable schema validation entirely
- Custom instance: Any object responding to #validate
389 390 391 392 |
# File 'lib/familia/settings.rb', line 389 def schema_validator(val = nil) @schema_validator = val if val @schema_validator || :json_schemer end |
#schemas(val = nil) ⇒ Hash
Hash mapping class names to their schema file paths. Takes precedence over convention-based discovery via schema_path.
369 370 371 372 |
# File 'lib/familia/settings.rb', line 369 def schemas(val = nil) @schemas = val if val @schemas || {} end |
#strict_write_order(val = nil) ⇒ Boolean
Controls whether collection writes on a DataType raise an error when the parent Horreum object has unsaved scalar field changes.
When false (default), a warning is emitted via Familia.warn. When true, a Familia::Problem exception is raised.
257 258 259 260 |
# File 'lib/familia/settings.rb', line 257 def strict_write_order(val = nil) @strict_write_order = val unless val.nil? @strict_write_order || false end |
#suffix(val = nil) ⇒ Object
47 48 49 50 |
# File 'lib/familia/settings.rb', line 47 def suffix(val = nil) @suffix = val if val @suffix end |
#transaction_mode(val = nil) ⇒ Symbol
Controls transaction behavior when connection handlers don't support transactions
Available modes:
- :warn (default): Log warning and execute commands individually
- :strict: Raise OperationModeError when transaction unavailable
- :permissive: Silently execute commands individually
198 199 200 201 202 203 204 205 206 207 |
# File 'lib/familia/settings.rb', line 198 def transaction_mode(val = nil) if val unless %i[strict warn permissive].include?(val) raise ArgumentError, 'Transaction mode must be :strict, :warn, or :permissive' end @transaction_mode = val end @transaction_mode || :warn # default to warn mode end |
Instance Method Details
#configure {|Settings| ... } ⇒ Settings Also known as: config
Configure Familia settings
407 408 409 410 |
# File 'lib/familia/settings.rb', line 407 def configure yield self if block_given? self end |
#default_suffix ⇒ Object
We define this do-nothing method because it reads better than simply Familia.suffix in some contexts.
65 66 67 |
# File 'lib/familia/settings.rb', line 65 def default_suffix suffix end |
#dirty_write_warnings(val = nil) ⇒ Symbol
Global default for how collection writes react when the parent Horreum has unsaved scalar field changes. Acts as the fallback when a Horreum subclass does not set its own +dirty_write_warnings+ class setting.
The resolved mode drives both warning and raise behavior: :warn/:once control warning frequency, :strict forces a raise, and :off suppresses everything (warnings and raises) for the class. See the precedence note below and Familia::DataType#warn_if_dirty!.
Available modes:
- :once (default): Warn once per distinct dirty-field signature within a dirty window (deduped). A change to the dirty set warns again.
- :warn: Warn on every collection write (legacy behavior).
- :strict: Raise Familia::Problem on every violation.
- :off: Suppress warnings entirely.
Note: +Familia.strict_write_order = true+ raises for any class whose mode is not explicitly +:off+. A class-level +:off+ is authoritative -- it suppresses both warnings and raises, overriding +strict_write_order+ and +raise_on_unsaved_parent_write+. "Off means off."
322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/familia/settings.rb', line 322 def dirty_write_warnings(val = nil) unless val.nil? valid = %i[strict warn once off] unless valid.include?(val) raise ArgumentError, "dirty_write_warnings must be one of #{valid.inspect}, got #{val.inspect}" end @dirty_write_warnings = val end @dirty_write_warnings || :once end |
#dirty_write_warnings=(val) ⇒ Object
334 335 336 |
# File 'lib/familia/settings.rb', line 334 def dirty_write_warnings=(val) dirty_write_warnings(val) end |
#pipelined_mode(val = nil) ⇒ Symbol
Controls pipeline behavior when connection handlers don't support pipelines
Available modes:
- :warn (default): Log warning and execute commands individually
- :strict: Raise OperationModeError when pipeline unavailable
- :permissive: Silently execute commands individually
224 225 226 227 228 229 230 231 232 233 |
# File 'lib/familia/settings.rb', line 224 def pipelined_mode(val = nil) if val unless %i[strict warn permissive].include?(val) raise ArgumentError, 'Pipeline mode must be :strict, :warn, or :permissive' end @pipelined_mode = val end @pipelined_mode || :warn # default to warn mode end |
#pipelined_mode=(val) ⇒ Object
235 236 237 238 239 240 241 |
# File 'lib/familia/settings.rb', line 235 def pipelined_mode=(val) unless %i[strict warn permissive].include?(val) raise ArgumentError, 'Pipeline mode must be :strict, :warn, or :permissive' end @pipelined_mode = val end |