このコミットが含まれているのは:
@@ -0,0 +1,56 @@
|
||||
require 'csv'
|
||||
|
||||
class PostImportSourceParser
|
||||
MAX_ROWS = 100
|
||||
MAX_BYTES = 1.megabyte
|
||||
MAX_CELL_BYTES = 20.kilobytes
|
||||
|
||||
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
|
||||
{ columns: (0...width).map { |index| { key: index.to_s,
|
||||
label: "#{ ('A'.ord + index).chr }:#{ 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
|
||||
{ columns: keys.each_with_index.map { |key, index| { key:, label: "#{ ('A'.ord + index).chr }:#{ key }" } },
|
||||
rows: value.each_with_index.map { |item, index| { source_row: index + 1,
|
||||
values: keys.map { 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
|
||||
end
|
||||
新しい課題から参照
ユーザをブロックする