Class: Familia::DataType Abstract

Inherits:
Object
  • Object
show all
Extended by:
ClassMethods, Features
Includes:
Base, Commands, Serialization
Defined in:
lib/familia/data_type.rb,
lib/familia/data_type/commands.rb,
lib/familia/data_type/serialization.rb

Overview

This class is abstract.

Subclass and implement Database data type specific methods

DataType - Base class for Database data type wrappers

This class provides common functionality for various Database data types such as String, List, UnsortedSet, SortedSet, and HashKey.

Direct Known Subclasses

HashKey, ListKey, SortedSet, StringKey, UnsortedSet

Defined Under Namespace

Modules: ClassMethods, Commands, Serialization

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Features::Autoloader

autoload_files, included, normalize_to_config_name

Methods included from Serialization

#deserialize_value, #deserialize_values, #deserialize_values_with_nil, #serialize_value

Methods included from Commands

#current_expiration, #delete!, #echo, #exists?, #expire, #expireat, #move, #persist, #rename, #renamenx, #type

Methods included from Base

add_feature, #as_json, #expired?, #expires?, find_feature, #generate_id, #to_json, #to_s, #ttl, #update_expiration, #uuid

Constructor Details

#initialize(keystring, opts = {}) ⇒ DataType

+keystring+: If parent is set, this will be used as the suffix for dbkey. Otherwise this becomes the value of the key. If this is an Array, the elements will be joined.

Options:

:class => A class that responds to Familia.load_method and Familia.dump_method. These will be used when loading and saving data from/to the database to unmarshal/marshal the class.

:parent => The Familia object that this datatype object belongs to. This can be a class that includes Familia or an instance.

:default_expiration => the time to live in seconds. When not nil, this will set the default expiration for this dbkey whenever #save is called. You can also call it explicitly via #update_expiration.

:default => the default value (String-only)

:dbkey => a hardcoded key to use instead of the deriving the from the name and parent (e.g. a derived key: customer:custid:secret_counter).

:suffix => the suffix to use for the key (e.g. 'scores' in customer:custid:scores). :prefix => the prefix to use for the key (e.g. 'customer' in customer:custid:scores).

Connection precendence: uses the database connection of the parent or the value of opts[:dbclient] or Familia.dbclient (in that order).



113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/familia/data_type.rb', line 113

def initialize(keystring, opts = {})
  @keystring = keystring
  @keystring = @keystring.join(Familia.delim) if @keystring.is_a?(Array)

  # Remove all keys from the opts that are not in the allowed list
  @opts = DataType.valid_keys_only(opts || {})

  # Apply the options to instance method setters of the same name
  @opts.each do |k, v|
    send(:"#{k}=", v) if respond_to? :"#{k}="
  end

  init if respond_to? :init
end

Class Attribute Details

Returns the value of attribute has_related_fields.



29
30
31
# File 'lib/familia/data_type.rb', line 29

def has_related_fields
  @has_related_fields
end

.registered_typesObject (readonly)

Returns the value of attribute registered_types.



29
30
31
# File 'lib/familia/data_type.rb', line 29

def registered_types
  @registered_types
end

.valid_optionsObject (readonly)

Returns the value of attribute valid_options.



29
30
31
# File 'lib/familia/data_type.rb', line 29

def valid_options
  @valid_options
end

Instance Attribute Details

#features_enabledObject (readonly) Originally defined in module Features

Returns the value of attribute features_enabled.

#keystringObject (readonly)

Returns the value of attribute keystring.



82
83
84
# File 'lib/familia/data_type.rb', line 82

def keystring
  @keystring
end

#logical_databaseObject (readonly)

Returns the value of attribute logical_database.



82
83
84
# File 'lib/familia/data_type.rb', line 82

def logical_database
  @logical_database
end

#logical_database(val = nil) ⇒ Object Originally defined in module ClassMethods

#optsObject (readonly)

Returns the value of attribute opts.



82
83
84
# File 'lib/familia/data_type.rb', line 82

def opts
  @opts
end

#parentObject Originally defined in module ClassMethods

Returns the value of attribute parent.

#prefixObject Originally defined in module ClassMethods

Returns the value of attribute prefix.

#suffixObject Originally defined in module ClassMethods

Returns the value of attribute suffix.

#uriObject

Returns the value of attribute uri.



82
83
84
# File 'lib/familia/data_type.rb', line 82

def uri
  @uri
end

#uri(val = nil) ⇒ Object Originally defined in module ClassMethods

Returns the value of attribute uri.

Class Method Details

.feature(feature_name = nil, **options) ⇒ Array? Originally defined in module Features

Enables a feature for the current class with optional configuration.

Features are modular capabilities that can be mixed into Familia::Horreum classes. Each feature can be configured with options that are stored per-class, ensuring complete isolation between different models.

Examples:

Enable feature without options

class User < Familia::Horreum
  feature :expiration
end

Enable feature with options (per-class storage)

class User < Familia::Horreum
  feature :object_identifier, generator: :uuid_v4
end

class Session < Familia::Horreum
  feature :object_identifier, generator: :hex  # Different options
end

# Each class maintains separate options:
User.feature_options(:object_identifier)    #=> {generator: :uuid_v4}
Session.feature_options(:object_identifier) #=> {generator: :hex}

Parameters:

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

    the name of the feature to enable. If nil, returns the list of currently enabled features.

  • options (Hash)

    configuration options for the feature. These are stored per-class and do not interfere with other models' configurations.

Returns:

  • (Array, nil)

    the list of enabled features if feature_name is nil, otherwise nil

