cache bundle install at devcontainer build step

This commit is contained in:
Dylan Knutson
2025-07-20 18:24:51 +00:00
parent 8854dddb4a
commit 3a8d71e2f7
20 changed files with 3114 additions and 39 deletions

View File

@@ -21,6 +21,10 @@ RSpec.describe HasAuxTable do
end
describe "column reporting" do
it "reports columns of the base class" do
expect(Vehicle.inspect).to include("name")
end
it "reports the correct columns on the string repr of the class" do
expect(Car.inspect).to include("fuel_type")
end
@@ -106,7 +110,7 @@ RSpec.describe HasAuxTable do
end
it "can set association on aux record" do
driver = Driver.create!(name: "John Doe")
driver = Driver.create!(name: "John Doe", license_number: 12_345)
car = Car.create!(name: "Honda Civic")
driver.car = car
expect(driver.car).to eq(car)
@@ -142,6 +146,20 @@ RSpec.describe HasAuxTable do
expect(plane.engine_type).to eq("turboprop")
end
describe "validations" do
it "validates the main record" do
driver = Driver.create!(name: "John Doe", license_number: 12_345)
expect(driver.valid?).to be_truthy
driver.name = nil
expect(driver.valid?).to be_falsey
end
it "validates through an association" do
car = Car.create!(name: "Honda Civic")
car.drivers.create!(name: "John Doe", license_number: 12_345)
end
end
describe "#changed?" do
it "returns true if the main record changes" do
car = Car.create!(name: "Honda Civic")
@@ -361,10 +379,7 @@ RSpec.describe HasAuxTable do
end
it "works with chained where clauses" do
# Chain where clauses with auxiliary columns
efficient_cars = Car.where(fuel_type: "hybrid").where(engine_size: 1.8)
expect(efficient_cars.length).to eq(1)
expect(efficient_cars.first.name).to eq("Toyota Prius")
end
@@ -377,6 +392,12 @@ RSpec.describe HasAuxTable do
car_names = cars.map(&:name).sort
expect(car_names).to eq(["Tesla Model 3", "Toyota Prius"])
end
it "works when sql is passed to where" do
cars = Car.where("fuel_type = 'hybrid'")
expect(cars.length).to eq(1)
expect(cars.first.name).to eq("Toyota Prius")
end
end
describe "query performance and optimization" do
@@ -689,7 +710,7 @@ RSpec.describe HasAuxTable do
describe "nested associations" do
it "can create a driver through the association" do
driver = @car.drivers.create!(name: "John Doe")
driver = @car.drivers.create!(name: "John Doe", license_number: 123_456)
expect(driver.car).to eq(@car)
expect(driver.car_id).to eq(@car.id)
expect(driver.car.fuel_type).to eq("hybrid")
@@ -708,7 +729,8 @@ RSpec.describe HasAuxTable do
end
it "can create a driver directly" do
driver = Driver.create!(car: @car, name: "John Doe")
driver =
Driver.create!(car: @car, name: "John Doe", license_number: 123_456)
expect(driver.car).to eq(@car)
expect(driver.car_id).to eq(@car.id)
expect(driver.car.fuel_type).to eq("hybrid")
@@ -716,12 +738,12 @@ RSpec.describe HasAuxTable do
end
it "can be accessed through the association" do
driver = @car.drivers.create!(name: "John Doe")
driver = @car.drivers.create!(name: "John Doe", license_number: 123_456)
expect(@car.drivers).to eq([driver])
end
it "can be destroyed through the association" do
driver = @car.drivers.create!(name: "John Doe")
driver = @car.drivers.create!(name: "John Doe", license_number: 123_456)
expect { driver.destroy }.to change { @car.reload.drivers.count }.by(-1)
end
@@ -736,6 +758,15 @@ RSpec.describe HasAuxTable do
d = drivers.find_by(license_number: 123_456)
expect(d.id).to eq(driver.id)
end
# it "can create STI models through associations" do
# user =
# TwitterUser.create!(last_login_at: Time.now, twitter_handle: "a_user")
# post = user.posts.create!(title: "twitter post")
# expect(post.user).to eq(user)
# expect(post.user_id).to eq(user.id)
# expect(post.user.twitter_handle).to eq("a_user")
# end
end
describe "#reload" do
@@ -768,7 +799,7 @@ RSpec.describe HasAuxTable do
it "reloads associations" do
expect(@car.drivers.length).to eq(0)
Driver.create!(car: @car, name: "Billy Kid")
Driver.create!(car: @car, name: "Billy Kid", license_number: 123_456)
expect(@car.drivers.length).to eq(0)
expect(@car.drivers.count).to eq(1)
@@ -869,4 +900,15 @@ RSpec.describe HasAuxTable do
expect(@book.read_book_joins.count).to eq(1)
end
end
describe "joins model with aux tables" do
it "can create a join record" do
doctor = Person.create!(name: "Dr. John Doe")
patient = Person.create!(name: "Jane Doe")
assoc = doctor.patients
assoc << patient
expect(doctor.patients.count).to eq(1)
expect(patient.doctors.count).to eq(1)
end
end
end

View File

@@ -1,10 +1,31 @@
# typed: strict
# frozen_string_literal: true
require "simplecov"
require "simplecov-lcov"
SimpleCov::Formatter::LcovFormatter.config do |c|
c.report_with_single_file = true
c.lcov_file_name = "lcov.info"
end
SimpleCov.start do
enable_coverage :branch
add_filter "spec"
formatter(
SimpleCov::Formatter::MultiFormatter.new(
[
SimpleCov::Formatter::LcovFormatter, # Add Lcov as an output when generating code coverage report
SimpleCov::Formatter::HTMLFormatter # Add other outputs for the code coverage report
]
)
)
end
require "pry"
require "active_record"
require "active_record/errors"
require "has_aux_table"
require "pry"
# Configure ActiveRecord to use in-memory SQLite database
ActiveRecord::Base.establish_connection(
@@ -21,7 +42,7 @@ RSpec.configure do |config|
# Disable RSpec exposing methods globally on `Module` and `main`
config.disable_monkey_patching!
# config.backtrace_inclusion_patterns = [/\bactiverecord\b/]
config.backtrace_inclusion_patterns = [/\bactiverecord\b/]
config.expect_with :rspec do |c|
c.syntax = :expect

View File

@@ -18,7 +18,9 @@ ActiveRecord::Schema.define do
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
@@ -35,7 +37,7 @@ ActiveRecord::Schema.define do
t.timestamps
t.create_aux :driver do |t|
t.integer :license_number
t.integer :license_number, index: true, null: false
t.references :car,
foreign_key: {
to_table: :vehicles_car_aux,
@@ -62,6 +64,20 @@ ActiveRecord::Schema.define do
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
@@ -127,12 +143,82 @@ class Plane < Vehicle
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
@@ -177,3 +263,52 @@ 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
# class TwitterUser < User
# aux_table :twitter
# validates :twitter_handle, presence: true
# has_many :posts, inverse_of: :user, class_name: "TwitterPost"
# end
# class RedditUser < User
# aux_table :reddit
# validates :reddit_handle, presence: true
# has_many :posts, inverse_of: :user, class_name: "RedditPost"
# end
# class Post < ActiveRecord::Base
# include HasAuxTable
# belongs_to :user, inverse_of: :posts
# end
# class TwitterPost < Post
# aux_table :twitter
# belongs_to :user, inverse_of: :posts, class_name: "TwitterUser"
# end
# class RedditPost < Post
# aux_table :reddit
# belongs_to :user, inverse_of: :posts, class_name: "RedditUser"
# end