Module: Rubycli::CommandLine
- Defined in:
- lib/rubycli/command_line.rb
Defined Under Namespace
Classes: Options
Constant Summary collapse
- USAGE =
<<~USAGE Usage: rubycli [--new|-n[=<value>]] [--pre-script=<src>] [--json-args|-j | --eval-args|-e | --eval-lax|-E] [--strict] [--check|-c] <target-path> [<class-or-module>] [-- <cli-args>...] Examples: rubycli path/to/greeter.rb greet Hanako rubycli path/to/multi_runner.rb AlternateRunner greet --name Ruby rubycli --auto-target path/to/mismatched_runner.rb greet Hanako rubycli --new='["a","b"]' path/to/collection_runner.rb run --mode summary rubycli --json-args --new='["a","b"]' path/to/collection_runner.rb run --mode '"summary"' Options: --new, -n [<value>] Instantiate the class/module before invoking CLI commands; one optional literal can follow and is bound to the constructor's first parameter (respects --json-args/--eval-args/--eval-lax; use --pre-script for several or keyword arguments) --pre-script=<src> Evaluate Ruby code and use its result as the exposed target (--init alias; also accepts space-separated form) --json-args, -j Parse all following arguments strictly as JSON (no YAML literals; even plain words need quoting) --eval-args, -e Evaluate following arguments as Ruby code (every argument must be valid Ruby) --eval-lax, -E Evaluate as Ruby but fall back to raw strings when parsing fails --auto-target, -a Auto-select the only callable constant when names don't match --strict Enforce documented input types/choices (invalid values abort) --check, -c Validate documentation/comments without executing commands --help, -h Show this message (the bare word `help` works too) (Note: --json-args cannot be combined with --eval-args or --eval-lax) (Note: Every option that accepts a value understands both --flag=value and --flag value forms.) When <class-or-module> is omitted, Rubycli infers it from the file name in CamelCase. Arguments are parsed as safe literals by default; pick a mode above if you need strict JSON or Ruby eval. Method return values are printed to STDOUT by default. <cli-args> are forwarded to Rubycli unchanged. USAGE
Class Method Summary collapse
- .consume_new_arguments!(args, options, inline_value) ⇒ Object
- .consume_pre_script_source!(args, options) ⇒ Object
- .dispatch(target_path, class_or_module, args, options) ⇒ Object
- .environment_state ⇒ Object
-
.extract_target!(args) ⇒ Object
Removes the target path and the optional explicit constant from
args. - .likely_new_args_value?(token) ⇒ Boolean
-
.parse_flags!(args, options) ⇒ Object
Consumes leading flags from
argsand fillsoptions. -
.print_usage(status) ⇒ Object
Usage requested with --help goes to stdout; usage shown because the invocation was wrong is a diagnostic and belongs on stderr.
-
.reject_conflicting_flags(options, args) ⇒ Object
Returns an exit status when the flag combination is unusable, otherwise nil.
- .restore_environment(state) ⇒ Object
- .run(argv = ARGV) ⇒ Object
Class Method Details
.consume_new_arguments!(args, options, inline_value) ⇒ Object
144 145 146 147 148 149 150 151 152 153 |
# File 'lib/rubycli/command_line.rb', line 144 def consume_new_arguments!(args, , inline_value) .new_flag = true .new_args = inline_value return unless inline_value.nil? candidate = args.first return unless candidate && !candidate.start_with?('-') && likely_new_args_value?(candidate) .new_args = args.shift end |
.consume_pre_script_source!(args, options) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/rubycli/command_line.rb', line 155 def consume_pre_script_source!(args, ) flag = args.shift source = args.shift unless source warn "[ERROR] #{flag} requires a file path or inline Ruby code" return 1 end context = File.file?(source) ? File.(source) : "(inline #{flag})" .pre_scripts << { value: source, context: context } nil end |
.dispatch(target_path, class_or_module, args, options) ⇒ Object
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/rubycli/command_line.rb', line 200 def dispatch(target_path, class_or_module, args, ) if .check_mode return Rubycli::Runner.check( target_path, class_or_module, new: .new_flag, new_args: .new_args, pre_scripts: .pre_scripts, constant_mode: .constant_mode ) end status = Rubycli::Runner.execute( target_path, class_or_module, args, new: .new_flag, new_args: .new_args, json: .json_mode, eval_args: .eval_mode, eval_lax: .eval_lax, pre_scripts: .pre_scripts, constant_mode: .constant_mode ) # The runner reports the CLI status; treat anything else as success so the # process always exits with a defined status. status.is_a?(Integer) ? status : 0 end |
.environment_state ⇒ Object
237 238 239 240 241 242 243 244 |
# File 'lib/rubycli/command_line.rb', line 237 def environment_state environment = Rubycli.environment { doc_check: environment.doc_check_mode?, strict_input: environment.strict_input?, print_result: environment.print_result? } end |
.extract_target!(args) ⇒ Object
Removes the target path and the optional explicit constant from args.
169 170 171 172 173 174 175 176 |
# File 'lib/rubycli/command_line.rb', line 169 def extract_target!(args) target_path = args.shift class_or_module = nil candidate = args.first class_or_module = args.shift if candidate && candidate != '--' && candidate.match?(/\A[A-Z]/) args.shift if args.first == '--' [target_path, class_or_module] end |
.likely_new_args_value?(token) ⇒ Boolean
253 254 255 |
# File 'lib/rubycli/command_line.rb', line 253 def likely_new_args_value?(token) token.include?(',') || token.start_with?('[', '{') end |
.parse_flags!(args, options) ⇒ Object
Consumes leading flags from args and fills options.
Returns an exit status when the CLI should stop, otherwise nil.
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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/rubycli/command_line.rb', line 88 def parse_flags!(args, ) until args.empty? case args.first when '-h', '--help', 'help' return print_usage(0) when /\A--new(?:=(.+))?\z/ inline_value = Regexp.last_match(1) args.shift consume_new_arguments!(args, , inline_value) when '-n' args.shift consume_new_arguments!(args, , nil) when /\A--pre-script=(.+)\z/, /\A--init=(.+)\z/ flag = Regexp.last_match(0).start_with?('--pre-script') ? '--pre-script' : '--init' .pre_scripts << { value: Regexp.last_match(1), context: "(inline #{flag})" } args.shift when '--pre-script', '--init' status = consume_pre_script_source!(args, ) return status if status when '--json-args', '-j' .json_mode = true args.shift when '--eval-args', '-e' .eval_mode = true args.shift when '--eval-lax', '-E' .eval_mode = true .eval_lax = true args.shift when '--strict' Rubycli.environment.enable_strict_input! args.shift when '--check', '-c' .check_mode = true Rubycli.environment.enable_doc_check! args.shift when '--print-result' args.shift when '--debug' args.shift warn '[ERROR] --debug flag has been removed; set RUBYCLI_DEBUG=true instead.' return 1 when '--auto-target', '-a' .constant_mode = :auto args.shift when '--' args.shift break else break end end nil end |
.print_usage(status) ⇒ Object
Usage requested with --help goes to stdout; usage shown because the invocation was wrong is a diagnostic and belongs on stderr.
232 233 234 235 |
# File 'lib/rubycli/command_line.rb', line 232 def print_usage(status) (status.zero? ? $stdout : $stderr).puts(USAGE) status end |
.reject_conflicting_flags(options, args) ⇒ Object
Returns an exit status when the flag combination is unusable, otherwise nil.
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/rubycli/command_line.rb', line 179 def reject_conflicting_flags(, args) if .json_mode && .eval_mode warn '[ERROR] --json-args cannot be combined with --eval-args or --eval-lax' return 1 end return nil unless .check_mode if .json_mode || .eval_mode warn '[ERROR] --check cannot be combined with --json-args or --eval-args' return 1 end unless args.empty? warn '[ERROR] --check does not accept command arguments' return 1 end nil end |
.restore_environment(state) ⇒ Object
246 247 248 249 250 251 |
# File 'lib/rubycli/command_line.rb', line 246 def restore_environment(state) environment = Rubycli.environment state[:doc_check] ? environment.enable_doc_check! : environment.disable_doc_check! state[:strict_input] ? environment.enable_strict_input! : environment.disable_strict_input! state[:print_result] ? environment.enable_print_result! : environment.disable_print_result! end |
.run(argv = ARGV) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/rubycli/command_line.rb', line 62 def run(argv = ARGV) previous_state = environment_state args = Array(argv).dup Rubycli.environment.enable_print_result! return print_usage(1) if args.empty? = Options.defaults status = parse_flags!(args, ) return status if status return print_usage(1) if args.empty? target_path, class_or_module = extract_target!(args) status = reject_conflicting_flags(, args) return status if status Rubycli.environment.clear_documentation_issues! dispatch(target_path, class_or_module, args, ) rescue Rubycli::Runner::Error => e warn "[ERROR] #{e.}" 1 ensure restore_environment(previous_state) end |