Class: Typed::CSVSerializer

Inherits:
Serializer show all
Defined in:
lib/typed/csv_serializer.rb

Overview

CSV is a flat, row-based format, so nested structs/hashes/arrays cannot be represented as their own columns. serialize fails with a SerializeError naming the offending field(s) rather than writing a lossy representation that deserialize could never parse back; see README's CSVSerializer section for the caveat.

Constant Summary collapse

Input =
type_member { {fixed: String} }
Output =
type_member { {fixed: String} }

Constants inherited from Serializer

Serializer::DeserializeResult, Serializer::Params

Instance Attribute Summary

Attributes inherited from Serializer

#coercer_cache, #schema

Instance Method Summary collapse

Constructor Details

#initialize(schema:) ⇒ CSVSerializer

Returns a new instance of CSVSerializer.



14
15
16
17
18
19
# File 'lib/typed/csv_serializer.rb', line 14

def initialize(schema:)
  require "csv"
  super
rescue LoadError
  raise ArgumentError, "csv gem is required for CSV serialization - add it to your Gemfile"
end

Instance Method Details

#deserialize(source) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/typed/csv_serializer.rb', line 22

def deserialize(source)
  parsed = CSV.parse(source, headers: true)
  return Failure.new(ParseError.new(format: :csv)) unless parsed.is_a?(CSV::Table)

  row = parsed.first
  return Failure.new(ParseError.new(format: :csv)) unless row.is_a?(CSV::Row)

  creation_params = schema.fields.each_with_object(T.let({}, Params)) do |field, hsh|
    hsh[field.name] = row[field.name.to_s]
  end

  deserialize_from_creation_params(creation_params)
rescue CSV::MalformedCSVError
  Failure.new(ParseError.new(format: :csv))
end

#serialize(struct) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/typed/csv_serializer.rb', line 39

def serialize(struct)
  return Failure.new(SerializeError.new("'#{struct.class}' cannot be serialized to target type of '#{schema.target}'.")) if struct.class != schema.target

  hsh = serialize_from_struct(struct:, should_serialize_values: true)

  non_scalar_fields = hsh.select { |_key, value| value.is_a?(Hash) || value.is_a?(Array) }.keys
  unless non_scalar_fields.empty?
    return Failure.new(SerializeError.new("'#{struct.class}' cannot be serialized to CSV because field(s) #{non_scalar_fields.join(", ")} are not scalar values."))
  end

  csv_string = CSV.generate do |csv|
    csv << hsh.keys.map(&:to_s)
    csv << hsh.values
  end

  Success.new(csv_string)
end