Class: LcpRuby::Export::ValueFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/lcp_ruby/export/value_formatter.rb

Overview

Formats field values for export output based on field type and user-selected options.

Constant Summary collapse

ENUM_MODES =
%w[label key both].freeze
BOOLEAN_MODES =
%w[true_false yes_no one_zero].freeze
DATE_MODES =
%w[iso locale custom].freeze

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ ValueFormatter

Returns a new instance of ValueFormatter.

Parameters:

  • options (Hash) (defaults to: {})

    formatting options from the export dialog “enum_mode” => “label” | “key” | “both” “date_format” => “iso” | “locale” | custom strftime pattern “datetime_format” => “iso” | “locale” | “epoch” | custom strftime pattern “boolean_mode” => “true_false” | “yes_no” | “one_zero” “decimal_separator” => “.” | “,”



15
16
17
# File 'lib/lcp_ruby/export/value_formatter.rb', line 15

def initialize(options = {})
  @options = (options || {}).transform_keys(&:to_s)
end

Instance Method Details

#format(value, field_definition: nil, model_name: nil, raw: false, output: :json) ⇒ Object?

Returns formatted value (String in label mode, native type in raw mode).

Parameters:

  • value (Object)

    the raw field value

  • field_definition (Metadata::FieldDefinition, nil) (defaults to: nil)
  • model_name (String, nil) (defaults to: nil)

    for i18n enum label lookup

  • raw (Boolean) (defaults to: false)

    when true, emit native types (numbers, booleans, ISO dates, enum keys, file hashes) instead of locale-formatted strings. Used by raw ‘.json` / `.csv` endpoints. See docs/design/raw_data_endpoints.md.

  • output (Symbol) (defaults to: :json)

    ‘:json` or `:csv`. Only consulted when `raw: true` to choose file/attachment shape (`filename` for JSON, URL for CSV).

Returns:

  • (Object, nil)

    formatted value (String in label mode, native type in raw mode)



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/lcp_ruby/export/value_formatter.rb', line 28

def format(value, field_definition: nil, model_name: nil, raw: false, output: :json)
  return nil if value.nil?

  type = field_definition&.type || infer_type(value)

  # has_many → terminal dot-paths (e.g. `tasks.status`, `tasks.due_date`,
  # `tasks.title`) deliver an Array of terminal values. Format each element
  # through the SAME type logic and recombine — a single chokepoint so
  # every type is handled, not just enum. Without it, non-enum Arrays fell
  # through to scalar parsers: `format_date([d1, d2])` parsed only the
  # first date (silent data loss) and string terminals emitted the Ruby
  # array literal `["a", "b"]`.
  #
  # Gate on the EXPLICIT field type, not `type`: only a declared `json`
  # field holds an Array that must serialize as JSON. When the field def
  # can't be resolved (field_definition nil), `type` would be
  # `infer_type(Array) == "json"` and wrongly skip the join — so a
  # has_many dot-path with no resolvable terminal would still emit the
  # Ruby array literal. Checking field_definition&.type keeps that case
  # joined.
  if value.is_a?(Array) && field_definition&.type != "json"
    formatted = value.map do |v|
      format(v, field_definition: field_definition, model_name: model_name, raw: raw, output: output)
    end
    # Raw JSON wants a native array of typed values; CSV and the
    # locale-formatted label modes want a single joined cell.
    return formatted if raw && output != :csv

    return formatted.compact.join(array_separator)
  end

  return format_raw(value, type, output) if raw

  case type
  when "enum"
    format_enum(value, field_definition, model_name)
  when "date"
    format_date(value)
  when "datetime"
    format_datetime(value)
  when "boolean"
    format_boolean(value)
  when "decimal", "float"
    format_decimal(value)
  when "json"
    value.is_a?(String) ? value : value.to_json
  else
    value.to_s
  end
end