hensei-api/db/seed/canonical.rb

105 lines
4.4 KiB
Ruby

# frozen_string_literal: true
# canonical.rb - Loads canonical seed data from CSV files into the database.
#
# This file is used to load canonical data for various models from CSV files
# located in db/seed/test. For models that reference other models by fixed IDs
# (e.g. Job, Guidebook, etc.), use the `use_id: true` option to preserve the CSV
# provided IDs (so that inter-model references remain correct).
#
# @example
# load_csv_for(Character, 'characters_test.csv', :granblue_id)
#
# # For objects that need to preserve the CSV "id" column:
# load_csv_for(Job, 'jobs_test.csv', :granblue_id, use_id: true)
#
require 'csv'
##
# Processes specified columns in an attributes hash to booleans.
#
# @param attrs [Hash] The attributes hash.
# @param columns [Array<Symbol>] The list of columns to cast to boolean.
def process_booleans(attrs, columns)
columns.each do |col|
next unless attrs.key?(col) && attrs[col].present?
# Use ActiveModel::Type::Boolean to cast the value.
attrs[col] = ActiveModel::Type::Boolean.new.cast(attrs[col])
end
end
##
# Processes specified columns in an attributes hash to dates.
#
# @param attrs [Hash] The attributes hash.
# @param columns [Array<Symbol>] The list of columns to parse as dates.
def process_dates(attrs, columns)
columns.each do |col|
next unless attrs.key?(col) && attrs[col].present?
# Parse the date, or assign nil if parsing fails.
attrs[col] = Date.parse(attrs[col]) rescue nil
end
end
##
# Loads CSV data for the given model class.
#
# Reads a CSV file from the db/seed/test directory and uses the given unique_key
# to determine whether a record already exists. If the record exists, its attributes
# are not overwritten; otherwise, a new record is created.
#
# @param model_class [Class] The ActiveRecord model class to load data for.
# @param csv_filename [String] The CSV filename (located in db/seed/test).
# @param unique_key [Symbol] The attribute used to uniquely identify a record (default: :granblue_id).
# @param use_id [Boolean] If true, preserves the CSV id field instead of removing it (default: false).
#
# @return [void]
def load_csv_for(model_class, csv_filename, unique_key = :granblue_id, use_id: false)
csv_file = Rails.root.join('db', 'seed', 'test', csv_filename)
# puts "Loading #{model_class.name} data from #{csv_file}..."
CSV.foreach(csv_file, headers: true) do |row|
# Convert CSV row to a hash with symbolized keys.
attrs = row.to_hash.symbolize_keys
# Process known boolean columns.
process_booleans(attrs, %i[flb ulb subaura limit transcendence])
# Process known date columns. Extend this list as needed.
process_dates(attrs, %i[release_date flb_date ulb_date transcendence_date created_at])
# Clean up attribute values: trim whitespace and convert empty strings to nil.
attrs.each { |k, v| attrs[k] = nil if v.is_a?(String) && v.strip.empty? }
# Remove the :id attribute unless we want to preserve it (for fixed canonical IDs).
attrs.except!(:id) unless use_id
# Skip records with missing associations (for test data)
if model_class == WeaponAwakening
next unless Weapon.exists?(attrs[:weapon_id]) && Awakening.exists?(attrs[:awakening_id])
end
# Find or create the record based on the unique key.
record = model_class.find_or_create_by!(unique_key => attrs[unique_key]) do |r|
# Assign all attributes except the unique_key.
r.assign_attributes(attrs.except(unique_key))
end
# puts "Loaded #{model_class.name}: #{record.public_send(unique_key)}"
end
end
# Load canonical data for core models.
# Load weapon_series first since weapons depend on it
load_csv_for(WeaponSeries, 'weapon_series_test.csv', :id, use_id: true)
load_csv_for(Awakening, 'awakenings_test.csv', :id, use_id: true)
load_csv_for(Summon, 'summons_test.csv', :id, use_id: true)
load_csv_for(Weapon, 'weapons_test.csv', :id, use_id: true)
load_csv_for(Character, 'characters_test.csv', :id, use_id: true)
# Load additional canonical data that require preserving the provided IDs.
load_csv_for(Job, 'jobs_test.csv', :id, use_id: true)
load_csv_for(Guidebook, 'guidebooks_test.csv', :id, use_id: true)
load_csv_for(JobAccessory, 'job_accessories_test.csv', :id, use_id: true)
load_csv_for(JobSkill, 'job_skills_test.csv', :id, use_id: true)
load_csv_for(WeaponAwakening, 'weapon_awakenings_test.csv', :id, use_id: true)
load_csv_for(WeaponKey, 'weapon_keys_test.csv', :id, use_id: true)