Module: Familia::Horreum::DefinitionMethods

Includes:
RelatedFieldsManagement, Settings
Defined in:
lib/familia/horreum/subclass/definition.rb

Overview

DefinitionMethods: Provides class-level functionality for Horreum subclasses

This module is extended into classes that include Familia::Horreum, providing methods for Database operations and object management.

Key features: * Includes RelatedFieldsManagement for DataType field handling * Defines methods for managing fields, identifiers, and dbkeys * Provides utility methods for working with Database objects

Instance Attribute Summary

Attributes included from Settings

#current_key_version, #default_expiration, #delim, #encryption_keys, #encryption_personalization

Instance Method Summary collapse

Methods included from RelatedFieldsManagement

#attach_class_related_field, #attach_instance_related_field

Methods included from Settings

#config, #default_suffix

Instance Method Details



162
163
164
165
# File 'lib/familia/horreum/subclass/definition.rb', line 162

def class_related_fields
  @class_related_fields ||= {}
  @class_related_fields
end

#config_nameString

Converts the class name into a string that can be used to look up configuration values. This is particularly useful when mapping familia models with specific database numbers in the configuration.

Examples:

V2::Session.config_name => ‘session’

Returns:

  • (String)

    The underscored class name as a string



183
184
185
186
187
188
# File 'lib/familia/horreum/subclass/definition.rb', line 183

def config_name
  name.split('::').last
      .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
      .gsub(/([a-z\d])([A-Z])/, '\1_\2')
      .downcase
end

#dump_methodObject



190
191
192
# File 'lib/familia/horreum/subclass/definition.rb', line 190

def dump_method
  @dump_method || :to_json # Familia.dump_method
end

#field(name, as: name, fast_method: :"#{name}!", on_conflict: :raise, category: nil) ⇒ Object

Defines a field for the class and creates accessor methods.

This method defines a new field for the class, creating getter and setter instance methods similar to attr_accessor. It also generates a fast writer method for immediate persistence to Redis.

