Module: Familia::Features::ObjectIdentifier

Defined in:
lib/familia/features/object_identifier.rb

Overview

ObjectIdentifier is a feature that provides unique object identifier management with configurable generation strategies. Object identifiers are crucial for distinguishing objects in distributed systems and providing stable references.

Object identifiers are:

  • Unique across the system
  • Persistent (stored in Valkey/Redis)
  • Lazily generated (only when first accessed)
  • Configurable (multiple generation strategies available)
  • Preserved during initialization (existing IDs never regenerated)

Generation Strategies:

  • :uuid_v7 (default) - UUID version 7 with embedded timestamp for sortability
  • :uuid_v4 - UUID version 4 for compatibility with legacy systems
  • :hex - High-entropy hexadecimal identifier using SecureIdentifier
  • Proc - Custom generation logic provided as a callable

Example Usage:

# Default UUID v7 generation class User < Familia::Horreum feature :object_identifier field :email end

user = User.new(email: 'user@example.com') user.objid # => "01234567-89ab-7def-8000-123456789abc" (UUID v7)

# UUID v4 for legacy compatibility class LegacyUser < Familia::Horreum feature :object_identifier, generator: :uuid_v4 field :email end

legacy = LegacyUser.new(email: 'legacy@example.com') legacy.objid # => "f47ac10b-58cc-4372-a567-0e02b2c3d479" (UUID v4)

# High-entropy hex for security-sensitive applications class SecureDocument < Familia::Horreum feature :object_identifier, generator: :hex field :title end

doc = SecureDocument.new(title: 'Classified') doc.objid # => "a1b2c3d4e5f6..." (256-bit hex)

# Custom generation strategy class TimestampedItem < Familia::Horreum feature :object_identifier, generator: -> { "item_#Familia.now.to_i_#SecureRandom.hex(4)" } field :data end

item = TimestampedItem.new(data: 'test') item.objid # => "item_1693857600_a1b2c3d4"

Data Integrity Guarantees:

The feature preserves the object identifier passed during initialization, ensuring that existing objects loaded from Valkey/Redis maintain their IDs:

# Loading existing object from Valkey/Redis preserves ID existing = User.new(objid: 'existing-uuid-value', email: 'existing@example.com') existing.objid # => "existing-uuid-value" (preserved, not regenerated)

Performance Characteristics:

  • Lazy Generation: IDs generated only when first accessed
  • Thread-Safe: Generator strategy configured once during initialization
  • Memory Efficient: No unnecessary ID generation for unused objects
  • Valkey/Redis Efficient: Only persists non-nil values to conserve memory

Security Considerations:

  • UUID v7 includes timestamp information (may leak timing data)
  • UUID v4 provides strong randomness without timing correlation
  • Hex generator provides maximum entropy (256 bits) for security-critical use cases
  • Custom generators allow domain-specific security requirements

Defined Under Namespace

Modules: ModelClassMethods, ModelInstanceMethods Classes: ObjectIdentifierFieldType

Constant Summary collapse

DEFAULT_GENERATOR =
:uuid_v7

Instance Method Summary collapse

Instance Method Details

#destroy!Object



436
437
438
439
440
441
442
443
# File 'lib/familia/features/object_identifier.rb', line 436

def destroy!
  # Clean up objid mapping when object is destroyed
  current_objid = instance_variable_get(:@objid)

  self.class.objid_lookup.remove_field(current_objid) if current_objid

  super if defined?(super)
end

#initObject

Initialize object identifier configuration

Called during object initialization to set up the ID generation strategy. This hook is called AFTER field initialization, ensuring that any objid values passed during construction are preserved.



451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/familia/features/object_identifier.rb', line 451

def init
  super if defined?(super)

  # The generator strategy is configured at the class level via feature options.
  # We don't need to store it per-instance since it's consistent for the class.
  # The actual generation happens lazily in the getter when needed.

  return unless Familia.debug?

  options = self.class.feature_options(:object_identifier)
  generator = options[:generator] || DEFAULT_GENERATOR
  Familia.trace :OBJID_INIT, nil, "Generator strategy: #{generator}"
end

#object_identifierString

Full-length alias for objid for clarity when needed

Returns:

  • (String)

    The object identifier



395
396
397
# File 'lib/familia/features/object_identifier.rb', line 395

def object_identifier
  objid
end

#object_identifier=(value) ⇒ Object



432
433
434
# File 'lib/familia/features/object_identifier.rb', line 432

def object_identifier=(value)
  self.objid = value
end