Class: Familia::Features::ExternalIdentifiers::ExternalIdentifierFieldType

Inherits:
Familia::FieldType
  • Object
show all
Defined in:
lib/familia/features/external_identifiers/external_identifier_field_type.rb

Overview

ExternalIdentifierFieldType - Fields that generate deterministic external identifiers

External identifier fields generate shorter, public-facing identifiers that are deterministically derived from object identifiers. These IDs are safe for use in URLs, APIs, and other external contexts where shorter IDs are preferred.

Key characteristics: - Deterministic generation from objid ensures consistency - Shorter than objid (128-bit vs 256-bit) for external use - Base-36 encoding for URL-safe identifiers - ‘ext_’ prefix for clear identification as external IDs - Lazy generation preserves values from initialization

Examples:

Using external identifier fields

class User < Familia::Horreum
  feature :object_identifiers
  feature :external_identifiers
  field :email
end

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

# Same objid always produces same extid
user2 = User.new(objid: user.objid, email: 'user@example.com')
user2.extid  # => "ext_abc123def456ghi789" (identical to user.extid)

Instance Attribute Summary

Attributes inherited from Familia::FieldType

#fast_method_name, #loggable, #method_name, #name, #on_conflict, #options

Instance Method Summary collapse

Methods inherited from Familia::FieldType

#define_fast_writer, #deserialize, #generated_methods, #initialize, #inspect, #install, #serialize, #transient?

Constructor Details

This class inherits a constructor from Familia::FieldType

Instance Method Details

#categorySymbol

Category for external identifier fields

Returns:

  • (Symbol)

    :external_identifier



114
115
116
# File 'lib/familia/features/external_identifiers/external_identifier_field_type.rb', line 114

def category
  :external_identifier
end

#define_getter(klass) ⇒ Object

Override getter to provide lazy generation from objid

Generates the external identifier deterministically from the object’s objid. This ensures consistency - the same objid will always produce the same extid. Only generates when objid is available.

Parameters:

  • klass (Class)

    The class to define the method on



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/familia/features/external_identifiers/external_identifier_field_type.rb', line 45

def define_getter(klass)
  field_name = @name
  method_name = @method_name

  handle_method_conflict(klass, method_name) do
    klass.define_method method_name do
      # Check if we already have a value (from initialization or previous generation)
      existing_value = instance_variable_get(:"@#{field_name}")
      return existing_value unless existing_value.nil?

      # Generate external identifier from objid if available
      generated_extid = generate_external_identifier
      return unless generated_extid

      instance_variable_set(:"@#{field_name}", generated_extid)

      # Update mapping if we have an identifier
      if respond_to?(:identifier) && identifier
        self.class.extid_lookup[generated_extid] = identifier
      end

      generated_extid
    end
  end
end

#define_setter(klass) ⇒ Object

Override setter to preserve values during initialization

This ensures that values passed during object initialization (e.g., when loading from Redis) are preserved and not overwritten by the lazy generation logic.

Parameters:

  • klass (Class)

    The class to define the method on



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/familia/features/external_identifiers/external_identifier_field_type.rb', line 79

def define_setter(klass)
  field_name = @name
  method_name = @method_name

  handle_method_conflict(klass, :"#{method_name}=") do
    klass.define_method :"#{method_name}=" do |value|
      # Remove old mapping if extid is changing
      old_value = instance_variable_get(:"@#{field_name}")
      if old_value && old_value != value && respond_to?(:identifier)
        self.class.extid_lookup.del(old_value)
      end

      # Set the new value
      instance_variable_set(:"@#{field_name}", value)

      # Update mapping if we have both extid and identifier
      if value && respond_to?(:identifier) && identifier
        self.class.extid_lookup[value] = identifier
      end
    end
  end
end

#persistent?Boolean

External identifier fields are persisted to database

Returns:

  • (Boolean)

    true - external identifiers are always persisted



106
107
108
# File 'lib/familia/features/external_identifiers/external_identifier_field_type.rb', line 106

def persistent?
  true
end