1301 lines
41 KiB
Ruby
1301 lines
41 KiB
Ruby
# typed: false
|
|
# frozen_string_literal: true
|
|
|
|
RSpec.describe HasAuxTable do
|
|
# Car class will be defined after schema setup
|
|
|
|
it "has a version number" do
|
|
expect(HasAuxTable::VERSION).not_to be nil
|
|
end
|
|
|
|
it "can create STI records" do
|
|
car = Car.create!(name: "Toyota Camry", type: "Car")
|
|
expect(car).to be_persisted
|
|
|
|
boat = Boat.create!(name: "Yacht", type: "Boat")
|
|
expect(boat).to be_persisted
|
|
end
|
|
|
|
it "is a clean test environment" do
|
|
expect(Vehicle.count).to eq(0)
|
|
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
|
|
|
|
it "does not include the aux table foreign key" do
|
|
expect(Car.inspect).not_to include("base_table_id")
|
|
end
|
|
|
|
it "reports created_at, updated_at timestamp columns at the end of the list" do
|
|
expect(Car.inspect).to match(/\bfuel_type\b.+\bupdated_at\b/)
|
|
expect(Car.inspect).to match(/\bname\b.+\bupdated_at\b/)
|
|
end
|
|
|
|
it "includes columns in instances of the model" do
|
|
car = Car.create!(name: "Honda Civic")
|
|
expect(car.inspect).to include("fuel_type")
|
|
expect(car.inspect).to include("engine_size")
|
|
expect(car.inspect).to include("created_at")
|
|
expect(car.inspect).to include("updated_at")
|
|
end
|
|
|
|
it "puts _at columns at the end of the list on instances" do
|
|
car = Car.create!(name: "Honda Civic")
|
|
expect(car.inspect).to match(/\bfuel_type\b.+\bupdated_at\b/)
|
|
expect(car.inspect).to match(/\bname\b.+\bupdated_at\b/)
|
|
end
|
|
end
|
|
|
|
it "can be created with .new" do
|
|
car = Car.new
|
|
car.name = "Honda Civic"
|
|
car.engine_size = 1.8
|
|
car.save!
|
|
expect(car.fuel_type).to be_nil
|
|
expect(car.engine_size).to eq(1.8)
|
|
expect(car.name).to eq("Honda Civic")
|
|
end
|
|
|
|
it "has the right #attributes" do
|
|
car =
|
|
Car.create!(name: "Honda Civic", fuel_type: "gasoline", engine_size: 2.0)
|
|
expect(car.attributes).to match(
|
|
hash_including(
|
|
"type" => "Car",
|
|
"id" => car.id,
|
|
"name" => "Honda Civic",
|
|
"fuel_type" => "gasoline",
|
|
"engine_size" => be_within(0.001).of(2.0),
|
|
"created_at" => be_within(0.001).of(car.created_at),
|
|
"updated_at" => be_within(0.001).of(car.updated_at)
|
|
)
|
|
)
|
|
end
|
|
|
|
it "reads attributes with read_attribute" do
|
|
car = Car.create!(name: "Honda Civic", fuel_type: "gasoline")
|
|
expect(car.read_attribute("name")).to eq("Honda Civic")
|
|
expect(car.read_attribute(:name)).to eq("Honda Civic")
|
|
expect(car.read_attribute("fuel_type")).to eq("gasoline")
|
|
expect(car.read_attribute(:fuel_type)).to eq("gasoline")
|
|
end
|
|
|
|
it "can be created as the base class" do
|
|
vehicle = Vehicle.create(type: "Vehicle", name: "big tractor")
|
|
expect(vehicle.attributes).to match(
|
|
hash_including(
|
|
"type" => "Vehicle",
|
|
"id" => vehicle.id,
|
|
"name" => "big tractor",
|
|
"created_at" => be_within(0.001).of(vehicle.created_at),
|
|
"updated_at" => be_within(0.001).of(vehicle.updated_at)
|
|
)
|
|
)
|
|
end
|
|
|
|
it "can be created through an association" do
|
|
lot = VehicleLot.create(name: "lot1")
|
|
lot.vehicles.create { |b| b.name = "vehicle1" }
|
|
lot.save!
|
|
lot.reload
|
|
expect(lot.vehicles.count).to eq(1)
|
|
expect(lot.vehicles.first.name).to eq("vehicle1")
|
|
end
|
|
|
|
it "can set association on aux record" do
|
|
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)
|
|
expect(driver.car_id).to eq(car.id)
|
|
driver.save!
|
|
|
|
driver = Driver.find(driver.id)
|
|
expect(driver.car).to eq(car)
|
|
end
|
|
|
|
it "defined_enums returns the correct values" do
|
|
engine_types = {
|
|
"turbofan" => 0,
|
|
"turboprop" => 1,
|
|
"piston" => 2,
|
|
"electric" => 3
|
|
}
|
|
expect(Plane.defined_enums).to eq({ "engine_type" => engine_types })
|
|
expect(Plane.engine_types).to eq(engine_types)
|
|
end
|
|
|
|
it "works with enums" do
|
|
plane = Plane.create!(name: "Boeing 747", engine_type: :turbofan)
|
|
expect(plane.engine_type).to eq("turbofan")
|
|
|
|
plane.engine_type = "piston"
|
|
expect(plane.engine_type).to eq("piston")
|
|
plane.save!
|
|
expect(plane.engine_type).to eq("piston")
|
|
expect(plane.piston?).to be_truthy
|
|
|
|
plane.turboprop!
|
|
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")
|
|
expect(car.changed?).to be_falsey
|
|
car.name = "Toyota Camry"
|
|
expect(car.changed?).to be_truthy
|
|
end
|
|
|
|
it "returns true if the aux record changes" do
|
|
car = Car.create!(name: "Honda Civic")
|
|
expect(car.changed?).to be_falsey
|
|
car.fuel_type = "hybrid"
|
|
expect(car.changed?).to be_truthy
|
|
end
|
|
end
|
|
|
|
describe "#changed_attributes" do
|
|
# changed_attributes returns a hash with the original values of the attribute
|
|
|
|
it "returns the changed attributes of the main record" do
|
|
car = Car.create!(name: "Honda Civic")
|
|
expect(car.changed_attributes).to eq({})
|
|
car.name = "Toyota Camry"
|
|
expect(car.changed_attributes).to eq({ "name" => "Honda Civic" })
|
|
end
|
|
|
|
it "returns the changed attributes of the aux record when original is nil" do
|
|
car = Car.create!(name: "Honda Civic")
|
|
expect(car.changed_attributes).to eq({})
|
|
car.fuel_type = "hybrid"
|
|
expect(car.changed_attributes).to eq({ "fuel_type" => nil })
|
|
end
|
|
|
|
it "returns the changed attributes of the aux record when original is not nil" do
|
|
car = Car.create!(name: "Honda Civic", fuel_type: "gasoline")
|
|
expect(car.changed_attributes).to eq({})
|
|
car.fuel_type = "hybrid"
|
|
expect(car.changed_attributes).to eq({ "fuel_type" => "gasoline" })
|
|
end
|
|
end
|
|
|
|
describe "database integration" do
|
|
it "provides automatic attribute accessors for auxiliary table columns" do
|
|
vehicle = Car.create!(name: "Honda Civic")
|
|
|
|
# Test getter methods (should return nil initially)
|
|
expect(vehicle.fuel_type).to be_nil
|
|
expect(vehicle.engine_size).to be_nil
|
|
|
|
# Test presence check methods
|
|
expect(vehicle.fuel_type?).to be_falsey
|
|
expect(vehicle.engine_size?).to be_falsey
|
|
|
|
# Test setter methods (should create auxiliary record automatically)
|
|
vehicle.fuel_type = "hybrid"
|
|
vehicle.engine_size = 1.8
|
|
|
|
# Test that values are set correctly
|
|
expect(vehicle.fuel_type).to eq("hybrid")
|
|
expect(vehicle.engine_size).to eq(1.8)
|
|
|
|
# Test presence check methods after setting values
|
|
expect(vehicle.fuel_type?).to be_truthy
|
|
expect(vehicle.engine_size?).to be_truthy
|
|
|
|
# Save and reload to verify persistence
|
|
vehicle.save!
|
|
reloaded_vehicle = Car.find(vehicle.id)
|
|
expect(reloaded_vehicle.fuel_type).to eq("hybrid")
|
|
expect(reloaded_vehicle.engine_size).to eq(1.8)
|
|
end
|
|
|
|
it "allows saving the model with auxiliary columns" do
|
|
car = Car.create!(name: "Honda Civic")
|
|
queries =
|
|
capture_queries do
|
|
car.fuel_type = "hybrid"
|
|
car.engine_size = 1.8
|
|
car.save!
|
|
end
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe "query extensions" do
|
|
before do
|
|
# Create test data
|
|
@car1 =
|
|
Car.create!(
|
|
name: "Toyota Prius",
|
|
type: "Car",
|
|
fuel_type: "hybrid",
|
|
engine_size: 1.8
|
|
)
|
|
|
|
@car2 =
|
|
Car.create!(
|
|
name: "Honda Civic",
|
|
type: "Car",
|
|
fuel_type: "gasoline",
|
|
engine_size: 2.0
|
|
)
|
|
|
|
@car3 =
|
|
Car.create!(
|
|
name: "Tesla Model 3",
|
|
type: "Car",
|
|
fuel_type: "electric",
|
|
engine_size: 0.0
|
|
)
|
|
end
|
|
|
|
describe "find method with automatic joins" do
|
|
it "automatically includes auxiliary table joins for find" do
|
|
# Find should automatically include joins to load auxiliary data
|
|
found_car = Car.find(@car1.id)
|
|
|
|
# Auxiliary attributes should be accessible
|
|
expect(found_car.fuel_type).to eq("hybrid")
|
|
expect(found_car.engine_size).to eq(1.8)
|
|
expect(found_car.name).to eq("Toyota Prius")
|
|
end
|
|
|
|
it "works with multiple IDs" do
|
|
cars = Car.find([@car1.id, @car2.id])
|
|
expect(cars.length).to eq(2)
|
|
|
|
# All cars should have auxiliary data loaded
|
|
prius = cars.find { |c| c.name == "Toyota Prius" }
|
|
civic = cars.find { |c| c.name == "Honda Civic" }
|
|
|
|
expect(prius.fuel_type).to eq("hybrid")
|
|
expect(civic.fuel_type).to eq("gasoline")
|
|
end
|
|
end
|
|
|
|
describe "find_by method with automatic joins" do
|
|
it "automatically includes auxiliary table joins for find_by" do
|
|
# Find_by should automatically include joins for auxiliary data
|
|
found_car = Car.find_by(name: "Toyota Prius")
|
|
|
|
expect(found_car).to be_present
|
|
expect(found_car.fuel_type).to eq("hybrid")
|
|
expect(found_car.engine_size).to eq(1.8)
|
|
end
|
|
|
|
it "works with auxiliary columns in find_by" do
|
|
# This should work with auxiliary columns due to automatic join
|
|
found_car = Car.find_by(fuel_type: "hybrid")
|
|
|
|
expect(found_car).to be_present
|
|
expect(found_car.name).to eq("Toyota Prius")
|
|
expect(found_car.fuel_type).to eq("hybrid")
|
|
end
|
|
|
|
it "returns nil when no record found" do
|
|
found_car = Car.find_by(fuel_type: "diesel")
|
|
expect(found_car).to be_nil
|
|
end
|
|
|
|
it "works with find_by!" do
|
|
expect { Car.find_by!(fuel_type: "diesel") }.to raise_error(
|
|
ActiveRecord::RecordNotFound
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "where method with automatic joins" do
|
|
it "automatically handles auxiliary columns in where clauses" do
|
|
# Query with auxiliary column should automatically include join
|
|
hybrid_cars = Car.where(fuel_type: "hybrid")
|
|
expect(hybrid_cars.length).to eq(1)
|
|
expect(hybrid_cars.first.name).to eq("Toyota Prius")
|
|
expect(hybrid_cars.first.fuel_type).to eq("hybrid")
|
|
end
|
|
|
|
it "works with multiple auxiliary columns" do
|
|
# Query with multiple auxiliary columns
|
|
efficient_cars = Car.where(fuel_type: "hybrid", engine_size: 1.8)
|
|
|
|
expect(efficient_cars.length).to eq(1)
|
|
expect(efficient_cars.first.name).to eq("Toyota Prius")
|
|
end
|
|
|
|
it "handles range queries on auxiliary columns" do
|
|
# Range query on auxiliary column
|
|
small_engine_cars = Car.where(engine_size: 0.0..1.9)
|
|
|
|
expect(small_engine_cars.length).to eq(2)
|
|
car_names = small_engine_cars.map(&:name).sort
|
|
expect(car_names).to eq(["Tesla Model 3", "Toyota Prius"])
|
|
end
|
|
|
|
it "supports mixed queries with main table and auxiliary table columns" do
|
|
# Mixed query with both main table and auxiliary table columns
|
|
prius_hybrids = Car.where(name: "Toyota Prius", fuel_type: "hybrid")
|
|
|
|
expect(prius_hybrids.length).to eq(1)
|
|
expect(prius_hybrids.first.name).to eq("Toyota Prius")
|
|
expect(prius_hybrids.first.fuel_type).to eq("hybrid")
|
|
end
|
|
|
|
it "handles IN queries on auxiliary columns" do
|
|
# IN query on auxiliary column
|
|
eco_cars = Car.where(fuel_type: %w[hybrid electric])
|
|
|
|
expect(eco_cars.length).to eq(2)
|
|
car_names = eco_cars.map(&:name).sort
|
|
expect(car_names).to eq(["Tesla Model 3", "Toyota Prius"])
|
|
end
|
|
|
|
it "doesn't add joins for queries without auxiliary columns" do
|
|
toyota_cars = Car.where(name: "Toyota Prius")
|
|
expect(toyota_cars.length).to eq(1)
|
|
expect(toyota_cars.first.name).to eq("Toyota Prius")
|
|
expect(toyota_cars.first.fuel_type).to eq("hybrid")
|
|
end
|
|
|
|
it "works with chained where clauses" do
|
|
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
|
|
|
|
it "supports complex query combinations" do
|
|
# Complex query with OR conditions
|
|
cars = Car.where(fuel_type: "hybrid").or(Car.where(engine_size: 0.0))
|
|
|
|
expect(cars.length).to eq(2)
|
|
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
|
|
|
|
it "works for .not queries" do
|
|
cars = Car.where.not(fuel_type: "hybrid")
|
|
expect(cars.length).to eq(2)
|
|
expect(cars.map(&:name)).to eq(["Honda Civic", "Tesla Model 3"])
|
|
end
|
|
end
|
|
|
|
describe "query performance and optimization" do
|
|
it "loads auxiliary data in single query with joins" do
|
|
# This test ensures we're using joins rather than N+1 queries
|
|
cars = Car.where(fuel_type: "gasoline")
|
|
|
|
# Should have loaded auxiliary data via join
|
|
expect(cars.length).to eq(1)
|
|
expect(cars.first.name).to eq("Honda Civic")
|
|
expect(cars.first.fuel_type).to eq("gasoline")
|
|
expect(cars.first.engine_size).to eq(2.0)
|
|
end
|
|
|
|
describe "query count validation" do
|
|
# These tests validate the performance optimizations using eager_load
|
|
# All query methods now use single queries with proper LEFT OUTER JOINs
|
|
|
|
it "loads single model with auxiliary data in one query using find" do
|
|
queries =
|
|
capture_queries do
|
|
car = Car.find(@car1.id)
|
|
# Access auxiliary attributes to ensure they're loaded
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "loads single model with auxiliary data in one query using find_by" do
|
|
queries =
|
|
capture_queries do
|
|
car = Car.find_by(name: "Toyota Prius")
|
|
# Access auxiliary attributes to ensure they're loaded
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "loads multiple models with auxiliary data in one query using where" do
|
|
queries =
|
|
capture_queries do
|
|
cars = Car.where(fuel_type: %w[hybrid electric])
|
|
# Access auxiliary attributes for all cars
|
|
cars.each do |car|
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "avoids N+1 queries when loading multiple models" do
|
|
# Create additional test data
|
|
|
|
5.times do |i|
|
|
Car.create!(
|
|
name: "Test Car #{i}",
|
|
fuel_type: "gasoline",
|
|
engine_size: 1.5
|
|
)
|
|
end
|
|
|
|
cars = nil
|
|
queries =
|
|
capture_queries do
|
|
cars = Car.where(fuel_type: "gasoline")
|
|
# Access auxiliary attributes for all cars - should not trigger additional queries
|
|
cars.each do |car|
|
|
car.fuel_type
|
|
car.engine_size
|
|
car.name
|
|
end
|
|
end
|
|
|
|
expect(queries.length).to eq(1) # Single query regardless of how many cars are loaded
|
|
expect(cars.length).to be >= 1 # At least the original Honda Civic plus new cars
|
|
end
|
|
|
|
it "uses single query when ordering by auxiliary columns" do
|
|
queries =
|
|
capture_queries do
|
|
cars = Car.where(engine_size: 1.0..3.0).order(:engine_size)
|
|
# Access all attributes
|
|
cars.each do |car|
|
|
car.name
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "uses single query for complex auxiliary column queries" do
|
|
queries =
|
|
capture_queries do
|
|
cars =
|
|
Car.where(fuel_type: "hybrid").or(Car.where(engine_size: 0.0))
|
|
# Access all attributes
|
|
cars.each do |car|
|
|
car.name
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "uses single query when finding by auxiliary columns" do
|
|
queries =
|
|
capture_queries do
|
|
car = Car.find_by(fuel_type: "hybrid", name: "Toyota Prius")
|
|
# Access all attributes
|
|
car.name
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "doesn't trigger additional queries when accessing auxiliary attributes after load" do
|
|
# First load the car
|
|
car = Car.find(@car1.id)
|
|
|
|
# Now count queries when accessing auxiliary attributes
|
|
queries =
|
|
capture_queries do
|
|
car.fuel_type
|
|
car.engine_size
|
|
car.fuel_type? # presence check
|
|
car.engine_size? # presence check
|
|
end
|
|
|
|
# Currently this should be 0 since auxiliary record is already loaded
|
|
expect(queries.length).to eq(0) # No additional queries should be triggered
|
|
end
|
|
|
|
it "handles mixed queries with main and auxiliary columns in single query" do
|
|
queries =
|
|
capture_queries do
|
|
cars = Car.where(name: "Toyota Prius", fuel_type: "hybrid")
|
|
cars.each do |car|
|
|
car.name
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "uses single query for range queries on auxiliary columns" do
|
|
queries =
|
|
capture_queries do
|
|
cars = Car.where(engine_size: 0.0..1.9)
|
|
cars.each do |car|
|
|
car.name
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "maintains single query performance with limit and offset" do
|
|
queries =
|
|
capture_queries do
|
|
car = Car.where(fuel_type: %w[hybrid electric]).limit(1).first
|
|
# Access auxiliary attributes
|
|
car.fuel_type
|
|
car.engine_size
|
|
end
|
|
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
end
|
|
|
|
it "works with order clauses on auxiliary columns" do
|
|
# Order by auxiliary column
|
|
cars_by_engine = Car.where(engine_size: 1.0..3.0).order(:engine_size)
|
|
|
|
expect(cars_by_engine.length).to eq(2)
|
|
expect(cars_by_engine.first.name).to eq("Toyota Prius")
|
|
expect(cars_by_engine.second.name).to eq("Honda Civic")
|
|
end
|
|
|
|
it "supports limit and offset with auxiliary columns" do
|
|
# Limit with auxiliary column query
|
|
first_hybrid = Car.where(fuel_type: %w[hybrid electric]).limit(1).first
|
|
|
|
expect(first_hybrid).to be_present
|
|
expect(["Toyota Prius", "Tesla Model 3"]).to include(first_hybrid.name)
|
|
end
|
|
end
|
|
|
|
describe "edge cases and error handling" do
|
|
it "handles queries with non-existent auxiliary columns gracefully" do
|
|
# This should not break and should fall back to normal query behavior
|
|
expect { Car.where(non_existent_column: "value").first }.to raise_error(
|
|
ActiveRecord::StatementInvalid
|
|
)
|
|
end
|
|
|
|
it "works with empty where conditions" do
|
|
# Empty where should not cause issues
|
|
cars = Car.where({})
|
|
expect(cars.length).to eq(3)
|
|
end
|
|
|
|
it "handles nil values in auxiliary columns" do
|
|
# Create a car with nil auxiliary values
|
|
Car.create!(name: "Incomplete Car", type: "Car")
|
|
Car.create!(
|
|
name: "Complete Car",
|
|
fuel_type: "gasoline",
|
|
engine_size: 2.0
|
|
)
|
|
|
|
# Query for cars with nil fuel_type
|
|
incomplete_cars = Car.where(fuel_type: nil)
|
|
expect(incomplete_cars.length).to eq(1)
|
|
expect(incomplete_cars.first.name).to eq("Incomplete Car")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "column overlap validation" do
|
|
it "raises error when auxiliary table defines column that exists in main table" do
|
|
# Create a test schema with overlapping columns
|
|
ActiveRecord::Schema.define do
|
|
create_base_table :test_overlap_mains do |t|
|
|
t.string :name
|
|
t.string :description
|
|
|
|
t.create_aux :overlap do |t|
|
|
t.string :name
|
|
t.string :extra_data
|
|
end
|
|
|
|
t.timestamps
|
|
end
|
|
end
|
|
|
|
# Define models that will trigger the validation
|
|
class TestOverlapMain < ActiveRecord::Base
|
|
include HasAuxTable
|
|
end
|
|
|
|
expect {
|
|
class TestOverlapChild < TestOverlapMain
|
|
aux_table :overlap
|
|
end
|
|
|
|
# Trigger schema loading to activate validation
|
|
TestOverlapChild.load_schema
|
|
}.to raise_error(ArgumentError, /defines column\(s\) 'name'/)
|
|
end
|
|
|
|
it "ignores system columns and foreign keys when checking for overlaps" do
|
|
# Create a test schema where system columns are duplicated (which should be allowed)
|
|
ActiveRecord::Schema.define do
|
|
create_base_table :test_system_cols_main do |t|
|
|
t.string :name
|
|
|
|
t.create_aux :has_timestamps do |t|
|
|
t.timestamps
|
|
end
|
|
|
|
t.timestamps
|
|
end
|
|
end
|
|
|
|
class TestSystemColsMain < ActiveRecord::Base
|
|
include HasAuxTable
|
|
end
|
|
|
|
expect {
|
|
class TestSystemColsChild < TestSystemColsMain
|
|
aux_table :has_timestamps
|
|
end
|
|
}.not_to raise_error
|
|
end
|
|
end
|
|
|
|
describe "methods that depend on relation" do
|
|
before(:each) do
|
|
@car =
|
|
Car.create!(name: "Toyota Prius", fuel_type: "hybrid", engine_size: 1.5)
|
|
end
|
|
|
|
describe "destroy" do
|
|
it "destroys the main record" do
|
|
expect { @car.destroy }.to change { Car.count }.by(-1)
|
|
end
|
|
|
|
it "destroys the aux record" do
|
|
expect { @car.destroy }.to change {
|
|
Object.const_get(:VehiclesCarAux).count
|
|
}.by(-1)
|
|
end
|
|
end
|
|
|
|
describe "associations" do
|
|
it "can create a driver through the association" do
|
|
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")
|
|
expect(driver.car.engine_size).to eq(1.5)
|
|
end
|
|
|
|
it "executes the hooks when creating through the association" do
|
|
driver =
|
|
@car
|
|
.drivers
|
|
.create!(name: "John Doe") do |driver|
|
|
driver.license_number = 123_456
|
|
end
|
|
|
|
expect(driver.license_number).to eq(123_456)
|
|
end
|
|
|
|
it "can create a driver directly" do
|
|
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")
|
|
expect(driver.car.engine_size).to eq(1.5)
|
|
end
|
|
|
|
it "can be accessed through the association" do
|
|
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", license_number: 123_456)
|
|
expect { driver.destroy }.to change { @car.reload.drivers.count }.by(-1)
|
|
end
|
|
|
|
it "can be queried through the association" do
|
|
driver = @car.drivers.create!(name: "John Doe", license_number: 123_456)
|
|
expect(@car.drivers.where(name: "John Doe")).to eq([driver])
|
|
|
|
drivers = @car.drivers
|
|
d = drivers.find_by!(license_number: 123_456)
|
|
expect(d.id).to eq(driver.id)
|
|
|
|
d = drivers.find_by(license_number: 123_456)
|
|
expect(d.id).to eq(driver.id)
|
|
end
|
|
|
|
it "can have the association queried when fk is on the main table" do
|
|
lot = VehicleLot.create!(name: "Lot 1")
|
|
nolot_car = @car
|
|
lot_car = Car.create!(name: "Car 1", vehicle_lot: lot)
|
|
|
|
expect(Car.where(vehicle_lot: lot)).to eq([lot_car])
|
|
expect(Car.where(vehicle_lot: nil)).to eq([nolot_car])
|
|
end
|
|
|
|
it "can have the association queried when fk is on the aux table" do
|
|
driver1 =
|
|
Driver.create!(name: "John Doe", license_number: 123, car: @car)
|
|
driver2 = Driver.create!(name: "Jane Goodall", license_number: 456)
|
|
nodriver_car = Car.create!(name: "No Driver Car")
|
|
|
|
expect(Driver.where(car: @car)).to eq([driver1])
|
|
expect(Driver.where(car: nil)).to eq([driver2])
|
|
expect(Driver.where(car: nodriver_car)).to eq([])
|
|
end
|
|
|
|
describe "custom foreign and primary keys" do
|
|
before(:each) do
|
|
@e1 =
|
|
ModelECustom.create!(
|
|
pk_base_id: 1,
|
|
fk_base_id: 2,
|
|
pk_aux_id: 3,
|
|
fk_aux_id: 4
|
|
)
|
|
@e2 =
|
|
ModelECustom.create!(
|
|
pk_base_id: 5,
|
|
fk_base_id: 6,
|
|
pk_aux_id: 7,
|
|
fk_aux_id: 8
|
|
)
|
|
end
|
|
|
|
describe "belongs_to association" do
|
|
it "works between base and base" do
|
|
# e1.fk_base_id <- e2.pk_base_id
|
|
@e1.base_to_base = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.fk_base_id).to eq(5)
|
|
expect(@e2.pk_base_id).to eq(5)
|
|
expect(ModelECustom.where(base_to_base: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(base_to_base: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between base and aux" do
|
|
# e1.fk_base_id <- e2.pk_aux_id
|
|
@e1.base_to_aux = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.fk_base_id).to eq(7)
|
|
expect(@e2.pk_aux_id).to eq(7)
|
|
expect(ModelECustom.where(base_to_aux: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(base_to_aux: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between aux and base" do
|
|
# e1.fk_aux_id <- e2.pk_base_id
|
|
@e1.aux_to_base = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.fk_aux_id).to eq(5)
|
|
expect(@e2.pk_base_id).to eq(5)
|
|
expect(ModelECustom.where(aux_to_base: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(aux_to_base: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between aux and aux" do
|
|
# e1.fk_aux_id <- e2.pk_aux_id
|
|
@e1.aux_to_aux = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.fk_aux_id).to eq(7)
|
|
expect(@e2.pk_aux_id).to eq(7)
|
|
expect(ModelECustom.where(aux_to_aux: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(aux_to_aux: @e1)).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "has_one association" do
|
|
it "works between base and base" do
|
|
# e1.pk_base_id -> e2.fk_base_id
|
|
@e1.owned_base_to_base = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_base_id).to eq(1)
|
|
expect(@e2.fk_base_id).to eq(1)
|
|
expect(ModelECustom.where(owned_base_to_base: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(owned_base_to_base: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between base and aux" do
|
|
# e1.pk_base_id -> e2.fk_aux_id
|
|
@e1.owned_base_to_aux = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_base_id).to eq(1)
|
|
expect(@e2.fk_aux_id).to eq(1)
|
|
expect(ModelECustom.where(owned_base_to_aux: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(owned_base_to_aux: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between aux and base" do
|
|
# e1.pk_aux_id -> e2.fk_base_id
|
|
@e1.owned_aux_to_base = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_aux_id).to eq(3)
|
|
expect(@e2.fk_base_id).to eq(3)
|
|
expect(ModelECustom.where(owned_aux_to_base: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(owned_aux_to_base: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between aux and aux" do
|
|
# e1.pk_aux_id -> e2.fk_aux_id
|
|
@e1.owned_aux_to_aux = @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_aux_id).to eq(3)
|
|
expect(@e2.fk_aux_id).to eq(3)
|
|
expect(ModelECustom.where(owned_aux_to_aux: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(owned_aux_to_aux: @e1)).to eq([])
|
|
end
|
|
end
|
|
|
|
describe "has_many association" do
|
|
it "works between base and base" do
|
|
# e1.pk_base_id -> e2.fk_base_id
|
|
@e1.owned_base_to_base_many << @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_base_id).to eq(1)
|
|
expect(@e2.fk_base_id).to eq(1)
|
|
expect(ModelECustom.where(owned_base_to_base_many: @e2)).to eq(
|
|
[@e1]
|
|
)
|
|
expect(ModelECustom.where(owned_base_to_base_many: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between base and aux" do
|
|
# e1.pk_base_id -> e2.fk_aux_id
|
|
@e1.owned_base_to_aux_many << @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_base_id).to eq(1)
|
|
expect(@e2.fk_aux_id).to eq(1)
|
|
expect(ModelECustom.where(owned_base_to_aux_many: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(owned_base_to_aux_many: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between aux and base" do
|
|
# e1.pk_aux_id -> e2.fk_base_id
|
|
@e1.owned_aux_to_base_many << @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_aux_id).to eq(3)
|
|
expect(@e2.fk_base_id).to eq(3)
|
|
expect(ModelECustom.where(owned_aux_to_base_many: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(owned_aux_to_base_many: @e1)).to eq([])
|
|
end
|
|
|
|
it "works between aux and aux" do
|
|
# e1.pk_aux_id -> e2.fk_aux_id
|
|
@e1.owned_aux_to_aux_many << @e2
|
|
@e1.save!
|
|
@e2.save!
|
|
expect(@e1.pk_aux_id).to eq(3)
|
|
expect(@e2.fk_aux_id).to eq(3)
|
|
expect(ModelECustom.where(owned_aux_to_aux_many: @e2)).to eq([@e1])
|
|
expect(ModelECustom.where(owned_aux_to_aux_many: @e1)).to eq([])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#reload" do
|
|
it "discards changes to aux attributes when reloading the model" do
|
|
@car.fuel_type = "gasoline"
|
|
@car.reload
|
|
expect(@car.fuel_type).to eq("hybrid")
|
|
end
|
|
|
|
it "discards changes to main attributes when reloading the model" do
|
|
@car.name = "Honda Civic"
|
|
@car.reload
|
|
expect(@car.name).to eq("Toyota Prius")
|
|
end
|
|
|
|
it "can be saved after reloading" do
|
|
@car.reload
|
|
@car.name = "Honda Civic"
|
|
@car.save!
|
|
|
|
car = Car.find(@car.id)
|
|
expect(car.name).to eq("Honda Civic")
|
|
end
|
|
|
|
it "reloads the right value" do
|
|
car2 = Car.find(@car.id)
|
|
expect(@car.name).to eq("Toyota Prius")
|
|
@car.name = "Honda Civic"
|
|
@car.fuel_type = "gasoline"
|
|
@car.save!
|
|
@car.reload
|
|
expect(@car.name).to eq("Honda Civic")
|
|
expect(@car.fuel_type).to eq("gasoline")
|
|
|
|
car2.reload
|
|
expect(car2.name).to eq("Honda Civic")
|
|
expect(car2.fuel_type).to eq("gasoline")
|
|
end
|
|
|
|
it "reloads with one query" do
|
|
queries = capture_queries { @car.reload }
|
|
expect(queries.length).to eq(1)
|
|
end
|
|
|
|
it "reloads associations" do
|
|
expect(@car.drivers.length).to eq(0)
|
|
|
|
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)
|
|
|
|
@car.reload
|
|
expect(@car.drivers.length).to eq(1)
|
|
expect(@car.drivers.count).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe "#exists?" do
|
|
it "works when present with base table attributes" do
|
|
expect(Car.exists?(id: @car.id)).to be_truthy
|
|
end
|
|
|
|
it "works when missing with with base table attributes" do
|
|
expect(Car.exists?(id: 9999)).to be_falsey
|
|
end
|
|
|
|
it "works when present with aux table attributes" do
|
|
expect(Car.exists?(fuel_type: "hybrid")).to be_truthy
|
|
end
|
|
|
|
it "works when missing with aux table attributes" do
|
|
expect(Car.exists?(fuel_type: "diesel")).to be_falsey
|
|
end
|
|
end
|
|
end
|
|
|
|
it "loads the aux data separately when loaded from main class" do
|
|
car =
|
|
Car.create!(name: "Honda Civic", fuel_type: "gasoline", engine_size: 2.0)
|
|
boat = Boat.create!(name: "Yacht", only_freshwater: true)
|
|
expect(Vehicle.count).to eq(2)
|
|
expect(Car.count).to eq(1)
|
|
expect(Boat.count).to eq(1)
|
|
|
|
expect(capture_queries { car = Vehicle.find(car.id) }.length).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(capture_queries { boat = Vehicle.find(boat.id) }.length).to eq(1)
|
|
expect(boat.only_freshwater).to eq(true)
|
|
end
|
|
|
|
it "unscoped can take a block" do
|
|
car = Car.create!(name: "Honda Civic", fuel_type: "gasoline")
|
|
car = Car.unscoped { Car.find(car.id) }
|
|
expect(car.fuel_type).to eq("gasoline")
|
|
end
|
|
|
|
describe "namespaced models" do
|
|
it "works with namespaced models" do
|
|
fork1 =
|
|
Kitchen::Fork.create!(name: "Fork", material: "metal", num_tongs: 3)
|
|
expect(fork1.material).to eq("metal")
|
|
expect(fork1.num_tongs).to eq(3)
|
|
end
|
|
end
|
|
|
|
it "can redefine constants" do
|
|
class TestModel < ActiveRecord::Base
|
|
include HasAuxTable
|
|
end
|
|
|
|
class TestModelSpecific < TestModel
|
|
aux_table :specific
|
|
end
|
|
|
|
Object.send(:remove_const, :TestModelSpecific)
|
|
|
|
expect {
|
|
class TestModelSpecific < TestModel
|
|
aux_table :specific
|
|
end
|
|
}.not_to raise_error
|
|
end
|
|
|
|
describe "counter cache" do
|
|
def verify_counter_cache(model, assoc_name, expected_count)
|
|
expect(model.send("#{assoc_name}_count")).to eq(expected_count)
|
|
expect(model.send(assoc_name).count).to eq(expected_count || 0)
|
|
model.reload
|
|
expect(model.send("#{assoc_name}_count")).to eq(expected_count)
|
|
expect(model.send(assoc_name).count).to eq(expected_count || 0)
|
|
end
|
|
|
|
let(:reader) { Reader.create!(name: "John Doe", reading_speed: 100) }
|
|
let(:book) do
|
|
Book.create!(title: "The Great Gatsby", author: "F. Scott Fitzgerald")
|
|
end
|
|
|
|
it "updates counter caches that are on the aux model" do
|
|
verify_counter_cache(reader, :read_book_joins, nil)
|
|
reader.read_books << book
|
|
verify_counter_cache(reader, :read_book_joins, 1)
|
|
end
|
|
|
|
it "updates counter caches on a non-aux model" do
|
|
verify_counter_cache(book, :read_book_joins, nil)
|
|
reader.read_books << book
|
|
verify_counter_cache(book, :read_book_joins, 1)
|
|
end
|
|
|
|
it "has_one is a base class, belongs_to is a subclass, created via subclass" do
|
|
a = ModelA.create!(a_field1: "a_0")
|
|
verify_counter_cache(a, :model_bs, nil)
|
|
|
|
5.times do |i|
|
|
ModelB.create!(model_a: a, b_field1: "b_#{i}")
|
|
verify_counter_cache(a, :model_bs, i + 1)
|
|
end
|
|
end
|
|
|
|
it "has_one is a base class, belongs_to is a subclass, created via association" do
|
|
a = ModelA.create!(a_field1: "a_0")
|
|
verify_counter_cache(a, :model_bs, nil)
|
|
|
|
5.times do |i|
|
|
a.model_bs.create!(b_field1: "b_#{i}")
|
|
verify_counter_cache(a, :model_bs, i + 1)
|
|
end
|
|
end
|
|
|
|
it "has_one is a subclass, belongs_to is a subclass, created via subclass" do
|
|
a = ModelA2.create!(a_field1: "a2_0", a2_field1: "a2_0")
|
|
verify_counter_cache(a, :model_b2s, nil)
|
|
|
|
5.times do |i|
|
|
a.model_b2s.create!(b_field1: "b_#{i}")
|
|
verify_counter_cache(a, :model_b2s, i + 1)
|
|
end
|
|
end
|
|
|
|
it "has_one is a subclass, belongs_to is a subclass, created via association" do
|
|
a = ModelA2.create!(a_field1: "a2_0", a2_field1: "a2_0")
|
|
verify_counter_cache(a, :model_b2s, nil)
|
|
|
|
5.times do |i|
|
|
a.model_b2s.create!(b_field1: "b_#{i}")
|
|
verify_counter_cache(a, :model_b2s, i + 1)
|
|
end
|
|
end
|
|
|
|
it "has_one is subclass, belongs_to is a vanilla class" do
|
|
a = ModelA1.create!(a_field1: "a1_0", a1_field1: "a1_0")
|
|
verify_counter_cache(a, :model_cs, nil)
|
|
|
|
5.times do |i|
|
|
a.model_cs.create!(c_field1: "c_#{i}")
|
|
verify_counter_cache(a, :model_cs, i + 1)
|
|
end
|
|
end
|
|
|
|
it "is a join table connecting two base classes" do
|
|
a = ModelA.create!(a_field1: "a1_0")
|
|
ds = 5.times.map { ModelD.create! }
|
|
verify_counter_cache(a, :ad_joins, nil)
|
|
ds.each { |d| verify_counter_cache(d, :ad_joins, nil) }
|
|
|
|
ds.each_with_index do |d, i|
|
|
a.ad_joins.create!(model_d: d)
|
|
verify_counter_cache(a, :ad_joins, i + 1)
|
|
verify_counter_cache(d, :ad_joins, 1)
|
|
end
|
|
end
|
|
|
|
it "is a join table connecting two subclasses" do
|
|
a = ModelA1.create!(a_field1: "a1_0", a1_field1: "a1_0")
|
|
ds = 3.times.map { |i| ModelD1.create!(d1_field1: "d1_#{i}") }
|
|
verify_counter_cache(a, :ad_joins, nil)
|
|
ds.each { |d| verify_counter_cache(d, :ad_joins, nil) }
|
|
|
|
ds.each_with_index do |d, i|
|
|
a.ad_joins.create!(model_d: d)
|
|
verify_counter_cache(a, :ad_joins, i + 1)
|
|
verify_counter_cache(d, :ad_joins, 1)
|
|
end
|
|
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
|
|
|
|
describe "allowing redefining of methods" do
|
|
it "allows method redefining with `allow_method_redefinition`" do
|
|
ActiveRecord::Schema.define do
|
|
create_base_table :test_model2s do |t|
|
|
t.string :on_base
|
|
t.create_aux :specific do |t|
|
|
t.string :on_aux
|
|
end
|
|
end
|
|
end
|
|
|
|
class TestModel2 < ActiveRecord::Base
|
|
include HasAuxTable
|
|
|
|
def on_base
|
|
"on_base #{super} #{id}"
|
|
end
|
|
end
|
|
|
|
expect {
|
|
class TestModel2A < TestModel2
|
|
aux_table :specific, allow_redefining: :on_base
|
|
def on_base
|
|
"2a_on_base_override #{super}"
|
|
end
|
|
|
|
def on_aux
|
|
"2a_on_aux_override #{super}"
|
|
end
|
|
end
|
|
}.not_to raise_error
|
|
|
|
expect {
|
|
class TestModel2B < TestModel2
|
|
aux_table :specific, allow_redefining: :on_base
|
|
end
|
|
}.not_to raise_error
|
|
|
|
base_model = TestModel2.create!(on_base: "base")
|
|
expect(base_model.on_base).to eq("on_base base #{base_model.id}")
|
|
|
|
specific_a = TestModel2A.create!(on_base: "2a_base", on_aux: "2a_aux")
|
|
expect(specific_a.on_base).to eq(
|
|
"2a_on_base_override on_base 2a_base #{specific_a.id}"
|
|
)
|
|
expect(specific_a.on_aux).to eq("2a_on_aux_override 2a_aux")
|
|
|
|
specific_b = TestModel2B.create!(on_base: "2b_base", on_aux: "2b_aux")
|
|
expect(specific_b.on_base).to eq("on_base 2b_base #{specific_b.id}")
|
|
expect(specific_b.on_aux).to eq("2b_aux")
|
|
end
|
|
end
|
|
|
|
describe "range queries" do
|
|
ActiveRecord::Schema.define do
|
|
create_base_table :test_range_models do |t|
|
|
t.integer :base_field
|
|
t.create_aux :specific do |t|
|
|
t.integer :aux_field
|
|
end
|
|
end
|
|
end
|
|
|
|
class TestRangeModel < ActiveRecord::Base
|
|
include HasAuxTable
|
|
end
|
|
|
|
class TestRangeModelSpecific < TestRangeModel
|
|
aux_table :specific
|
|
end
|
|
|
|
before do
|
|
@bases = (0..5).map { |i| TestRangeModel.create!(base_field: i) }
|
|
@specifics =
|
|
(0..5).map do |i|
|
|
TestRangeModelSpecific.create!(base_field: i, aux_field: i)
|
|
end
|
|
end
|
|
|
|
it "works with a from..to range" do
|
|
expect(TestRangeModel.where(base_field: 1..5)).to eq(
|
|
@bases[1..5] + @specifics[1..5]
|
|
)
|
|
expect(TestRangeModelSpecific.where(base_field: 2..3)).to eq(
|
|
@specifics[2..3]
|
|
)
|
|
expect(TestRangeModelSpecific.where(aux_field: 2..3)).to eq(
|
|
@specifics[2..3]
|
|
)
|
|
expect(TestRangeModelSpecific.where(aux_field: 4..7)).to eq(
|
|
@specifics[4..5]
|
|
)
|
|
end
|
|
|
|
it "works with a from.. range" do
|
|
expect(TestRangeModel.where(base_field: 1..)).to eq(
|
|
@bases[1..5] + @specifics[1..5]
|
|
)
|
|
|
|
expect(TestRangeModelSpecific.where(aux_field: 1..)).to eq(
|
|
@specifics[1..5]
|
|
)
|
|
end
|
|
|
|
it "works with a ..to range" do
|
|
expect(TestRangeModel.where(base_field: ..4)).to eq(
|
|
@bases[0..4] + @specifics[0..4]
|
|
)
|
|
|
|
expect(TestRangeModelSpecific.where(aux_field: ..4)).to eq(
|
|
@specifics[0..4]
|
|
)
|
|
end
|
|
end
|
|
end
|