auto-compute forge chain fields from forged_from

- add before_save callback to calculate forge_order and forge_chain_id
- add validation to prevent circular forge chains
This commit is contained in:
Justin Edmund 2025-12-22 19:52:59 -08:00
parent 3abc10a5e2
commit 5c578ee527
2 changed files with 54 additions and 1 deletions

View file

@ -139,6 +139,9 @@ class Weapon < ApplicationRecord
# Forge chain scopes
scope :in_forge_chain, ->(chain_id) { where(forge_chain_id: chain_id).order(:forge_order) }
# Forge chain callbacks
before_save :compute_forge_chain_fields, if: :forged_from_changed?
# Forge chain methods
def forged_from_weapon
return nil unless forged_from.present?
@ -188,4 +191,48 @@ class Weapon < ApplicationRecord
found = WeaponSeries.find_by(id: value) || WeaponSeries.find_by(slug: value)
self.weapon_series = found
end
# Validation to prevent circular forge chains
validate :no_circular_forge_chain
def no_circular_forge_chain
return unless forged_from.present?
visited = Set.new([granblue_id])
current = forged_from
while current.present?
if visited.include?(current)
errors.add(:forged_from, 'creates a circular forge chain')
return
end
visited << current
current = Weapon.find_by(granblue_id: current)&.forged_from
end
end
private
# Auto-compute forge_order and forge_chain_id based on forged_from
def compute_forge_chain_fields
if forged_from.present?
base_weapon = Weapon.find_by(granblue_id: forged_from)
if base_weapon
# Inherit or create forge_chain_id from base weapon
self.forge_chain_id = base_weapon.forge_chain_id || base_weapon.id
# Compute forge_order as base weapon's order + 1
self.forge_order = base_weapon.forge_order.to_i + 1
# Ensure base weapon has forge_chain_id if it didn't
if base_weapon.forge_chain_id.nil?
base_weapon.update_column(:forge_chain_id, base_weapon.id)
base_weapon.update_column(:forge_order, 0) if base_weapon.forge_order.nil?
end
end
else
# Clearing forged_from - reset forge_order if part of a chain
self.forge_order = 0 if forge_chain_id.present?
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[8.0].define(version: 2025_12_20_100000) do
ActiveRecord::Schema[8.0].define(version: 2025_12_21_210000) do
# These are extensions that must be enabled in order to support this database
enable_extension "btree_gin"
enable_extension "pg_catalog.plpgsql"
@ -989,6 +989,12 @@ ActiveRecord::Schema[8.0].define(version: 2025_12_20_100000) do
t.integer "promotions", default: [], null: false, array: true
t.uuid "weapon_series_id"
t.boolean "gacha", default: false, null: false
t.integer "extra_prerequisite"
t.string "forged_from"
t.uuid "forge_chain_id"
t.integer "forge_order"
t.index ["forge_chain_id"], name: "index_weapons_on_forge_chain_id"
t.index ["forged_from"], name: "index_weapons_on_forged_from"
t.index ["gacha"], name: "index_weapons_on_gacha"
t.index ["granblue_id"], name: "index_weapons_on_granblue_id"
t.index ["name_en"], name: "index_weapons_on_name_en", opclass: :gin_trgm_ops, using: :gin