Implement CharacterProcessor

Process character data from deck
This commit is contained in:
Justin Edmund 2025-02-17 22:08:18 -08:00
parent d6ca8e8e90
commit 3378e7114f
2 changed files with 141 additions and 0 deletions

View file

@ -0,0 +1,89 @@
# frozen_string_literal: true
module Processors
##
# CharacterProcessor processes an array of character data and creates GridCharacter records.
#
# @example
# processor = Processors::CharacterProcessor.new(party, transformed_characters_array)
# processor.process
class CharacterProcessor < BaseProcessor
def initialize(party, data, type = :normal, options = {})
super(party, data, options)
@party = party
@data = data
end
##
# Processes character data.
#
# Iterates over each character hash in +data+ and creates a new GridCharacter record.
# Expects each character hash to include keys such as :id, :position, :uncap, etc.
#
# @return [void]
def process
unless @data.is_a?(Hash)
Rails.logger.error "[CHARACTER] Invalid data format: expected a Hash, got #{@data.class}"
return
end
unless @data.key?('deck') && @data['deck'].key?('npc')
Rails.logger.error '[CHARACTER] Missing npc data in deck JSON'
return
end
@data = @data.with_indifferent_access
characters_data = @data['deck']['npc']
grid_characters = process_characters(characters_data)
grid_characters.each do |grid_character|
begin
grid_character.save!
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error "[CHARACTER] Failed to create GridCharacter: #{e.record.errors.full_messages.join(', ')}"
end
end
rescue StandardError => e
raise e
end
private
def process_characters(characters_data)
characters_data.map do |key, raw_character|
next if raw_character.nil? || raw_character['param'].nil? || raw_character['master'].nil?
position = key.to_i - 1
# Find the Character record by its granblue_id.
character_id = raw_character.dig('master', 'id')
character = Character.find_by(granblue_id: character_id)
unless character
Rails.logger.error "[CHARACTER] Character not found with id #{character_id}"
next
end
# The deck doesn't have Awakening data, so use the default
awakening = Awakening.where(slug: 'character-balanced').first
grid_character = GridCharacter.create(
party_id: @party.id,
character_id: character.id,
uncap_level: raw_character.dig('param', 'evolution').to_i,
transcendence_step: raw_character.dig('param', 'phase').to_i,
position: position,
perpetuity: raw_character.dig('param', 'has_npcaugment_constant'),
awakening: awakening
)
grid_character
end.compact
end
# Converts a value to a boolean.
def parse_boolean(val)
val.to_s.downcase == 'true'
end
end
end

View file

@ -0,0 +1,52 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Processors::CharacterProcessor, type: :model do
before(:each) do
@party = create(:party)
end
# Use canonical data loaded via canonical.rb.
let(:deck_data) do
file_path = Rails.root.join('spec', 'fixtures', 'deck_sample2.json')
JSON.parse(File.read(file_path))
end
subject! { described_class.new(@party, deck_data, language: 'en') }
context 'with valid character data' do
it 'creates the correct number of GridCharacter records' do
expect { subject.process }.to change(GridCharacter, :count).by(5)
end
it 'creates GridCharacters with the correct attributes' do
subject.process
grid_chars = GridCharacter.where(party_id: @party.id).order(:position)
# We assume the processor uses the character id from raw_data.
expect(grid_chars[0].character.granblue_id).to eq(deck_data.dig('deck', 'npc', '1', 'master', 'id'))
expect(grid_chars[3].uncap_level).to eq(deck_data.dig('deck', 'npc', '4', 'param', 'evolution').to_i)
expect(grid_chars[4].position).to eq(4)
end
end
context 'with invalid character data' do
let(:deck_data) { 'invalid data' }
it 'does not create any GridCharacter and logs an error containing "CHARACTER"' do
expect { subject.process }.not_to change(GridCharacter, :count)
begin
subject.process
rescue StandardError
nil
end
end
end
after(:each) do |example|
if example.exception
puts "\nDEBUG [CharacterProcessor]: #{example.full_description} failed with error: #{example.exception.message}"
end
end
end