85 lines
2.6 KiB
Ruby
85 lines
2.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'date'
|
|
|
|
module AceOfBase
|
|
# A single row of data, including casting and validations.
|
|
class Record
|
|
include Comparable
|
|
|
|
STRING_TOO_LONG_MESSAGE = 'Field `%s` is over the maximum length of %d characters (%d in total)'
|
|
VALUE_OUTSIDE_RANGE = 'Field `%s`: %p is outside the range of %p'
|
|
INVALID_DATE_MESSAGE = 'Field `%s`: unable to parse date with format %p'
|
|
INCORRECT_COLUMN_NUMBER_MESSAGE = 'Row contains an incorrect number of columns. Expected 7, received %d'
|
|
ATTRIBUTES = %i[project shot version status finish_date internal_bid created_date].freeze
|
|
|
|
attr_reader :errors, *ATTRIBUTES
|
|
|
|
def initialize(row)
|
|
@errors = []
|
|
cast_and_validate(row)
|
|
end
|
|
|
|
def valid?
|
|
errors.empty?
|
|
end
|
|
|
|
def <=>(other)
|
|
ATTRIBUTES
|
|
.lazy
|
|
.map { |attr| public_send(attr) <=> other.public_send(attr) }
|
|
.find(-> { 0 }, &:nonzero?)
|
|
end
|
|
|
|
private
|
|
|
|
def cast_and_validate(row) # rubocop:disable Metrics/AbcSize
|
|
return add_incorrect_column_number_error(row.size) unless row.size == 7
|
|
|
|
@project = cast_string_with_limit(:project, row[0], 64)
|
|
@shot = cast_string_with_limit(:shot, row[1], 64)
|
|
@version = cast_integer_within_range(:version, row[2], 0..0xffff)
|
|
@status = cast_string_with_limit(:status, row[3], 32)
|
|
@finish_date = cast_date_with_format(:finish_date, row[4], '%Y-%m-%d')
|
|
@internal_bid = cast_float_within_range(:internal_bid, row[5], 0..0xffff)
|
|
@created_date = cast_date_with_format(:created_date, row[6], '%Y-%m-%d %H:%M')
|
|
end
|
|
|
|
def add_incorrect_column_number_error(column_count)
|
|
add_error(format(INCORRECT_COLUMN_NUMBER_MESSAGE, column_count))
|
|
end
|
|
|
|
def add_error(message)
|
|
@errors << message
|
|
nil
|
|
end
|
|
|
|
def cast_string_with_limit(field, incoming, limit)
|
|
casted = incoming.to_s
|
|
length = casted.length
|
|
return add_error(format(STRING_TOO_LONG_MESSAGE, field, limit, length)) if length > limit
|
|
|
|
casted
|
|
end
|
|
|
|
def cast_integer_within_range(field, incoming, range)
|
|
casted = incoming.to_i
|
|
return add_error(format(VALUE_OUTSIDE_RANGE, field, incoming, range)) unless range.cover?(casted)
|
|
|
|
casted
|
|
end
|
|
|
|
def cast_date_with_format(field, incoming, format)
|
|
DateTime.strptime(incoming.to_s, format)
|
|
rescue ArgumentError
|
|
add_error(format(INVALID_DATE_MESSAGE, field, format))
|
|
end
|
|
|
|
def cast_float_within_range(field, incoming, range)
|
|
casted = incoming.to_f
|
|
return add_error(format(VALUE_OUTSIDE_RANGE, field, incoming, range)) unless range.cover?(casted)
|
|
|
|
casted
|
|
end
|
|
end
|
|
end
|