Implement CharacterProcessor
Process character data from deck
This commit is contained in:
parent
d6ca8e8e90
commit
3378e7114f
2 changed files with 141 additions and 0 deletions
89
app/services/processors/character_processor.rb
Normal file
89
app/services/processors/character_processor.rb
Normal 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
|
||||||
52
spec/services/processors/character_processor_spec.rb
Normal file
52
spec/services/processors/character_processor_spec.rb
Normal 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
|
||||||
Loading…
Reference in a new issue