Module: Tina4::FieldTypes::ClassMethods
- Defined in:
- lib/tina4/field_types.rb
Instance Method Summary collapse
-
#apply_fk_registry! ⇒ Object
Apply any deferred FK-registry has_many wiring for this class.
- #blob_field(name, nullable: true, default: nil) ⇒ Object
- #boolean_field(name, nullable: true, default: nil) ⇒ Object
- #date_field(name, nullable: true, default: nil) ⇒ Object
- #datetime_field(name, nullable: true, default: nil) ⇒ Object
- #decimal_field(name, precision: 10, scale: 2, nullable: true, default: nil) ⇒ Object
- #field_definitions ⇒ Object
- #float_field(name, nullable: true, default: nil) ⇒ Object
-
#foreign_key_field(name, references:, related_name: nil, **options) ⇒ Object
Declare a foreign key integer column and auto-wire relationships.
- #integer_field(name, primary_key: false, auto_increment: false, nullable: true, default: nil) ⇒ Object
- #json_field(name, nullable: true, default: nil) ⇒ Object
- #numeric_field(name, nullable: true, default: nil) ⇒ Object
- #primary_key_field ⇒ Object
-
#primary_key_fields ⇒ Object
EVERY primary-key field name, in declaration order.
-
#resolve_referenced_class(references) ⇒ Object
Resolve a ForeignKeyField
references:argument to a live Tina4::ORM subclass, or nil if it is not (yet) loaded. - #string_field(name, length: 255, primary_key: false, nullable: true, default: nil) ⇒ Object
- #table_name(name = nil) ⇒ Object
- #text_field(name, nullable: true, default: nil) ⇒ Object
- #timestamp_field(name, nullable: true, default: nil) ⇒ Object
Instance Method Details
#apply_fk_registry! ⇒ Object
Apply any deferred FK-registry has_many wiring for this class. Called automatically when a class that is referenced by a ForeignKeyField is defined.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/tina4/field_types.rb', line 177 def apply_fk_registry! # Anonymous classes (Class.new(Tina4::ORM), common in specs) have a nil # name and can never be a string-form ForeignKeyField target, so there # is nothing to wire — bail out instead of raising on nil.split. return if name.nil? class_simple_name = name.split("::").last return unless defined?(@@_fk_registry) && @@_fk_registry.key?(class_simple_name) @@_fk_registry[class_simple_name].each do |entry| next if entry[:applied] has_many(entry[:has_many_name], class_name: entry[:declaring_class].name.split("::").last, foreign_key: entry[:foreign_key]) if respond_to?(:has_many, true) entry[:applied] = true end end |
#blob_field(name, nullable: true, default: nil) ⇒ Object
89 90 91 |
# File 'lib/tina4/field_types.rb', line 89 def blob_field(name, nullable: true, default: nil) register_field(name, :blob, nullable: nullable, default: default) end |
#boolean_field(name, nullable: true, default: nil) ⇒ Object
73 74 75 |
# File 'lib/tina4/field_types.rb', line 73 def boolean_field(name, nullable: true, default: nil) register_field(name, :boolean, nullable: nullable, default: default) end |
#date_field(name, nullable: true, default: nil) ⇒ Object
77 78 79 |
# File 'lib/tina4/field_types.rb', line 77 def date_field(name, nullable: true, default: nil) register_field(name, :date, nullable: nullable, default: default) end |
#datetime_field(name, nullable: true, default: nil) ⇒ Object
81 82 83 |
# File 'lib/tina4/field_types.rb', line 81 def datetime_field(name, nullable: true, default: nil) register_field(name, :datetime, nullable: nullable, default: default) end |
#decimal_field(name, precision: 10, scale: 2, nullable: true, default: nil) ⇒ Object
64 65 66 67 |
# File 'lib/tina4/field_types.rb', line 64 def decimal_field(name, precision: 10, scale: 2, nullable: true, default: nil) register_field(name, :decimal, precision: precision, scale: scale, nullable: nullable, default: default) end |
#field_definitions ⇒ Object
10 11 12 |
# File 'lib/tina4/field_types.rb', line 10 def field_definitions @field_definitions ||= {} end |
#float_field(name, nullable: true, default: nil) ⇒ Object
60 61 62 |
# File 'lib/tina4/field_types.rb', line 60 def float_field(name, nullable: true, default: nil) register_field(name, :float, nullable: nullable, default: default) end |
#foreign_key_field(name, references:, related_name: nil, **options) ⇒ Object
Declare a foreign key integer column and auto-wire relationships.
Automatically:
- Registers an integer field for the column
- Calls belongs_to on this class (strip _id suffix for association name)
- Calls has_many on the referenced class. The accessor name is the
declaring class name lowercased + "s" (e.g. Post → posts), matching
Python; override with related_name:. Works whether the referenced
class loaded before OR after this one, including the string form
(references: "Author") for forward references — resolution is
deferred via Tina4::ORM.inherited until the target class exists.
Example:
class Post < Tina4::ORM
integer_field :id, primary_key: true
foreign_key_field :user_id, references: User
end
# post.user → belongs_to auto-wired
# user.posts → has_many auto-wired
120 121 122 123 124 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 |
# File 'lib/tina4/field_types.rb', line 120 def foreign_key_field(name, references:, related_name: nil, **) register_field(name, :integer, **) # Derive association name: strip _id suffix belongs_name = name.to_s.end_with?("_id") ? name.to_s[0..-4].to_sym : name.to_sym # Wire belongs_to on this class belongs_to(belongs_name, class_name: references.to_s.split("::").last, foreign_key: name.to_s) if respond_to?(:belongs_to, true) # Wire has_many on referenced class (if already a loaded Class) if references.is_a?(Class) && references.respond_to?(:has_many, true) hm_name = ( || "#{self.name.split("::").last.downcase}s").to_sym references.has_many(hm_name, class_name: self.name.split("::").last, foreign_key: name.to_s) end # Register for deferred wiring (resolves when referenced class is later loaded) @@_fk_registry ||= {} ref_name = references.is_a?(Class) ? references.name.split("::").last : references.to_s.split("::").last @@_fk_registry[ref_name] ||= [] hm_key = ( || "#{self.name.split("::").last.downcase}s").to_s @@_fk_registry[ref_name] << { declaring_class: self, has_many_name: hm_key.to_sym, foreign_key: name.to_s } # If the referenced model is ALREADY loaded (string form whose target # is defined, or any forward reference that has since resolved), wire # the deferred entry right now. The inherited hook on Tina4::ORM only # fires when a NEW class is defined, so a string reference to a class # that loaded BEFORE this one would otherwise never wire its has_many # side. resolve_referenced_class returns the live class for either the # Class or the string form. resolved = resolve_referenced_class(references) resolved.apply_fk_registry! if resolved && resolved.respond_to?(:apply_fk_registry!, true) end |
#integer_field(name, primary_key: false, auto_increment: false, nullable: true, default: nil) ⇒ Object
46 47 48 49 |
# File 'lib/tina4/field_types.rb', line 46 def integer_field(name, primary_key: false, auto_increment: false, nullable: true, default: nil) register_field(name, :integer, primary_key: primary_key, auto_increment: auto_increment, nullable: nullable, default: default) end |
#json_field(name, nullable: true, default: nil) ⇒ Object
93 94 95 |
# File 'lib/tina4/field_types.rb', line 93 def json_field(name, nullable: true, default: nil) register_field(name, :json, nullable: nullable, default: default) end |
#numeric_field(name, nullable: true, default: nil) ⇒ Object
69 70 71 |
# File 'lib/tina4/field_types.rb', line 69 def numeric_field(name, nullable: true, default: nil) register_field(name, :float, nullable: nullable, default: default) end |
#primary_key_field ⇒ Object
14 15 16 |
# File 'lib/tina4/field_types.rb', line 14 def primary_key_field @primary_key_field end |
#primary_key_fields ⇒ Object
EVERY primary-key field name, in declaration order.
A key may span several columns. primary_key_field returns only ONE and
is kept for the auto-increment paths, which are single-column by
definition. Anything that ADDRESSES a row must use this: keying on one
column of a composite key matches every row sharing that value, which is
the data-loss shape feature 4 removed from the raw write path below.
25 26 27 28 |
# File 'lib/tina4/field_types.rb', line 25 def primary_key_fields keys = field_definitions.select { |_n, o| o[:primary_key] }.keys keys.empty? ? [:id] : keys end |
#resolve_referenced_class(references) ⇒ Object
Resolve a ForeignKeyField references: argument to a live Tina4::ORM
subclass, or nil if it is not (yet) loaded. Accepts either a Class or a
class-name String (with or without a namespace). The string form is the
documented deferred-safe path: a bare constant used before its class is
defined raises NameError (plain Ruby), so forward references must be
written as strings.
163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/tina4/field_types.rb', line 163 def resolve_referenced_class(references) return references if references.is_a?(Class) return nil unless defined?(Tina4::ORM) && Tina4::ORM.respond_to?(:model_subclasses) simple = references.to_s.split("::").last Tina4::ORM.model_subclasses.find do |klass| klass.name && klass.name.split("::").last == simple end rescue StandardError nil end |
#string_field(name, length: 255, primary_key: false, nullable: true, default: nil) ⇒ Object
51 52 53 54 |
# File 'lib/tina4/field_types.rb', line 51 def string_field(name, length: 255, primary_key: false, nullable: true, default: nil) register_field(name, :string, length: length, primary_key: primary_key, nullable: nullable, default: default) end |
#table_name(name = nil) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/tina4/field_types.rb', line 30 def table_name(name = nil) if name @table_name = name else base = self.name.split("::").last.downcase # Pluralization is OFF by default (canonical, matching the Python # master): the table name is the bare class name lowercased. Opt in by # setting TINA4_ORM_PLURAL_TABLE_NAMES to a truthy value (true/1/yes/on) # to append "s". if ENV.fetch("TINA4_ORM_PLURAL_TABLE_NAMES", "").match?(/\A(true|1|yes|on)\z/i) base += "s" unless base.end_with?("s") end @table_name || base end end |
#text_field(name, nullable: true, default: nil) ⇒ Object
56 57 58 |
# File 'lib/tina4/field_types.rb', line 56 def text_field(name, nullable: true, default: nil) register_field(name, :text, nullable: nullable, default: default) end |
#timestamp_field(name, nullable: true, default: nil) ⇒ Object
85 86 87 |
# File 'lib/tina4/field_types.rb', line 85 def (name, nullable: true, default: nil) register_field(name, :timestamp, nullable: nullable, default: default) end |