From 8e1c19380108f8956a9b425751ea4e6f872d86c2 Mon Sep 17 00:00:00 2001 From: Dylan Knutson Date: Fri, 18 Jul 2025 05:51:24 +0000 Subject: [PATCH] spec refactor --- sorbet/config | 2 +- ...ux_table_spec.rb => has_aux_table_spec.rb} | 181 ++---------------- spec/spec_helper.rb | 105 ++++++++++ spec/spec_models.rb | 56 ++++++ 4 files changed, 179 insertions(+), 165 deletions(-) rename spec/active_record/{aux_table_spec.rb => has_aux_table_spec.rb} (84%) create mode 100644 spec/spec_models.rb diff --git a/sorbet/config b/sorbet/config index 36b8918..df1e53c 100644 --- a/sorbet/config +++ b/sorbet/config @@ -2,4 +2,4 @@ . --ignore=/tmp/ --ignore=/vendor/bundle ---enable-experimental-requires-ancestor \ No newline at end of file +--enable-experimental-requires-ancestor diff --git a/spec/active_record/aux_table_spec.rb b/spec/active_record/has_aux_table_spec.rb similarity index 84% rename from spec/active_record/aux_table_spec.rb rename to spec/active_record/has_aux_table_spec.rb index 76a65dd..48c7b86 100644 --- a/spec/active_record/aux_table_spec.rb +++ b/spec/active_record/has_aux_table_spec.rb @@ -1,155 +1,8 @@ +# typed: false # frozen_string_literal: true RSpec.describe HasAuxTable do - LOG_QUERIES = false - - # Helper method to count queries - def count_queries(&block) - query_count = 0 - query_callback = - lambda { |name, start, finish, message_id, values| query_count += 1 } - - ActiveSupport::Notifications.subscribed( - query_callback, - "sql.active_record" - ) do - if LOG_QUERIES - old_logger = ActiveRecord::Base.logger - ActiveRecord::Base.logger = Logger.new(STDOUT) - ActiveRecord::Base.logger.level = Logger::DEBUG - end - block.call - ActiveRecord::Base.logger = old_logger if LOG_QUERIES - end - - query_count - end - - before(:all) do - # 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 - - t.create_aux :boat do |t| - t.boolean :only_freshwater - end - end - - create_base_table :people do |t| - t.string :name - t.timestamps - - t.create_aux :driver do |t| - t.integer :license_number - t.references :car, - null: false, - 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 :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 - 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 Person < ActiveRecord::Base - include HasAuxTable - self.table_name = "people" - end - - class Driver < Person - aux_table :driver - belongs_to :car, inverse_of: :drivers - 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 - end + before(:all) { SpecHelper.initialize_spec_schema! } # Car class will be defined after schema setup @@ -270,7 +123,7 @@ RSpec.describe HasAuxTable do it "allows saving the model with auxiliary columns" do car = Car.create!(name: "Honda Civic") num_queries = - count_queries do + SpecHelper.count_queries do car.fuel_type = "hybrid" car.engine_size = 1.8 car.save! @@ -450,7 +303,7 @@ RSpec.describe HasAuxTable do it "loads single model with auxiliary data in one query using find" do query_count = - count_queries do + SpecHelper.count_queries do car = Car.find(@car1.id) # Access auxiliary attributes to ensure they're loaded car.fuel_type @@ -462,7 +315,7 @@ RSpec.describe HasAuxTable do it "loads single model with auxiliary data in one query using find_by" do query_count = - count_queries do + SpecHelper.count_queries do car = Car.find_by(name: "Toyota Prius") # Access auxiliary attributes to ensure they're loaded car.fuel_type @@ -474,7 +327,7 @@ RSpec.describe HasAuxTable do it "loads multiple models with auxiliary data in one query using where" do query_count = - count_queries do + SpecHelper.count_queries do cars = Car.where(fuel_type: %w[hybrid electric]) # Access auxiliary attributes for all cars cars.each do |car| @@ -499,7 +352,7 @@ RSpec.describe HasAuxTable do cars = nil query_count = - count_queries do + SpecHelper.count_queries do cars = Car.where(fuel_type: "gasoline") # Access auxiliary attributes for all cars - should not trigger additional queries cars.each do |car| @@ -515,7 +368,7 @@ RSpec.describe HasAuxTable do it "uses single query when ordering by auxiliary columns" do query_count = - count_queries do + SpecHelper.count_queries do cars = Car.where(engine_size: 1.0..3.0).order(:engine_size) # Access all attributes cars.each do |car| @@ -530,7 +383,7 @@ RSpec.describe HasAuxTable do it "uses single query for complex auxiliary column queries" do query_count = - count_queries do + SpecHelper.count_queries do cars = Car.where(fuel_type: "hybrid").or(Car.where(engine_size: 0.0)) # Access all attributes @@ -546,7 +399,7 @@ RSpec.describe HasAuxTable do it "uses single query when finding by auxiliary columns" do query_count = - count_queries do + SpecHelper.count_queries do car = Car.find_by(fuel_type: "hybrid", name: "Toyota Prius") # Access all attributes car.name @@ -563,7 +416,7 @@ RSpec.describe HasAuxTable do # Now count queries when accessing auxiliary attributes query_count = - count_queries do + SpecHelper.count_queries do car.fuel_type car.engine_size car.fuel_type? # presence check @@ -576,7 +429,7 @@ RSpec.describe HasAuxTable do it "handles mixed queries with main and auxiliary columns in single query" do query_count = - count_queries do + SpecHelper.count_queries do cars = Car.where(name: "Toyota Prius", fuel_type: "hybrid") cars.each do |car| car.name @@ -590,7 +443,7 @@ RSpec.describe HasAuxTable do it "uses single query for range queries on auxiliary columns" do query_count = - count_queries do + SpecHelper.count_queries do cars = Car.where(engine_size: 0.0..1.9) cars.each do |car| car.name @@ -604,7 +457,7 @@ RSpec.describe HasAuxTable do it "maintains single query performance with limit and offset" do query_count = - count_queries do + SpecHelper.count_queries do car = Car.where(fuel_type: %w[hybrid electric]).limit(1).first # Access auxiliary attributes car.fuel_type @@ -814,7 +667,7 @@ RSpec.describe HasAuxTable do end it "reloads with one query" do - num_queries = count_queries { @car.reload } + num_queries = SpecHelper.count_queries { @car.reload } expect(num_queries).to eq(1) end end @@ -846,12 +699,12 @@ RSpec.describe HasAuxTable do expect(Car.count).to eq(1) expect(Boat.count).to eq(1) - expect(count_queries { car = Vehicle.find(car.id) }).to eq(1) + expect(SpecHelper.count_queries { car = Vehicle.find(car.id) }).to eq(1) expect(car.fuel_type).to eq("gasoline") expect(car.engine_size).to eq(2.0) expect(car.name).to eq("Honda Civic") - expect(count_queries { boat = Vehicle.find(boat.id) }).to eq(1) + expect(SpecHelper.count_queries { boat = Vehicle.find(boat.id) }).to eq(1) expect(boat.only_freshwater).to eq(true) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index bf93242..4be4a1a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,4 @@ +# typed: strict # frozen_string_literal: true require "active_record" @@ -29,3 +30,107 @@ RSpec.configure do |config| end end end + +module SpecHelper + extend T::Sig + extend T::Helpers + LOG_QUERIES = T.let(false, T::Boolean) + + # Helper method to count queries + sig { params(block: T.proc.void).returns(Integer) } + def self.count_queries(&block) + query_count = 0 + query_callback = + lambda { |name, start, finish, message_id, values| query_count += 1 } + + ActiveSupport::Notifications.subscribed( + query_callback, + "sql.active_record" + ) do + if LOG_QUERIES + old_logger = ActiveRecord::Base.logger + ActiveRecord::Base.logger = Logger.new(STDOUT) + ActiveRecord::Base.logger.level = Logger::DEBUG + end + block.call + ActiveRecord::Base.logger = old_logger if LOG_QUERIES + end + + query_count + end + + sig { void } + def self.initialize_spec_schema! + # 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 + + t.create_aux :boat do |t| + t.boolean :only_freshwater + end + end + + create_base_table :people do |t| + t.string :name + t.timestamps + + t.create_aux :driver do |t| + t.integer :license_number + t.references :car, + null: false, + 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 :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 + + require_relative "spec_models" + end + end +end diff --git a/spec/spec_models.rb b/spec/spec_models.rb new file mode 100644 index 0000000..ae6dbe9 --- /dev/null +++ b/spec/spec_models.rb @@ -0,0 +1,56 @@ +# typed: strict +# frozen_string_literal: true + +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 Person < ActiveRecord::Base + include HasAuxTable + self.table_name = "people" +end + +class Driver < Person + aux_table :driver + belongs_to :car, inverse_of: :drivers +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