Class: Timeprice::CLI

Inherits:
Thor
  • Object
show all
Defined in:
lib/timeprice/cli.rb,
lib/timeprice/cli/formatting.rb,
lib/timeprice/cli/presenters/compare.rb,
lib/timeprice/cli/presenters/sources.rb,
lib/timeprice/cli/presenters/exchange.rb,
lib/timeprice/cli/presenters/inflation.rb

Defined Under Namespace

Modules: Formatting, Presenters

Constant Summary collapse

KNOWN_COMMANDS =
%w[inflation fx compare sources version].freeze
HELP_ROWS =

Top-level help lists command names + descriptions only — matching git, gh, cargo. Arg signatures live in per-command help (‘timeprice help fx`).

[
  ["inflation", "Inflation-adjust an amount between two dates"],
  ["fx",        "Convert an amount between currencies on a date"],
  ["compare",   "Combine FX + inflation across (year, currency) points"],
  ["sources",   "List bundled data sources and coverage"],
  ["version",   "Print the installed timeprice version"],
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.all_commandsObject

Thor 1.5 ships a built-in ‘tree` command on every subclass. Strip it from this subclass — it’s an internal debugging aid that leaks into our help output. all_commands inherits from the base Thor class, so filter it on read.



17
18
19
# File 'lib/timeprice/cli.rb', line 17

def self.all_commands
  super.except("tree")
end

.exit_on_failure?Boolean

Return false so Thor::Error propagates to our wrapper in ‘start`, where we prettify the message and add a `See: timeprice help COMMAND` hint.

Returns:

  • (Boolean)


25
26
27
# File 'lib/timeprice/cli.rb', line 25

def self.exit_on_failure?
  false
end

.help(shell, subcommand = false) ⇒ Object

Thor’s API dictates the positional boolean signature — keyword arg would break the override.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/timeprice/cli.rb', line 61

def self.help(shell, subcommand = false) # rubocop:disable Style/OptionalBooleanParameter
  return super if subcommand

  shell.say "timeprice — offline historical inflation & FX for Ruby"
  shell.say ""
  shell.say "Commands:"
  width = HELP_ROWS.map { |usage, _| usage.length }.max
  HELP_ROWS.each do |usage, desc|
    shell.say format("  %-#{width}s  %s", usage, desc)
  end
  shell.say ""
  shell.say "Global options:"
  shell.say "  --json   Output result as JSON"
  shell.say ""
  shell.say "Run `timeprice help COMMAND` for usage and options."
end

.prettify_thor_message(msg) ⇒ Object



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

def self.prettify_thor_message(msg)
  msg
    .sub(/\ANo value provided for required options /, "missing required options: ")
    .gsub("'", "")
end

.start(given_args = ARGV, config = {}) ⇒ Object

Pass debug: true so Thor re-raises Thor::Error (including option-parse failures) instead of printing its own message and silently continuing when exit_on_failure? is false. We catch and format ourselves.



44
45
46
47
48
49
50
51
# File 'lib/timeprice/cli.rb', line 44

def self.start(given_args = ARGV, config = {})
  super(given_args, config.merge(debug: true))
rescue Thor::Error => e
  warn "Error: #{prettify_thor_message(e.message)}"
  cmd = given_args.first
  warn "  See: timeprice help #{cmd}" if KNOWN_COMMANDS.include?(cmd)
  exit 1
end

Instance Method Details

#compare(amount) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/timeprice/cli.rb', line 111

def compare(amount)
  with_error_handling do
    from_tuple = parse_compare_token(options[:from], label: "--from")
    to_tuple   = parse_compare_token(options[:to],   label: "--to")
    result = Timeprice.compare(
      amount: parse_amount(amount),
      from: from_tuple,
      to: to_tuple
    )
    render Presenters::Compare.new(result)
  end
end

#fx(amount, from_currency, to_currency) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
# File 'lib/timeprice/cli.rb', line 96

def fx(amount, from_currency, to_currency)
  with_error_handling do
    result = Timeprice.exchange(
      amount: parse_amount(amount),
      from: from_currency,
      to: to_currency,
      date: options[:date]
    )
    render Presenters::Exchange.new(result)
  end
end

#inflation(amount) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/timeprice/cli.rb', line 82

def inflation(amount)
  with_error_handling do
    result = Timeprice.inflation(
      amount: parse_amount(amount),
      from: options[:from],
      to: options[:to],
      country: options[:country]
    )
    render Presenters::Inflation.new(result)
  end
end

#sourcesObject



127
128
129
# File 'lib/timeprice/cli.rb', line 127

def sources
  render Presenters::Sources.new(Timeprice::Sources.list, verbose: options[:verbose])
end

#versionObject



132
133
134
135
136
137
138
# File 'lib/timeprice/cli.rb', line 132

def version
  if options[:json]
    say JSON.generate({ version: Timeprice::VERSION, repo: "patrick204nqh/timeprice" })
  else
    say "timeprice #{Timeprice::VERSION} — patrick204nqh/timeprice"
  end
end