Parameters:

  • name (Symbol, String)

    the name of the field to define. If a method with the same name already exists, an error is raised.

  • as (Symbol, String, false, nil) (defaults to: name)

    as the name to use for the accessor method (defaults to name). If false or nil, no accessor methods are created.

  • fast_method (Symbol, false, nil) (defaults to: :"#{name}!")

    the name to use for the fast writer method (defaults to :”#name!”). If false or nil, no fast writer method is created.

  • on_conflict (Symbol) (defaults to: :raise)

    conflict resolution strategy when method already exists: - :raise - raise error if method exists (default) - :skip - skip definition if method exists - :warn - warn but proceed (may overwrite) - :ignore - proceed silently (may overwrite)

  • category (Symbol, nil) (defaults to: nil)

    field category for special handling: - nil - regular field (default) - :encrypted - field contains encrypted data - :transient - field is not persisted - Others, depending on features available



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/familia/horreum/subclass/definition.rb', line 97

def field(name, as: name, fast_method: :"#{name}!", on_conflict: :raise, category: nil)
  # Use field type system for consistency
  require_relative '../../field_type'

  # Create appropriate field type based on category
  field_type = if category == :transient
                 require_relative '../../features/transient_fields/transient_field_type'
                 TransientFieldType.new(name, as: as, fast_method: false, on_conflict: on_conflict)
               else
                 # For regular fields and other categories, create custom field type with category override
                 custom_field_type = Class.new(FieldType) do
                   define_method :category do
                     category || :field
                   end
                 end
                 custom_field_type.new(name, as: as, fast_method: fast_method, on_conflict: on_conflict)
               end

  register_field_type(field_type)
end

#field_method_mapObject

Returns a hash mapping field names to method names for backward compatibility



204
205
206
# File 'lib/familia/horreum/subclass/definition.rb', line 204

def field_method_map
  field_types.transform_values(&:method_name)
end

#field_typesObject

Storage for field type instances



199
200
201
# File 'lib/familia/horreum/subclass/definition.rb', line 199

def field_types
  @field_types ||= {}
end

#fieldsObject

Returns the list of field names defined for the class in the order that they were defined. i.e. field :a; field :b; fields => [:a, :b].



157
158
159
160
# File 'lib/familia/horreum/subclass/definition.rb', line 157

def fields
  @fields ||= []
  @fields
end

#has_relations?Boolean

Returns:

  • (Boolean)


172
173
174
# File 'lib/familia/horreum/subclass/definition.rb', line 172

def has_relations?
  @has_relations ||= false
end

#identifier_field(val = nil) ⇒ Object

Sets or retrieves the unique identifier field for the class.

This method defines or returns the field or method that contains the unique identifier used to generate the dbkey for the object. If a value is provided, it sets the identifier field; otherwise, it returns the current identifier field.

Parameters:

  • val (Object) (defaults to: nil)

    the field name or method to set as the identifier field (optional).

Returns:

  • (Object)

    the current identifier field.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/familia/horreum/subclass/definition.rb', line 58

def identifier_field(val = nil)
  if val
    # Validate identifier field definition at class definition time
    case val
    when Symbol, String, Proc
      @identifier_field = val
    else
      raise Problem, <<~ERROR
        Invalid identifier field definition: #{val.inspect}.
        Use a field name (Symbol/String) or Proc.
      ERROR
    end
  end
  @identifier_field
end

#load_methodObject



194
195
196
# File 'lib/familia/horreum/subclass/definition.rb', line 194

def load_method
  @load_method || :from_json # Familia.load_method
end

#logical_database(v = nil) ⇒ Object



149
150
151
152
153
# File 'lib/familia/horreum/subclass/definition.rb', line 149

def logical_database(v = nil)
  Familia.trace :DB, Familia.dbclient, "#{@logical_database} #{v.nil?}", caller(0..2) if Familia.debug?
  @logical_database = v unless v.nil?
  @logical_database || parent&.logical_database
end

#persistent_fieldsObject

Get fields for serialization (excludes transients)



209
210
211
212
213
# File 'lib/familia/horreum/subclass/definition.rb', line 209

def persistent_fields
  fields.select do |field|
    field_types[field]&.persistent?
  end
end

#prefix(a = nil) ⇒ String, Symbol

Sets or retrieves the prefix for generating Redis keys.

The exception is only raised when both @prefix is nil/falsy AND name is nil, which typically occurs with anonymous classes that haven’t had their prefix explicitly set.

Parameters:

  • a (String, Symbol, nil) (defaults to: nil)

    the prefix to set (optional).

Returns:

  • (String, Symbol)

    the current prefix.



138
139
140
141
142
143
144
145
146
147
# File 'lib/familia/horreum/subclass/definition.rb', line 138

def prefix(a = nil)
  @prefix = a if a
  @prefix || begin
    if name.nil?
      raise Problem, 'Cannot generate prefix for anonymous class. ' \
                     'Use `prefix` method to set explicitly.'
    end
    name.downcase.gsub('::', Familia.delim).to_sym
  end
end

#register_field_type(field_type) ⇒ Object

Register a field type instance with this class

This method installs the field type’s methods and registers it for later reference. It maintains backward compatibility by creating FieldDefinition objects.

Parameters:

  • field_type (FieldType)

    The field type to register



230
231
232
233
234
235
236
# File 'lib/familia/horreum/subclass/definition.rb', line 230

def register_field_type(field_type)
  fields << field_type.name
  field_type.install(self)
  # Complete the registration after installation. If we do this beforehand
  # we can run into issues where it looks like it's already installed.
  field_types[field_type.name] = field_type
end


167
168
169
170
# File 'lib/familia/horreum/subclass/definition.rb', line 167

def related_fields
  @related_fields ||= {}
  @related_fields
end

#suffix(a = nil, &blk) ⇒ String, Symbol

Sets or retrieves the suffix for generating Redis keys.

Parameters:

  • a (String, Symbol, nil) (defaults to: nil)

    the suffix to set (optional).

  • blk (Proc)

    a block that returns the suffix (optional).

Returns:

  • (String, Symbol)

    the current suffix or Familia.default_suffix if none is set.



124
125
126
127
# File 'lib/familia/horreum/subclass/definition.rb', line 124

def suffix(a = nil, &blk)
  @suffix = a || blk if a || !blk.nil?
  @suffix || Familia.default_suffix
end

#transient_field(name) ⇒ Object

Create and register a transient field type

Parameters:

  • name (Symbol)

    The field name

  • options (Hash)

    Field options



243
244
245
246
247
# File 'lib/familia/horreum/subclass/definition.rb', line 243

def transient_field(name, **)
  require_relative '../../features/transient_fields/transient_field_type'
  field_type = TransientFieldType.new(name, **, fast_method: false)
  register_field_type(field_type)
end

#transient_fieldsObject

Get fields that are not persisted to the database (transients)



216
217
218
219
220
# File 'lib/familia/horreum/subclass/definition.rb', line 216

def transient_fields
  fields.select do |field|
    field_types[field]&.transient?
  end
end