Class: Exwiw::CLI

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

Constant Summary collapse

KNOWN_SUBCOMMANDS =
%w[export explain schema].freeze
SCHEMA_VERBS =

The verbs exwiw schema takes, mirroring the exwiw:schema:* rake tasks a Rails application would run instead.

%w[generate check tidy].freeze
SCHEMA_CHECK_DIRTY_EXIT =

schema check exit codes. 1 means "the config needs work" — that is the signal CI acts on — so a failure to perform the check (an unreachable database, a malformed config) must not use it, or an infrastructure problem reads as a schema problem and gets "fixed" by regenerating.

1
SCHEMA_CHECK_ERROR_EXIT =
2
DEFAULT_CONFIG_PATHS =

Config file loaded automatically when --config is omitted, if one exists in the current directory. Kept at the project root (rather than under exwiw/) so that config-relative paths like schema_dir: exwiw/schema read naturally. Both extensions are accepted; .yml wins when both are present.

%w[exwiw.yml exwiw.yaml].freeze
ALLOWED_CONFIG_KEYS =

Keys accepted in the config file. Anything outside this set is rejected so a typo surfaces immediately instead of being silently ignored. These mirror the non-connection CLI options (plus adapter).

%w[
  adapter
  schema_dir
  output_dir
  output_format
  insert_only
  after_insert_hook
  log_level
  target_table
  target_collection
  ids
  ids_field
  scope_column
  parallel_workers
  explain_verbosity
  mongodb_query_timeout_ms
].freeze
EXPLAIN_VERBOSITIES =

MongoDB explain verbosity levels (passed through to the server's explain command). queryPlanner only plans the query and is the safe default — the query is not executed; the other two run it to gather runtime stats.

%w[queryPlanner executionStats allPlansExecution].freeze
DEFAULT_EXPLAIN_VERBOSITY =
"queryPlanner"
REJECTED_CONNECTION_KEYS =

Database connection settings are environment-specific (and sometimes secret-adjacent), so they must be passed via CLI/env, never the committed config file. adapter is the one connection-ish key allowed in config.

%w[host port user database uri password].freeze
EXPORT_ONLY_CONFIG_KEYS =

Keys that only make sense for export. They are skipped when merging config for explain so a shared config file does not trip validate_explain_only!, and for schema, which performs no export at all.

%w[output_dir output_format insert_only after_insert_hook parallel_workers].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(argv) ⇒ CLI

Returns a new instance of CLI.



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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/exwiw/cli.rb', line 75

def initialize(argv)
  @argv = argv.dup

  @subcommand =
    if !@argv.empty? && !@argv.first.start_with?("-") && KNOWN_SUBCOMMANDS.include?(@argv.first)
      @argv.shift
    else
      "export"
    end

  # `schema` is the one subcommand taking a verb of its own
  # (`exwiw schema generate`), so the next positional argument belongs to
  # it. An unknown or missing verb is not consumed here — validation
  # reports it with the list of valid ones rather than OptionParser
  # failing on a stray argument.
  @schema_verb =
    if @subcommand == "schema" && !@argv.empty? && !@argv.first.start_with?("-")
      @argv.shift
    end

  @help = @argv.empty?

  @database_host = nil
  @database_port = nil
  @database_user = nil
  @database_password = ENV["DATABASE_PASSWORD"]
  @connection_uri = nil
  @output_dir = nil
  @schema_dir = nil
  @from_db = false
  @config_file_path = nil
  @database_adapter = nil
  @database_name = nil
  @target_table_name = nil
  @target_collection_name = nil
  @ids = []
  @ids_field = nil
  @scope_column = nil
  @output_format = nil
  @insert_only = nil
  @after_insert_hook_path = nil
  @parallel_workers = nil
  @mongodb_query_timeout_ms = nil
  @explain_verbosity = nil
  # nil (not :info) so we can tell "user passed --log-level" from the default,
  # letting a config-file value fill in; the :info default is applied later.
  @log_level = nil

  parser.parse!(@argv)
end

Class Method Details

.start(argv) ⇒ Object



71
72
73
# File 'lib/exwiw/cli.rb', line 71

def self.start(argv)
  new(argv).run
end

Instance Method Details

#runObject



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/exwiw/cli.rb', line 126

def run
  if @help
    puts parser.help
    return
  end

  validate_options!

  connection_config = ConnectionConfig.new(
    adapter: @database_adapter,
    host: @database_host,
    port: @database_port,
    user: @database_user,
    password: @database_password,
    database_name: @database_name,
    uri: @connection_uri,
    mongodb_query_timeout_ms: @mongodb_query_timeout_ms,
  )

  dump_target = DumpTarget.new(
    table_name: @target_table_name,
    ids: @ids,
    ids_field: @ids_field,
    scope_column: @scope_column,
  )

  logger = build_logger

  case @subcommand
  when "export"
    confirm_output_dir_clear!
    Runner.new(
      connection_config: connection_config,
      output_dir: @output_dir,
      schema_dir: @schema_dir,
      dump_target: dump_target,
      output_format: @output_format,
      insert_only: @insert_only,
      after_insert_hook_path: @after_insert_hook_path,
      parallel_workers: @parallel_workers,
      cli_options: build_cli_options_hash,
      logger: logger,
    ).run
  when "explain"
    ExplainRunner.new(
      connection_config: connection_config,
      schema_dir: @schema_dir,
      dump_target: dump_target,
      logger: logger,
      io: $stdout,
      explain_verbosity: @explain_verbosity,
    ).run
  when "schema"
    run_schema(connection_config)
  end
end