Class: DBF::CLI

Inherits:
Object
  • Object
show all
Defined in:
lib/dbf/cli.rb

Defined Under Namespace

Classes: TerminalFilter

Constant Summary collapse

USAGE =
<<~HELP
  usage: dbf [-h|-s|-a|-c|-r|-j|-J] filename
    -h = print this message
    -v = print the DBF gem version
    -s = print summary information
    -a = create an ActiveRecord::Schema
    -r = create a Sequel migration
    -c = export as CSV
    -j = export as a JSON array
    -J = export as JSON Lines (one record per line)
HELP
CONTROL_BYTES =

Bytes a terminal interprets as control or escape sequences. A crafted DBF can carry these in column names and record values, so they are replaced before file-derived text reaches an interactive terminal.

/[\x00-\x1F\x7F]/n
CONTROL_BYTES_KEEPING_NEWLINES =

The same, but keeping CR and LF so CSV row separators survive.

/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/n
ACTIONS =
{
  'a' => :print_ar_schema,
  'r' => :print_sequel_schema,
  's' => :print_summary,
  'c' => :print_csv,
  'j' => :print_json,
  'J' => :print_jsonl
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(argv, stdout: $stdout, stderr: $stderr) ⇒ CLI

Returns a new instance of CLI.



67
68
69
70
71
# File 'lib/dbf/cli.rb', line 67

def initialize(argv, stdout: $stdout, stderr: $stderr)
  @argv = argv.dup
  @stdout = stdout
  @stderr = stderr
end

Class Method Details

.run(argv, stdout: $stdout, stderr: $stderr) ⇒ Object



63
64
65
# File 'lib/dbf/cli.rb', line 63

def self.run(argv, stdout: $stdout, stderr: $stderr)
  new(argv, stdout: stdout, stderr: stderr).run
end

.sanitize(value, pattern = CONTROL_BYTES) ⇒ String

Replaces terminal control bytes. Substitution happens on a byte copy so a value whose bytes are invalid in its encoding cannot raise here.

Parameters:

  • value (Object)
  • pattern (Regexp) (defaults to: CONTROL_BYTES)

Returns:

  • (String)


32
33
34
35
# File 'lib/dbf/cli.rb', line 32

def self.sanitize(value, pattern = CONTROL_BYTES)
  string = value.to_s
  string.b.gsub(pattern, '?').force_encoding(string.encoding)
end

Instance Method Details

#runObject



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/dbf/cli.rb', line 82

def run
  params = OptionParser.new.getopts(@argv, 'hsacrvjJ')

  if params['v']
    print_version
  elsif params['h']
    print_help
  else
    filename = @argv.shift
    return missing_filename unless filename

    action = ACTIONS.find { |flag, _method| params[flag] }&.last
    send(action, filename) if action
  end
  0
rescue DBF::FileNotFoundError => e
  @stderr.puts "DBF::FileNotFoundError: #{e.message}"
  1
end