Module: Timeprice::Sources::Coverage

Defined in:
lib/timeprice/sources/coverage.rb

Overview

Computes coverage strings for bundled data sources at runtime. All filesystem reads happen here so the Sources attribution registry stays a pure data table.

Class Method Summary collapse

Class Method Details

.cpi(country) ⇒ Object



26
27
28
29
30
31
32
33
34
# File 'lib/timeprice/sources/coverage.rb', line 26

def cpi(country)
  data = DataLoader.load_cpi(country)
  monthly = (data["monthly"] || {}).keys.sort
  annual  = (data["annual"]  || {}).keys.sort
  parts = []
  parts << "monthly #{monthly.first}..#{monthly.last} (#{monthly.size})" unless monthly.empty?
  parts << "annual #{annual.first}..#{annual.last} (#{annual.size})" unless annual.empty?
  parts.join(", ")
end

.ecb_summary(years) ⇒ Object



54
55
56
57
58
59
# File 'lib/timeprice/sources/coverage.rb', line 54

def ecb_summary(years)
  ecb_years = years.select { |y| year_has_currency?(y, %w[EUR GBP JPY]) }
  return "no ECB data" if ecb_years.empty?

  "USD↔EUR/GBP/JPY daily #{ecb_years.first}..#{ecb_years.last}"
end

.for(src) ⇒ String

Parameters:

  • src (Hash)

    one entry from Sources::ATTRIBUTIONS

Returns:

  • (String)


16
17
18
19
20
21
22
23
24
# File 'lib/timeprice/sources/coverage.rb', line 16

def for(src)
  case src[:kind]
  when "cpi" then cpi(src[:country])
  when "fx"  then fx(src[:id])
  else "n/a"
  end
rescue StandardError => e
  "(coverage unavailable: #{e.message})"
end

.fx(id) ⇒ Object



36
37
38
39
40
41
# File 'lib/timeprice/sources/coverage.rb', line 36

def fx(id)
  years = fx_years
  return "no data" if years.empty?

  id == "fx_vnd" ? vnd_summary(years) : ecb_summary(years)
end

.fx_rootObject



66
67
68
# File 'lib/timeprice/sources/coverage.rb', line 66

def fx_root
  File.join(DataLoader.data_root, "fx", "usd")
end

.fx_yearsObject



43
44
45
# File 'lib/timeprice/sources/coverage.rb', line 43

def fx_years
  Dir[File.join(fx_root, "*.json")].map { |f| File.basename(f, ".json").to_i }.sort
end

.vnd_summary(years) ⇒ Object



47
48
49
50
51
52
# File 'lib/timeprice/sources/coverage.rb', line 47

def vnd_summary(years)
  with_vnd = years.select { |y| year_has_currency?(y, %w[VND]) }
  return "no VND data" if with_vnd.empty?

  "USD↔VND #{with_vnd.first}..#{with_vnd.last}"
end

.year_has_currency?(year, codes) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
64
# File 'lib/timeprice/sources/coverage.rb', line 61

def year_has_currency?(year, codes)
  rates = JSON.parse(File.read(File.join(fx_root, "#{year}.json")))["rates"]
  rates.any? { |_, v| v.keys.intersect?(codes) }
end