ファイル
btrc-hub/backend/app/services/post_import_source_parser.rb
T
2026-07-12 01:11:07 +09:00

79 行
3.0 KiB
Ruby
Raw Blame 履歴

このファイルには曖昧(ambiguous)なUnicode文字が含まれてゐます
このファイルには,他の文字と見間違える可能性があるUnicode文字が含まれてゐます. それが意図的なものと考えられる場合は,この警告を無視して構ゐません. それらの文字を表示するにはエスケープボタンを使用します.
require 'csv'
class PostImportSourceParser
MAX_ROWS = 100
MAX_BYTES = 1.megabyte
MAX_CELL_BYTES = 20.kilobytes
MAX_COLUMNS = 50
def initialize source:, format:, has_header: false, json_path: nil
@source = source.to_s
@format = format.to_s
@has_header = ActiveModel::Type::Boolean.new.cast(has_header)
@json_path = json_path.to_s
end
def parse
raise ArgumentError, '入力が大きすぎます.' if @source.bytesize > MAX_BYTES
@format == 'json' ? parse_json : parse_delimited
end
private
def parse_delimited
separator = @format == 'csv' ? ',' : "\t"
table = CSV.parse(@source, col_sep: separator).map { |row| row.map { _1.to_s.strip } }
raise ArgumentError, '入力が空です.' if table.empty?
headers = @has_header ? table.shift : nil
ensure_limits!(table)
width = table.map(&:length).max.to_i
raise ArgumentError, "列数は #{ MAX_COLUMNS } 列までです." if width > MAX_COLUMNS
{ columns: (0...width).map { |index| { key: index.to_s,
label: "#{ column_name(index) }#{ headers&.[](index).presence || '列' }" } },
rows: table.each_with_index.map { |values, index| { source_row: index + (@has_header ? 2 : 1), values: } } }
rescue CSV::MalformedCSVError => e
raise ArgumentError, "CSV・TSV の形式が不正です: #{ e.message }"
end
def parse_json
value = JSON.parse(@source, max_nesting: 20)
@json_path.split('.').reject(&:blank?).each { |part| value = value.is_a?(Array) ? value[Integer(part)] : value.fetch(part) }
raise ArgumentError, 'JSON の投稿候補は配列にしてください.' unless value.is_a?(Array)
ensure_limits!(value)
keys = value.grep(Hash).flat_map(&:keys).map(&:to_s).uniq
raise ArgumentError, "列数は #{ MAX_COLUMNS } 列までです." if keys.length > MAX_COLUMNS
{ columns: keys.each_with_index.map { |key, index| { key:, label: "#{ column_name(index) }#{ key }" } },
rows: value.each_with_index.map { |item, index| { source_row: index + 1,
values: keys.map { json_cell(item.is_a?(Hash) ? item[_1] || item[_1.to_sym] : nil) } } } }
rescue JSON::ParserError, KeyError, ArgumentError => e
raise ArgumentError, "JSON の形式が不正です: #{ e.message }"
end
def ensure_limits! rows
raise ArgumentError, "取込件数は #{ MAX_ROWS } 件までです." if rows.length > MAX_ROWS
raise ArgumentError, 'セルが大きすぎます.' if rows.flatten.any? { _1.to_s.bytesize > MAX_CELL_BYTES }
end
def json_cell value
case value
when nil then ''
when String, Numeric, TrueClass, FalseClass then value.to_s
when Array, Hash then JSON.generate(value)
else raise ArgumentError, 'JSON のセル値が不正です.'
end
end
def column_name index
name = +''
loop do
name.prepend((65 + (index % 26)).chr)
index = (index / 26) - 1
break if index < 0
end
name
end
end