Raises:

.inherited(obj) ⇒ Object Originally defined in module ClassMethods

.register(klass, methname) ⇒ Object Originally defined in module ClassMethods

To be called inside every class that inherits DataType +methname+ is the term used for the class and instance methods that are created for the given +klass+ (e.g. set, list, etc)

.registered_type(methname) ⇒ Object Originally defined in module ClassMethods

Get the registered type class from a given method name +methname+ is the method name used to register the class (e.g. :set, :list, etc) Returns the registered class or nil if not found

.relations?Boolean Originally defined in module ClassMethods

Returns:

  • (Boolean)

.valid_keys_only(opts) ⇒ Object Originally defined in module ClassMethods

Instance Method Details

#class?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'lib/familia/data_type.rb', line 187

def class?
  !@opts[:class].to_s.empty? && @opts[:class].is_a?(Familia)
end

#dbclientObject

TODO: Replace with Chain of Responsibility pattern



129
130
131
132
133
134
135
# File 'lib/familia/data_type.rb', line 129

def dbclient
  return Fiber[:familia_transaction] if Fiber[:familia_transaction]
  return @dbclient if @dbclient

  # Delegate to parent if present, otherwise fall back to Familia
  parent ? parent.dbclient : Familia.dbclient(opts[:logical_database])
end

#dbkeyString

Produces the full dbkey for this object.

This method determines the appropriate dbkey based on the context of the DataType object:

  1. If a hardcoded key is set in the options, it returns that key.
  2. For instance-level DataType objects, it uses the parent instance's dbkey method.
  3. For class-level DataType objects, it uses the parent class's dbkey method.
  4. For standalone DataType objects, it uses the keystring as the full dbkey.

For class-level DataType objects (parent_class? == true):

  • The suffix is optional and used to differentiate between different types of objects.
  • If no suffix is provided, the class's default suffix is used (via the self.suffix method).
  • If a nil suffix is explicitly passed, it won't appear in the resulting dbkey.
  • Passing nil as the suffix is how class-level DataType objects are created without the global default 'object' suffix.

Examples:

Instance-level DataType

user_instance.some_datatype.dbkey  # => "user:123:some_datatype"

Class-level DataType

User.some_datatype.dbkey  # => "user:some_datatype"

Standalone DataType

DataType.new("mykey").dbkey  # => "mykey"

Class-level DataType with explicit nil suffix

User.dbkey("123", nil)  # => "user:123"

Returns:

  • (String)

    The full dbkey.



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/familia/data_type.rb', line 167

def dbkey
  # Return the hardcoded key if it's set. This is useful for
  # support legacy keys that aren't derived in the same way.
  return opts[:dbkey] if opts[:dbkey]

  if parent_instance?
    # This is an instance-level datatype object so the parent instance's
    # dbkey method is defined in Familia::Horreum::InstanceMethods.
    parent.dbkey(keystring)
  elsif parent_class?
    # This is a class-level datatype object so the parent class' dbkey
    # method is defined in Familia::Horreum::DefinitionMethods.
    parent.dbkey(keystring, nil)
  else
    # This is a standalone DataType object where it's keystring
    # is the full database key (dbkey).
    keystring
  end
end

#direct_access {|dbclient, dbkey| ... } ⇒ Object

Provides a structured way to "gear down" to run db commands that are not implemented in our DataType classes since we intentionally don't have a method_missing method.

Yields:



194
195
196
# File 'lib/familia/data_type.rb', line 194

def direct_access
  yield(dbclient, dbkey)
end

#dump_methodObject



259
260
261
# File 'lib/familia/data_type.rb', line 259

def dump_method
  self.class.dump_method
end

#load_methodObject



263
264
265
# File 'lib/familia/data_type.rb', line 263

def load_method
  self.class.load_method
end

#parentObject



210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/familia/data_type.rb', line 210

def parent
  # Return cached ParentDefinition if available
  return @parent if @parent

  # Return class-level parent if no instance parent
  return self.class.parent unless @parent_ref

  # Create ParentDefinition dynamically from stored reference.
  # This ensures we get the current identifier value (available after initialization)
  # rather than a stale nil value from initialization time. Cannot cache due to frozen object.
  Horreum::ParentDefinition.from_parent(@parent_ref)
end

#parent=(value) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/familia/data_type.rb', line 223

def parent=(value)
  case value
  when Horreum::ParentDefinition
    @parent = value
  when nil
    @parent = nil
    @parent_ref = nil
  else
    # Store parent instance reference for lazy ParentDefinition creation.
    # During initialization, the parent's identifier may not be available yet,
    # so we defer ParentDefinition creation until first access for memory efficiency.
    # Note: @parent_ref is not cleared after use because DataType objects are frozen.
    @parent_ref = value
    @parent = nil  # Will be created dynamically in parent method
  end
end

#parent?Boolean

Returns:

  • (Boolean)


206
207
208
# File 'lib/familia/data_type.rb', line 206

def parent?
  parent_class? || parent_instance?
end

#parent_class?Boolean

Returns:

  • (Boolean)


202
203
204
# File 'lib/familia/data_type.rb', line 202

def parent_class?
  parent.is_a?(Class) && parent.ancestors.include?(Familia::Horreum)
end

#parent_instance?Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/familia/data_type.rb', line 198

def parent_instance?
  parent&.is_a?(Horreum::ParentDefinition)
end

#urlObject

Returns the value of attribute uri.



84
85
86
# File 'lib/familia/data_type.rb', line 84

def uri
  @uri
end