Files
has_aux_table/spec/spec_models.rb
2025-07-24 05:22:55 +00:00

489 lines
12 KiB
Ruby

# typed: strict
# frozen_string_literal: true
extend T::Sig
# Set up the database schema for testing
ActiveRecord::Schema.define do
create_table :vehicle_lots do |t|
t.string :name
t.timestamps
end
create_base_table :vehicles do |t|
t.string :name
t.references :vehicle_lot, foreign_key: { to_table: :vehicle_lots }
t.timestamps
t.create_aux :car do |t|
t.string :fuel_type
t.decimal :engine_size, precision: 3, scale: 1
end
end
change_base_table :vehicles do |t|
t.create_aux :boat do |t|
t.boolean :only_freshwater
end
t.create_aux :plane do |t|
t.integer :engine_type
end
end
create_base_table :people do |t|
t.string :name
t.timestamps
t.create_aux :driver do |t|
t.integer :license_number, index: true, null: false
t.references :car,
foreign_key: {
to_table: :vehicles_car_aux,
primary_key: :base_table_id
}
end
t.create_aux :captain do |t|
t.references :boat,
null: false,
foreign_key: {
to_table: :vehicles_boat_aux,
primary_key: :base_table_id
}
end
t.create_aux :passenger do |t|
t.references :boat,
null: false,
foreign_key: {
to_table: :vehicles_boat_aux,
primary_key: :base_table_id
}
end
end
create_base_table :relationship_joins do |t|
t.create_aux :doctor_patient do |t|
t.integer :num_exams
t.references :doctor, foreign_key: { to_table: :people }
t.references :patient, foreign_key: { to_table: :people }
end
t.create_aux :employer_employee do |t|
t.boolean :signed_nda
t.references :employer, foreign_key: { to_table: :people }
t.references :employee, foreign_key: { to_table: :people }
end
end
create_base_table :utensils do |t|
t.string :name
t.string :material
t.timestamps
t.create_aux :fork do |t|
t.integer :num_tongs
end
t.create_aux :spoon do |t|
t.string :curvature
end
end
create_aux_table :people, :reader do |t|
t.integer :reading_speed
t.integer :read_book_joins_count
end
create_table :books do |t|
t.string :title
t.string :author
t.integer :pages
t.integer :read_book_joins_count
t.timestamps
end
create_table :read_book_joins,
id: false,
primary_key: %i[book_id reader_id] do |t|
t.references :book, foreign_key: { to_table: :books }
t.references :reader,
foreign_key: {
to_table: :people_reader_aux,
primary_key: :base_table_id
}
end
end
class Vehicle < ActiveRecord::Base
include HasAuxTable
belongs_to :vehicle_lot
end
class VehicleLot < ActiveRecord::Base
has_many :vehicles
end
class Car < Vehicle
aux_table :car
has_many :drivers, inverse_of: :car
end
class Boat < Vehicle
aux_table :boat
has_many :passengers, inverse_of: :boat
belongs_to :captain, inverse_of: :boat
end
class Plane < Vehicle
aux_table :plane
enum :engine_type, { turbofan: 0, turboprop: 1, piston: 2, electric: 3 }
end
class Person < ActiveRecord::Base
extend T::Sig
include HasAuxTable
validates :name, presence: true, uniqueness: true
sig do
params(
associations: [Symbol, Symbol],
table_name: Symbol,
model_class_name: T.any(Symbol, String, T.class_of(ActiveRecord::Base)),
join_class_name: T.any(Symbol, String, T.class_of(ActiveRecord::Base))
).void
end
def self.has_and_belongs_to_many_through(
associations,
table_name:,
model_class_name:,
join_class_name:
)
from_assoc_plural, to_assoc_plural = associations
from_assoc_class_name, to_assoc_class_name =
model_class_name,
model_class_name
from_assoc_singular = from_assoc_plural.to_s.singularize
from_join_assoc_name = :"#{from_assoc_singular}_#{table_name}"
to_assoc_singular = to_assoc_plural.to_s.singularize
to_join_assoc_name = :"#{to_assoc_singular}_#{table_name}"
has_many(
from_join_assoc_name,
primary_key: primary_key,
foreign_key: "#{from_assoc_singular}_id",
inverse_of: from_assoc_singular,
class_name: join_class_name
)
has_many(
to_assoc_plural,
through: from_join_assoc_name,
source: to_assoc_singular,
class_name: to_assoc_class_name
)
has_many(
to_join_assoc_name,
primary_key: primary_key,
foreign_key: "#{to_assoc_singular}_id",
inverse_of: to_assoc_singular,
class_name: join_class_name
)
has_many(
from_assoc_plural,
through: to_join_assoc_name,
source: from_assoc_singular,
class_name: from_assoc_class_name
)
end
has_and_belongs_to_many_through(
%i[doctors patients],
model_class_name: "Person",
join_class_name: "DoctorPatientJoin",
table_name: :doctor_patient_joins
)
has_and_belongs_to_many_through(
%i[employers employees],
model_class_name: "Person",
join_class_name: "EmployerEmployeeJoin",
table_name: :employer_employee_joins
)
end
class Driver < Person
aux_table :driver
belongs_to :car, optional: true
validates :license_number, presence: true, uniqueness: true
end
class Captain < Person
aux_table :captain
has_one :boat, inverse_of: :captain
end
class Passenger < Person
aux_table :passenger
belongs_to :boat, inverse_of: :passengers
end
module Kitchen
class Utensil < ActiveRecord::Base
include HasAuxTable
end
class Fork < Utensil
aux_table :fork
end
class Spoon < Utensil
aux_table :spoon
end
end
# Non-aux table model that has_and_belongs_to_many w/ counter cache
class Book < ActiveRecord::Base
has_many :read_book_joins, inverse_of: :book
has_many :readers, through: :read_book_joins
end
# Aux table model that has_and_belongs_to_many w/ counter cache
class Reader < Person
aux_table :reader
has_many :read_book_joins, inverse_of: :reader
has_many :read_books, through: :read_book_joins, source: :book
end
# The join table for the has_and_belongs_to_many association between Reader and Book
class ReadBookJoin < ActiveRecord::Base
belongs_to :book, counter_cache: true
belongs_to :reader, counter_cache: true
end
# Join table that has aux records
class RelationshipJoin < ActiveRecord::Base
include HasAuxTable
end
class DoctorPatientJoin < RelationshipJoin
aux_table :doctor_patient
belongs_to :doctor, class_name: "Person"
belongs_to :patient, class_name: "Person"
end
class EmployerEmployeeJoin < RelationshipJoin
aux_table :employer_employee
belongs_to :employer, class_name: "Person"
belongs_to :employee, class_name: "Person"
end
class User < ActiveRecord::Base
include HasAuxTable
has_many :posts, inverse_of: :user, class_name: "Post"
end
ActiveRecord::Schema.define do
create_base_table :model_as do |t|
t.string :a_field1
t.integer :model_bs_count
t.integer :ad_joins_count
t.create_aux :a1 do |t|
t.integer :a1_field1
t.integer :model_cs_count
end
t.create_aux :a2 do |t|
t.string :a2_field1
t.integer :model_b2s_count
end
end
create_base_table :model_bs do |t|
t.string :b_field1
t.references :model_a, foreign_key: { to_table: :model_as }
t.create_aux :b1 do |t|
t.integer :b1_field1
end
t.create_aux :b2 do |t|
t.references :model_a2,
foreign_key: {
to_table: :model_as_a2_aux,
primary_key: :base_table_id
}
end
end
create_table :model_cs do |t|
t.string :c_field1
t.references :model_a1,
foreign_key: {
to_table: :model_as_a1_aux,
primary_key: :base_table_id
}
end
create_base_table :model_ds do |t|
t.integer :ad_joins_count
t.create_aux :d1 do |t|
t.string :d1_field1
end
end
create_table :ad_joins, primary_key: %i[model_a_id model_d_id] do |t|
t.references :model_a, foreign_key: { to_table: :model_as }
t.references :model_d, foreign_key: { to_table: :model_ds }
end
# A* has_many B
# A1 has_many C
# A* has_many ADJoin
# B belongs_to A
# C belongs_to A1
# D* has_many ADJoin
# ADJoin belongs_to A
# ADJoin belongs_to D
end
class ModelA < ActiveRecord::Base
include HasAuxTable
has_many :model_bs, inverse_of: :model_a
has_many :ad_joins, inverse_of: :model_a
has_many :model_ds, through: :ad_joins
validates :a_field1, presence: true, uniqueness: true
end
class ModelA1 < ModelA
aux_table :a1
has_many :model_cs, inverse_of: :model_a1
validates :a1_field1, presence: true, uniqueness: true
end
class ModelA2 < ModelA
aux_table :a2
has_many :model_b2s, inverse_of: :model_a2
validates :a2_field1, presence: true, uniqueness: true
end
class ModelB < ActiveRecord::Base
include HasAuxTable
belongs_to :model_a, inverse_of: :model_bs, counter_cache: true
validates :b_field1, presence: true, uniqueness: true
end
class ModelB1 < ModelB
aux_table :b1
validates :b1_field1, presence: true, uniqueness: true
end
class ModelB2 < ModelB
aux_table :b2
belongs_to :model_a2, inverse_of: :model_b2s, counter_cache: true
end
class ModelC < ActiveRecord::Base
belongs_to :model_a1, inverse_of: :model_cs, counter_cache: true
validates :c_field1, presence: true, uniqueness: true
end
class ModelD < ActiveRecord::Base
include HasAuxTable
has_many :ad_joins, inverse_of: :model_d
has_many :model_as, through: :ad_joins
end
class ModelD1 < ModelD
aux_table :d1
validates :d1_field1, presence: true, uniqueness: true
end
class AdJoin < ActiveRecord::Base
belongs_to :model_a, inverse_of: :ad_joins, counter_cache: true
belongs_to :model_d, inverse_of: :ad_joins, counter_cache: true
end
ActiveRecord::Schema.define do
create_base_table :model_es do |t|
t.integer :pk_base_id, index: true
t.integer :fk_base_id, index: true
t.create_aux :e do |t|
t.integer :pk_aux_id, index: true
t.integer :fk_aux_id, index: true
end
end
end
class ModelE < ActiveRecord::Base
include HasAuxTable
end
class ModelECustom < ModelE
aux_table :e
belongs_to :base_to_base,
class_name: "ModelECustom",
foreign_key: :fk_base_id,
primary_key: :pk_base_id,
optional: true
belongs_to :base_to_aux,
class_name: "ModelECustom",
foreign_key: :fk_base_id,
primary_key: :pk_aux_id,
optional: true
belongs_to :aux_to_base,
class_name: "ModelECustom",
foreign_key: :fk_aux_id,
primary_key: :pk_base_id,
optional: true
belongs_to :aux_to_aux,
class_name: "ModelECustom",
foreign_key: :fk_aux_id,
primary_key: :pk_aux_id,
optional: true
has_one :owned_base_to_base,
class_name: "ModelECustom",
primary_key: :pk_base_id,
foreign_key: :fk_base_id
has_one :owned_base_to_aux,
class_name: "ModelECustom",
primary_key: :pk_base_id,
foreign_key: :fk_aux_id
has_one :owned_aux_to_base,
class_name: "ModelECustom",
primary_key: :pk_aux_id,
foreign_key: :fk_base_id
has_one :owned_aux_to_aux,
class_name: "ModelECustom",
primary_key: :pk_aux_id,
foreign_key: :fk_aux_id
has_many :owned_base_to_base_many,
class_name: "ModelECustom",
primary_key: :pk_base_id,
foreign_key: :fk_base_id
has_many :owned_base_to_aux_many,
class_name: "ModelECustom",
primary_key: :pk_base_id,
foreign_key: :fk_aux_id
has_many :owned_aux_to_base_many,
class_name: "ModelECustom",
primary_key: :pk_aux_id,
foreign_key: :fk_base_id
has_many :owned_aux_to_aux_many,
class_name: "ModelECustom",
primary_key: :pk_aux_id,
foreign_key: :fk_aux_id
end