Add Item#dyeworks_base_item database field, and populate it

In this change, instead of *always* inferring the Dyeworks base item
from the item name at runtime, we now have a database field that tracks
it, and auto-populates whenever an item *seems* to need a Dyeworks base
item but doesn't have one yet.

This will enable us to set the base item manually in cases where it
can't be inferred, and load Dyeworks base items for the Item Getting
Guide in one query with `includes(:dyeworks_base_item)`.

This migration does a bit more of the fix-em-up scripting work *in* the
migration itself than I usually do, mainly because there's so much in
this one that I think being extra-explicit is useful. We make sure to
do it gracefully though!
This commit is contained in:
Emi Matchu 2024-06-07 20:10:06 -07:00
parent 68cb44d159
commit 857cb547ed
3 changed files with 99 additions and 2 deletions

View file

@ -14,6 +14,9 @@ class Item < ApplicationRecord
has_one :nc_mall_record
has_many :parent_swf_asset_relationships, :as => :parent
has_many :swf_assets, :through => :parent_swf_asset_relationships
belongs_to :dyeworks_base_item, class_name: "Item",
default: -> { inferred_dyeworks_base_item }, optional: true
attr_writer :current_body_id, :owned, :wanted
@ -208,7 +211,7 @@ class Item < ApplicationRecord
Dyeworks\s+(?<color>\S+)\s*(?<base>.+)
)$
}x
def dyeworks_base_item
def inferred_dyeworks_base_item
name_match = name.match(DYEWORKS_NAME_PATTERN)
return nil if name_match.nil?

View file

@ -0,0 +1,91 @@
class AddDyeworksBaseItemIdToItems < ActiveRecord::Migration[7.1]
def change
add_reference :items, :dyeworks_base_item, type: :integer,
foreign_key: {to_table: :items}
# Find Dyeworks items, and fill in their base item field. (The Item model
# is configured to try to infer this when saving Dyeworks-seeming items
# with no matching base item yet.)
#
# Some item names that exist right now are known to not fit the pattern, so
# we try to set them manually if present in this copy of the database.
reversible do |direction|
direction.up do
dyeworks_items = Item.where("name LIKE ?", "Dyeworks %").to_a
puts "Found #{dyeworks_items.size} Dyeworks items, " +
"inferring base items…"
dyeworks_items.each(&:save!)
num_successes = dyeworks_items.select(&:dyeworks?).size
puts "Inferred Dyeworks base item for #{num_successes} items"
set_manually "Baby Valentine Jumper and Shirt",
"Dyeworks Baby Blue: Baby Valentine Jumper",
"Dyeworks Baby Pink: Baby Valentine Jumper",
"Dyeworks Purple: Baby Valentine Jumper"
set_manually "Field of Flowers Foreground",
"Dyeworks Black: Field of Flowers",
"Dyeworks Blue: Field of Flowers",
"Dyeworks Yellow: Field of Flowers"
set_manually "2010 Games Master Challenge NC Challenge Lulu Shirt",
"Dyeworks Black: Games Master Challenge 2010 Lulu Shirt",
"Dyeworks Orange: Games Master Challenge 2010 Lulu Shirt",
"Dyeworks Purple: Games Master Challenge 2010 Lulu Shirt"
set_manually "Stars and Glitter Face Paint",
"Dyeworks Blue: Stars and Glitter Facepaint",
"Dyeworks Green: Stars and Glitter Facepaint",
"Dyeworks Purple: Stars and Glitter Facepaint"
set_manually "Hanging Winter Candle Garland",
"Dyeworks Brown: Hanging Winter Candles Garland",
"Dyeworks Purple: Hanging Winter Candles Garland",
"Dyeworks Silver: Hanging Winter Candles Garland"
set_manually "Lovely Berry Blush Makeup",
"Dyeworks Magenta: Lovely Berry Blush",
"Dyeworks Peach: Lovely Berry Blush",
"Dyeworks Soft Pink: Lovely Berry Blush"
set_manually "Winter Lights Effect",
"Dyeworks Orange & Pink: Winter Lights Effects",
"Dyeworks Red & Green: Winter Lights Effects",
"Dyeworks Yellow & Magenta: Winter Lights Effects"
danglers = Item.where("name LIKE ?", "Dyeworks %").
where(dyeworks_base_item_id: nil).to_a
puts "There are now #{danglers.size} Dyeworks-seeming items in the " +
"database without a matching base item."
danglers.each do |dangler|
puts "- #{dangler.name}"
end
end
end
end
private
def set_manually(base_item_name, *dyeworks_item_names)
base_item = Item.find_by_name(base_item_name)
if base_item.nil?
puts "Skipping all manual Dyeworks for base item #{base_item}: not found"
return
end
dyeworks_item_names.each do |dyeworks_item_name|
dyeworks_item = Item.find_by_name(dyeworks_item_name)
if dyeworks_item.nil?
puts "Skipping manual Dyeworks for #{base_item_name} -> " +
"#{dyeworks_item_name}: not found"
next
end
dyeworks_item.update!(dyeworks_base_item: base_item)
puts "Manually assigned Dyeworks #{base_item_name} -> " +
"#{dyeworks_item_name}"
end
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_06_03_181855) do
ActiveRecord::Schema[7.1].define(version: 2024_06_08_022149) do
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t|
t.integer "species_id", null: false
t.integer "color_id", null: false
@ -136,6 +136,8 @@ ActiveRecord::Schema[7.1].define(version: 2024_06_03_181855) do
t.string "name", null: false
t.text "description", size: :medium, null: false
t.string "rarity", default: "", null: false
t.integer "dyeworks_base_item_id"
t.index ["dyeworks_base_item_id"], name: "index_items_on_dyeworks_base_item_id"
t.index ["modeling_status_hint", "created_at", "id"], name: "items_modeling_status_hint_and_created_at_and_id"
t.index ["modeling_status_hint", "created_at"], name: "items_modeling_status_hint_and_created_at"
t.index ["modeling_status_hint", "id"], name: "items_modeling_status_hint_and_id"
@ -292,6 +294,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_06_03_181855) do
add_foreign_key "alt_styles", "colors"
add_foreign_key "alt_styles", "species"
add_foreign_key "items", "items", column: "dyeworks_base_item_id"
add_foreign_key "nc_mall_records", "items"
add_foreign_key "outfits", "alt_styles"
end