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, required: false, min: nil, max: nil) ⇒ Object
- #field_definitions ⇒ Object
- #float_field(name, nullable: true, default: nil, required: false, min: nil, max: 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, required: false, min: nil, max: nil) ⇒ Object
Feature 19: the constraint options (required:, min:/max: for numbers, min_length:/max_length:/pattern: for strings) are ENFORCED on save() by ORM#validate -- bringing Ruby up to the shared cross-framework richness (Ruby used to be null-only).
- #json_field(name, nullable: true, default: nil) ⇒ Object
- #numeric_field(name, nullable: true, default: nil, required: false, min: nil, max: 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, required: false, min_length: nil, max_length: nil, pattern: nil) ⇒ Object
- #table_name(name = nil) ⇒ Object
- #text_field(name, nullable: true, default: nil, required: false, min_length: nil, max_length: nil, pattern: 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.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/tina4/field_types.rb', line 194 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
103 104 105 |
# File 'lib/tina4/field_types.rb', line 103 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
87 88 89 |
# File 'lib/tina4/field_types.rb', line 87 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
91 92 93 |
# File 'lib/tina4/field_types.rb', line 91 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
95 96 97 |
# File 'lib/tina4/field_types.rb', line 95 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, required: false, min: nil, max: nil) ⇒ Object
76 77 78 79 80 |
# File 'lib/tina4/field_types.rb', line 76 def decimal_field(name, precision: 10, scale: 2, nullable: true, default: nil, required: false, min: nil, max: nil) register_field(name, :decimal, precision: precision, scale: scale, nullable: nullable, default: default, required: required, min: min, max: max) 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, required: false, min: nil, max: nil) ⇒ Object
71 72 73 74 |
# File 'lib/tina4/field_types.rb', line 71 def float_field(name, nullable: true, default: nil, required: false, min: nil, max: nil) register_field(name, :float, nullable: nullable, default: default, required: required, min: min, max: max) 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 and SMART-pluralized via
Tina4.pluralize (e.g. Post → posts, Category → categories -- the same
inflection the hand-written has_many uses, not a naive + "s" that
produced "categorys"); 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
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/tina4/field_types.rb', line 137 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 = ( || Tina4.pluralize(self.name.split("::").last.downcase)).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 = ( || Tina4.pluralize(self.name.split("::").last.downcase)).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, required: false, min: nil, max: nil) ⇒ Object
Feature 19: the constraint options (required:, min:/max: for numbers,
min_length:/max_length:/pattern: for strings) are ENFORCED on save() by
ORM#validate -- bringing Ruby up to the shared cross-framework richness
(Ruby used to be null-only). length: stays a DDL VARCHAR sizing hint and
is deliberately never validated (parity with PHP's length); use
max_length: for a value cap.
52 53 54 55 56 |
# File 'lib/tina4/field_types.rb', line 52 def integer_field(name, primary_key: false, auto_increment: false, nullable: true, default: nil, required: false, min: nil, max: nil) register_field(name, :integer, primary_key: primary_key, auto_increment: auto_increment, nullable: nullable, default: default, required: required, min: min, max: max) end |
#json_field(name, nullable: true, default: nil) ⇒ Object
107 108 109 |
# File 'lib/tina4/field_types.rb', line 107 def json_field(name, nullable: true, default: nil) register_field(name, :json, nullable: nullable, default: default) end |
#numeric_field(name, nullable: true, default: nil, required: false, min: nil, max: nil) ⇒ Object
82 83 84 85 |
# File 'lib/tina4/field_types.rb', line 82 def numeric_field(name, nullable: true, default: nil, required: false, min: nil, max: nil) register_field(name, :float, nullable: nullable, default: default, required: required, min: min, max: max) 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.
180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/tina4/field_types.rb', line 180 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, required: false, min_length: nil, max_length: nil, pattern: nil) ⇒ Object
58 59 60 61 62 63 |
# File 'lib/tina4/field_types.rb', line 58 def string_field(name, length: 255, primary_key: false, nullable: true, default: nil, required: false, min_length: nil, max_length: nil, pattern: nil) register_field(name, :string, length: length, primary_key: primary_key, nullable: nullable, default: default, required: required, min_length: min_length, max_length: max_length, pattern: pattern) 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, required: false, min_length: nil, max_length: nil, pattern: nil) ⇒ Object
65 66 67 68 69 |
# File 'lib/tina4/field_types.rb', line 65 def text_field(name, nullable: true, default: nil, required: false, min_length: nil, max_length: nil, pattern: nil) register_field(name, :text, nullable: nullable, default: default, required: required, min_length: min_length, max_length: max_length, pattern: pattern) end |
#timestamp_field(name, nullable: true, default: nil) ⇒ Object
99 100 101 |
# File 'lib/tina4/field_types.rb', line 99 def (name, nullable: true, default: nil) register_field(name, :timestamp, nullable: nullable, default: default) end |