Class: Iriq::CLI

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

Overview

Flag-driven CLI. The default action for an input is a combined parse + normalize + explain summary; the -p/-n/-e flags select individual sections. The only subcommand is ‘cluster`, which is structurally different (many inputs, not one). Construct with explicit IO so specs can run it without shelling out.

Constant Summary collapse

SECTION_FLAGS =
%i[parse normalize].freeze
TOP_N_STATS =
10
LARGE_BATCH_THRESHOLD =

When extraction yields this many or more IRIs, the default pipe output switches from a URL list to clusters — a longer list is easier to read as route-shape groups.

10
USAGE =
<<~TXT
  Usage: iriq [options] <input>
         iriq [options] < text
         iriq cluster [options] [file]

  <input> may be an IRI, a file path (extracted automatically), or piped
  text via stdin.

  Sections (combine freely):
    -n, --normalize       Shape-normalized form
    -p, --parse           Parsed fields

  Corpus + stats:
        --corpus PATH     Load/create a JSON corpus; observe and save atomically.
                          -n becomes corpus-informed once it has data.
        --stats           Print rolling aggregates

  Other:
    -h, --help            Show this message
    -j, --json            Emit JSON instead of human-readable output
    -N, --no-hints        Use {integer_id} placeholders instead of {user_id}
        --no-scheme-less  Skip foo.com/path extraction (explicit-scheme only)
    -V, --version         Print version

  Subcommands:
    cluster [file]        Force cluster view (default for ≥10 IRIs anyway)

  Examples:
    iriq foo.com/users/456
    iriq -n https://foo.com/users/123
    iriq ./access.log                     # auto-detect file → extract URLs
    cat README.md | iriq -n               # one normalized URL per line
    cat README.md | iriq --corpus c.json
TXT

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of CLI.



57
58
59
60
61
# File 'lib/iriq/cli.rb', line 57

def initialize(stdin: $stdin, stdout: $stdout, stderr: $stderr)
  @stdin  = stdin
  @stdout = stdout
  @stderr = stderr
end

Instance Attribute Details

#stderrObject (readonly)

Returns the value of attribute stderr.



55
56
57
# File 'lib/iriq/cli.rb', line 55

def stderr
  @stderr
end

#stdinObject (readonly)

Returns the value of attribute stdin.



55
56
57
# File 'lib/iriq/cli.rb', line 55

def stdin
  @stdin
end

#stdoutObject (readonly)

Returns the value of attribute stdout.



55
56
57
# File 'lib/iriq/cli.rb', line 55

def stdout
  @stdout
end

Instance Method Details

#parseable_iri?(input) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
107
108
109
# File 'lib/iriq/cli.rb', line 104

def parseable_iri?(input)
  Iriq.parse(input)
  true
rescue Iriq::ParseError
  false
end

#run(argv) ⇒ Object

Returns an integer exit code.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/iriq/cli.rb', line 64

def run(argv)
  args, opts = parse_options(argv)

  return print_usage(stdout, 0) if opts[:help]
  return print_version          if opts[:version]

  explicit_cluster = (args.first == "cluster")
  args.shift if explicit_cluster

  # Auto-detect: a positional argument that isn't parseable as an IRI
  # but IS an existing file gets treated as a file to extract from. This
  # is what makes `iriq ./access.log` and `iriq /var/log/foo.log` Just
  # Work without a separate --extract flag.
  positional_is_file = args.first && File.file?(args.first) && !parseable_iri?(args.first)

  batch_mode = explicit_cluster || positional_is_file ||
               (args.empty? && piped_stdin?)

  return print_usage(stdout, 0) if args.empty? && !batch_mode

  corpus = opts[:corpus] ? load_corpus(opts[:corpus]) : nil

  code = if batch_mode
    cmd_batch(args, opts, corpus, explicit_cluster: explicit_cluster)
  elsif opts[:stats]
    cmd_stats(corpus, opts)
  else
    cmd_summary(args, opts, corpus)
  end

  corpus.save(opts[:corpus]) if corpus && opts[:corpus]
  code
rescue Iriq::ParseError => e
  stderr.puts "iriq: parse error: #{e.message}"
  2
rescue OptionParser::ParseError => e
  stderr.puts "iriq: #{e.message}"
  1
end