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
-
#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.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/tina4/field_types.rb', line 165 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
77 78 79 |
# File 'lib/tina4/field_types.rb', line 77 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
61 62 63 |
# File 'lib/tina4/field_types.rb', line 61 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
65 66 67 |
# File 'lib/tina4/field_types.rb', line 65 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
69 70 71 |
# File 'lib/tina4/field_types.rb', line 69 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
52 53 54 55 |
# File 'lib/tina4/field_types.rb', line 52 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
48 49 50 |
# File 'lib/tina4/field_types.rb', line 48 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
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/tina4/field_types.rb', line 108 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
34 35 36 37 |
# File 'lib/tina4/field_types.rb', line 34 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
81 82 83 |
# File 'lib/tina4/field_types.rb', line 81 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
57 58 59 |
# File 'lib/tina4/field_types.rb', line 57 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 |
#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.
151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/tina4/field_types.rb', line 151 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
39 40 41 42 |
# File 'lib/tina4/field_types.rb', line 39 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
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/tina4/field_types.rb', line 18 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
44 45 46 |
# File 'lib/tina4/field_types.rb', line 44 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
73 74 75 |
# File 'lib/tina4/field_types.rb', line 73 def (name, nullable: true, default: nil) register_field(name, :timestamp, nullable: nullable, default: default) end |