Module: Timeprice::Schema

Defined in:
lib/timeprice/schema.rb

Overview

Single source of truth for the on-disk v4 CPI/manifest format. Both the reader (DataLoader) and the writer (today: pipeline ‘CountryFile`) route through here so the schema lives in exactly one place.

Constant Summary collapse

CURRENT_VERSION =
4
SUPPORTED_VERSIONS =
[3, 4].freeze
KEY_SCHEMA_VERSION =
"schema_version"
KEY_COUNTRY =
"country"
KEY_INDEX =
"index"
KEY_SERIES =
"series"
KEY_PROVENANCE =
"provenance"
KEY_PROVIDERS =
"providers"
GRANULARITIES =
%i[monthly quarterly annual].freeze
BASE_YEAR_RE =
/\A(?<period>.+?)=100(?:\s*\(rebased\s+(?<rebased>\d{4}-\d{2}-\d{2})\))?\z/

Class Method Summary collapse

Class Method Details

.assert_supported!(version, path) ⇒ Object



30
31
32
33
34
# File 'lib/timeprice/schema.rb', line 30

def assert_supported!(version, path)
  return if supported?(version)

  fail UnsupportedSchemaVersion.new(version, path)
end

.deserialise_base_year(index) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/timeprice/schema.rb', line 68

def deserialise_base_year(index)
  return nil unless index.is_a?(Hash)

  period = index["base_period"]
  rebased = index["rebased_at"]
  return nil if period.nil?

  rebased ? "#{period}=100 (rebased #{rebased})" : "#{period}=100"
end

.dump_cpi(country:, base_year:, monthly:, annual:, providers:, provenance:, quarterly: {}) ⇒ Object

Build a CPI payload ready for JSON.dump. Series keys are emitted in a stable order (annual, monthly[, quarterly]) so file diffs stay tight.



38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/timeprice/schema.rb', line 38

def dump_cpi(country:, base_year:, monthly:, annual:, providers:, provenance:, quarterly: {})
  series = { "annual" => annual, "monthly" => monthly }
  series["quarterly"] = quarterly unless quarterly.empty?
  {
    KEY_SCHEMA_VERSION => CURRENT_VERSION,
    KEY_COUNTRY => country.to_s.upcase,
    KEY_INDEX => serialise_base_year(base_year),
    KEY_SERIES => series,
    KEY_PROVENANCE => provenance,
    KEY_PROVIDERS => providers,
  }
end

.load_cpi(parsed, path:) ⇒ Object

Validate a parsed payload (read from disk) against the schema, then return it unchanged. Raises UnsupportedSchemaVersion if the version field is missing or unknown.



54
55
56
57
# File 'lib/timeprice/schema.rb', line 54

def load_cpi(parsed, path:)
  assert_supported!(parsed[KEY_SCHEMA_VERSION], path)
  parsed
end

.serialise_base_year(str) ⇒ Object



59
60
61
62
63
64
65
66
# File 'lib/timeprice/schema.rb', line 59

def serialise_base_year(str)
  m = BASE_YEAR_RE.match(str.to_s)
  if m
    { "base_period" => m[:period], "rebased_at" => m[:rebased] }
  else
    { "base_period" => str.to_s, "rebased_at" => nil }
  end
end

.supported?(version) ⇒ Boolean

Returns:

  • (Boolean)


26
27
28
# File 'lib/timeprice/schema.rb', line 26

def supported?(version)
  SUPPORTED_VERSIONS.include?(version)
end