From 58fabad3c218ec0f06fcc03a0aa1629eb02dbe95 Mon Sep 17 00:00:00 2001 From: Emi Matchu Date: Sun, 2 Nov 2025 08:46:53 +0000 Subject: [PATCH] [WV2] Filter colors, using advanced fallbacks --- app/controllers/outfits_controller.rb | 25 ++++++++++++++++--------- app/models/pet_type.rb | 20 ++++++++++++++++++++ app/models/species.rb | 9 +++++++++ spec/models/pet_type_spec.rb | 19 +++++++++++++++++++ spec/models/species_spec.rb | 10 +++++++++- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/app/controllers/outfits_controller.rb b/app/controllers/outfits_controller.rb index 34b6e16c..d1397c49 100644 --- a/app/controllers/outfits_controller.rb +++ b/app/controllers/outfits_controller.rb @@ -78,17 +78,24 @@ class OutfitsController < ApplicationController end def new_v2 - @colors = Color.alphabetical - @species = Species.alphabetical - # Get selected species and color from params, or default to Blue Acara - species_id = params[:species] || Species.find_by_name("Acara")&.id - color_id = params[:color] || Color.find_by_name("Blue")&.id + @selected_species = params[:species] ? Species.find_by_id(params[:species]) : Species.find_by_name("Acara") + @selected_color = params[:color] ? Color.find_by_id(params[:color]) : Color.find_by_name("Blue") - # Find the pet type (species+color combination) - @selected_species = Species.find_by(id: species_id) - @selected_color = Color.find_by(id: color_id) - @pet_type = PetType.find_by(species_id: species_id, color_id: color_id) if @selected_species && @selected_color + # Load valid colors for the selected species (colors that have existing pet types) + @species = Species.alphabetical + @colors = @selected_species.compatible_colors + + # Find the best pet type for this species+color combo + # If the exact combo doesn't exist, this will fall back to a simple color + @pet_type = PetType.for_species_and_color( + species_id: @selected_species.id, + color_id: @selected_color.id + ) + + # Use the pet type's actual color as the selected color + # (might differ from requested color if we fell back to a simple color) + @selected_color = @pet_type&.color # Load items from the objects[] parameter item_ids = params[:objects] || [] diff --git a/app/models/pet_type.rb b/app/models/pet_type.rb index de088ffe..6b8cd396 100644 --- a/app/models/pet_type.rb +++ b/app/models/pet_type.rb @@ -48,6 +48,26 @@ class PetType < ApplicationRecord random_pet_types end + # Given a species ID and color ID, return the best matching PetType. + # + # If the exact species+color combo exists, return it. + # Otherwise, find the best fallback for that species: + # - Prefer the requested color if available + # - Otherwise prefer simple colors (basic > standard > alphabetical) + # + # This matches the wardrobe behavior where we automatically switch to a valid + # color when the user selects a species that doesn't support their current color. + # + # Returns the PetType, or nil if no pet types exist for this species. + def self.for_species_and_color(species_id:, color_id:) + return nil if species_id.nil? + + where(species_id: species_id) + .preferring_color(color_id) + .preferring_simple + .first + end + def as_json(options={}) super({ only: [:id], diff --git a/app/models/species.rb b/app/models/species.rb index 8f43f05f..f71b725a 100644 --- a/app/models/species.rb +++ b/app/models/species.rb @@ -34,4 +34,13 @@ class Species < ApplicationRecord def self.param_to_id(param) param.match?(/\A\d+\Z/) ? param.to_i : find_by_name!(param).id end + + # Get all colors that are compatible with this species (have pet types) + # Returns an ActiveRecord::Relation of Color records + def compatible_colors + Color.alphabetical + .joins(:pet_types) + .where(pet_types: { species_id: id }) + .distinct + end end diff --git a/spec/models/pet_type_spec.rb b/spec/models/pet_type_spec.rb index 906cf8a2..231a3c77 100644 --- a/spec/models/pet_type_spec.rb +++ b/spec/models/pet_type_spec.rb @@ -38,4 +38,23 @@ RSpec.describe PetType do expect(PetType.find_by_param!("123-456")).to eq pet_types(:newcolor_newspecies) end end + + describe ".for_species_and_color" do + it('returns the exact match when it exists') do + result = PetType.for_species_and_color(species_id: species(:acara), color_id: colors(:blue)) + expect(result).to eq pet_types(:blue_acara) + end + + it('returns nil when species is nil') do + result = PetType.for_species_and_color(species_id: nil, color_id: colors(:blue)) + expect(result).to be_nil + end + + it('falls back to a simple color when exact match does not exist') do + # Request a species that exists but with a color that might not + # It should fall back to a basic/standard color for that species + result = PetType.for_species_and_color(species_id: species(:acara), color_id: 999) + expect(result).to eq pet_types(:blue_acara) + end + end end diff --git a/spec/models/species_spec.rb b/spec/models/species_spec.rb index 8876de4e..b6e0673a 100644 --- a/spec/models/species_spec.rb +++ b/spec/models/species_spec.rb @@ -1,7 +1,15 @@ require_relative '../rails_helper' RSpec.describe Species do - fixtures :species + fixtures :species, :colors + + describe "#valid_colors_for_species" do + it('returns colors that have pet types for the species') do + # The Blue Acara exists in fixtures, as does a "Color #123 Acara", which we'll ignore. + compatible_colors = species(:acara).compatible_colors + expect(compatible_colors.map(&:id)).to eq [8] + end + end describe '#to_param' do it("uses name when possible") do