Module: Eco::API::UseCases::GraphQL::Samples::Pages::Template::CsvBuild::FormatMap

Defined in:
lib/eco/api/usecases/graphql/samples/pages/template/csv_build/format_map.rb

Overview

ISOLATED column-format mapping for the CSV → template build pipeline.

============================ IMPORTANT / TODO ============================ The EXACT columnar CSV format for the ~300-template bulk build is due ~mid-July 2026 and is NOT yet confirmed. This class is the SINGLE place the assumed format is encoded, precisely so that when the real format lands, only THIS file (and its spec) needs to change — the Parser and Builder are written against the neutral intermediate RowSpec, not against column names.

Assumed format (documented here, one row per FIELD, hierarchy carried on each row):

stage,stage_ordering,section,section_ordering,section_layout, field_label,field_type,field_required,field_column,field_description,field_options

  • field_type — a template field type accepted by addField (e.g. plain_text, select, date, number, gauge, rich_text, people, ...). Passed through verbatim.
  • field_options — for select-type fields: pipe-separated label:weight pairs, e.g. "High:10|Medium:5|Low:0". Weight optional ("High|Medium|Low").
  • field_description — carries the SECTION/FIELD IDENTITY convention (see below).

SECTION / FIELD IDENTITY CONVENTION (per the CSV-pipeline project notes): stable identity for a section/field across rebuilds is expressed via a HIDDEN marker field + a DESCRIPTION token, not via the volatile Mongo id. The parser threads field_description through so the builder can stamp a deterministic identity token; sections inherit identity from their heading + a hidden

anchor field. This keeps a rebuilt template pair-able against a prior version by the diff engine.

To adapt to the real format: change COLUMNS + the #value/#options readers here. Everything downstream consumes RowSpec, which is format-agnostic.

Defined Under Namespace

Classes: RowSpec

Constant Summary collapse

COLUMNS =

Logical field => CSV header name (assumed). CHANGE HERE when the real format arrives.

{
  stage:             'stage',
  stage_ordering:    'stage_ordering',
  section:           'section',
  section_ordering:  'section_ordering',
  section_layout:    'section_layout',
  field_label:       'field_label',
  field_type:        'field_type',
  field_required:    'field_required',
  field_column:      'field_column',
  field_description: 'field_description',
  field_options:     'field_options'
}.freeze
OPTIONS_DELIMITER =

Delimiters (assumed). CHANGE HERE if the real format differs.

'|'.freeze
OPTION_WEIGHT_SEPARATOR =
':'.freeze

Class Method Summary collapse

Class Method Details

.integer(row, logical) ⇒ Object



95
96
97
98
# File 'lib/eco/api/usecases/graphql/samples/pages/template/csv_build/format_map.rb', line 95

def integer(row, logical)
  raw = value(row, logical)
  raw && Integer(raw, exception: false)
end

.options(cell) ⇒ Object

Parse the options cell into [ { label:, weight: }, ... ]. Empty → [].



76
77
78
79
80
81
82
83
# File 'lib/eco/api/usecases/graphql/samples/pages/template/csv_build/format_map.rb', line 76

def options(cell)
  return [] if cell.nil? || cell.to_s.strip.empty?

  cell.to_s.split(OPTIONS_DELIMITER).map do |token|
    label, weight = token.split(OPTION_WEIGHT_SEPARATOR, 2).map(&:strip)
    { label: label, weight: (weight && !weight.empty? ? Integer(weight, exception: false) : nil) }
  end
end

.row_spec(row) ⇒ Object

Map one CSV row (a Hash keyed by header string) to a RowSpec.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/eco/api/usecases/graphql/samples/pages/template/csv_build/format_map.rb', line 59

def row_spec(row)
  RowSpec.new(
    stage:             value(row, :stage),
    stage_ordering:    integer(row, :stage_ordering),
    section:           value(row, :section),
    section_ordering:  integer(row, :section_ordering),
    section_layout:    value(row, :section_layout) || 'content',
    field_label:       value(row, :field_label),
    field_type:        value(row, :field_type),
    field_required:    truthy?(row, :field_required),
    field_column:      integer(row, :field_column) || 0,
    field_description: value(row, :field_description),
    field_options:     options(value(row, :field_options))
  )
end

.truthy?(row, logical) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
103
104
105
# File 'lib/eco/api/usecases/graphql/samples/pages/template/csv_build/format_map.rb', line 100

def truthy?(row, logical)
  raw = value(row, logical)
  return false if raw.nil?

  %w[1 true yes y required].include?(raw.downcase)
end

.value(row, logical) ⇒ Object



85
86
87
88
89
90
91
92
93
# File 'lib/eco/api/usecases/graphql/samples/pages/template/csv_build/format_map.rb', line 85

def value(row, logical)
  header = COLUMNS.fetch(logical)
  raw    = row[header]
  raw = row[header.to_sym] if raw.nil? && row.respond_to?(:key?)
  return nil if raw.nil?

  str = raw.to_s.strip
  str.empty? ? nil : str
end