Class: Familia::FieldType
- Inherits:
-
Object
- Object
- Familia::FieldType
- Defined in:
- lib/familia/field_type.rb
Overview
Base class for all field types in Familia
Field types encapsulate the behavior for different kinds of fields, including how their getter/setter methods are defined and how values are serialized/deserialized.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#fast_method_name ⇒ Object
readonly
Returns the value of attribute fast_method_name.
-
#method_name ⇒ Object
readonly
Returns the value of attribute method_name.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#on_conflict ⇒ Object
readonly
Returns the value of attribute on_conflict.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
-
#category ⇒ Symbol
The category for this field type (used for filtering).
-
#define_fast_writer(klass) ⇒ Object
Define the fast writer method on the target class.
-
#define_getter(klass) ⇒ Object
Define the getter method on the target class.
-
#define_setter(klass) ⇒ Object
Define the setter method on the target class.
-
#deserialize(value, _record = nil) ⇒ Object
Deserialize a value from database storage.
-
#generated_methods ⇒ Array<Symbol>
Returns all method names generated for this field (used for conflict detection).
-
#initialize(name, as: name, fast_method: :"#{name}!", on_conflict: :raise, **options) ⇒ FieldType
constructor
Initialize a new field type.
-
#inspect ⇒ String
(also: #to_s)
Enhanced inspection output for debugging.
-
#install(klass) ⇒ Object
Install this field type on a class.
-
#persistent? ⇒ Boolean
Whether this field should be persisted to the database.
-
#serialize(value, _record = nil) ⇒ Object
Serialize a value for database storage.
-
#transient? ⇒ Boolean
Constructor Details
#initialize(name, as: name, fast_method: :"#{name}!", on_conflict: :raise, **options) ⇒ FieldType
Initialize a new field type
43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/familia/field_type.rb', line 43 def initialize(name, as: name, fast_method: :"#{name}!", on_conflict: :raise, **) @name = name.to_sym @method_name = as == false ? nil : as.to_sym @fast_method_name = fast_method == false ? nil : fast_method&.to_sym # Validate fast method name format if @fast_method_name && !@fast_method_name.to_s.end_with?('!') raise ArgumentError, "Fast method name must end with '!' (got: #{@fast_method_name})" end @on_conflict = on_conflict @options = end |
Instance Attribute Details
#fast_method_name ⇒ Object (readonly)
Returns the value of attribute fast_method_name.
30 31 32 |
# File 'lib/familia/field_type.rb', line 30 def fast_method_name @fast_method_name end |
#method_name ⇒ Object (readonly)
Returns the value of attribute method_name.
30 31 32 |
# File 'lib/familia/field_type.rb', line 30 def method_name @method_name end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
30 31 32 |
# File 'lib/familia/field_type.rb', line 30 def name @name end |
#on_conflict ⇒ Object (readonly)
Returns the value of attribute on_conflict.
30 31 32 |
# File 'lib/familia/field_type.rb', line 30 def on_conflict @on_conflict end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
30 31 32 |
# File 'lib/familia/field_type.rb', line 30 def @options end |
Instance Method Details
#category ⇒ Symbol
The category for this field type (used for filtering)
178 179 180 |
# File 'lib/familia/field_type.rb', line 178 def category :field end |
#define_fast_writer(klass) ⇒ Object
Define the fast writer method on the target class
Fast methods provide direct database access for immediate persistence. Subclasses can override this to customize fast method behavior.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/familia/field_type.rb', line 125 def define_fast_writer(klass) return unless @fast_method_name&.to_s&.end_with?('!') field_name = @name method_name = @method_name fast_method_name = @fast_method_name handle_method_conflict(klass, fast_method_name) do klass.define_method fast_method_name do |*args| raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0 or 1)" if args.size > 1 val = args.first # If no value provided, return current stored value return hget(field_name) if val.nil? begin # Trace the operation if debugging is enabled Familia.trace :FAST_WRITER, dbclient, "#{field_name}: #{val.inspect}", caller(1..1) if Familia.debug? # Convert value for database storage prepared = serialize_value(val) Familia.ld "[FieldType#define_fast_writer] #{fast_method_name} val: #{val.class} prepared: #{prepared.class}" # Use the setter method to update instance variable send(:"#{method_name}=", val) if method_name # Persist to database immediately ret = hset(field_name, prepared) ret.zero? || ret.positive? rescue Familia::Problem => e raise "#{fast_method_name} method failed: #{e.}", e.backtrace end end end end |
#define_getter(klass) ⇒ Object
Define the getter method on the target class
Subclasses can override this to customize getter behavior. The default implementation creates a simple attr_reader equivalent.
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/familia/field_type.rb', line 89 def define_getter(klass) field_name = @name method_name = @method_name handle_method_conflict(klass, method_name) do klass.define_method method_name do instance_variable_get(:"@#{field_name}") end end end |
#define_setter(klass) ⇒ Object
Define the setter method on the target class
Subclasses can override this to customize setter behavior. The default implementation creates a simple attr_writer equivalent.
107 108 109 110 111 112 113 114 115 116 |
# File 'lib/familia/field_type.rb', line 107 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| instance_variable_set(:"@#{field_name}", value) end end end |
#deserialize(value, _record = nil) ⇒ Object
Deserialize a value from database storage
Subclasses can override this to customize deserialization. The default implementation passes values through unchanged.
204 205 206 |
# File 'lib/familia/field_type.rb', line 204 def deserialize(value, _record = nil) value end |
#generated_methods ⇒ Array<Symbol>
Returns all method names generated for this field (used for conflict detection)
212 213 214 |
# File 'lib/familia/field_type.rb', line 212 def generated_methods [@method_name, @fast_method_name].compact end |
#inspect ⇒ String Also known as: to_s
Enhanced inspection output for debugging
220 221 222 223 224 225 226 227 228 229 |
# File 'lib/familia/field_type.rb', line 220 def inspect attributes = [ "name=#{@name}", "method_name=#{@method_name}", "fast_method_name=#{@fast_method_name}", "on_conflict=#{@on_conflict}", "category=#{category}" ] "#<#{self.class.name} #{attributes.join(' ')}>" end |
#install(klass) ⇒ Object
Install this field type on a class
This method defines all necessary methods on the target class and registers the field type for later reference.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/familia/field_type.rb', line 64 def install(klass) if @method_name # For skip strategy, check for any method conflicts first if @on_conflict == :skip has_getter_conflict = klass.method_defined?(@method_name) || klass.private_method_defined?(@method_name) has_setter_conflict = klass.method_defined?(:"#{@method_name}=") || klass.private_method_defined?(:"#{@method_name}=") # If either getter or setter conflicts, skip the whole field return if has_getter_conflict || has_setter_conflict end define_getter(klass) define_setter(klass) end define_fast_writer(klass) if @fast_method_name end |
#persistent? ⇒ Boolean
Whether this field should be persisted to the database
166 167 168 |
# File 'lib/familia/field_type.rb', line 166 def persistent? true end |
#serialize(value, _record = nil) ⇒ Object
Serialize a value for database storage
Subclasses can override this to customize serialization. The default implementation passes values through unchanged.
191 192 193 |
# File 'lib/familia/field_type.rb', line 191 def serialize(value, _record = nil) value end |
#transient? ⇒ Boolean
170 171 172 |
# File 'lib/familia/field_type.rb', line 170 def transient? !persistent? end |