Class: Aspera::Cli::Manager
- Inherits:
-
Object
- Object
- Aspera::Cli::Manager
- Defined in:
- lib/aspera/cli/manager.rb
Overview
parse command line options arguments options start with ‘-’, others are commands resolves on extended value syntax
Instance Attribute Summary collapse
-
#ask_missing_mandatory ⇒ Object
Returns the value of attribute ask_missing_mandatory.
-
#ask_missing_optional ⇒ Object
Returns the value of attribute ask_missing_optional.
-
#parser ⇒ Object
readonly
Returns the value of attribute parser.
Class Method Summary collapse
- .cli_bad_arg(error_msg, choices) ⇒ Object
-
.get_from_list(shortval, descr, allowed_values) ⇒ Object
find shortened string value in allowed symbol list.
- .time_to_string(time) ⇒ Object
Instance Method Summary collapse
- #add_opt_boolean(option_symbol, help, *on_args) ⇒ Object
-
#add_opt_date(option_symbol, *on_args) ⇒ Object
define an option with date format.
-
#add_opt_list(option_symbol, values, help, *on_args) ⇒ Object
define an option with restricted values.
-
#add_opt_simple(option_symbol, *on_args) ⇒ Object
define an option with open values.
-
#add_opt_switch(option_symbol, *on_args, &block) ⇒ Object
define an option without value.
-
#add_option_preset(preset_hash, op = :push) ⇒ Object
param must be hash.
- #apply_options_preset(preset, where, force = false) ⇒ Object
-
#command_or_arg_empty? ⇒ Boolean
check if there were unprocessed values to generate error.
-
#declare_option(option_symbol, type) ⇒ Object
declare option of type :accessor, or :value.
-
#declared_options(all = true) ⇒ Object
return options as taken from config file and command line just before command execution.
- #enum_to_bool(enum) ⇒ Object
-
#final_errors ⇒ Object
unprocessed options or arguments ?.
- #get_interactive(type, descr, expected = :single) ⇒ Object
-
#get_next_argument(descr, expected = :single, is_type = :mandatory) ⇒ Object
Value, list or nil.
- #get_next_command(command_list) ⇒ Object
-
#get_option(option_symbol, is_type = :optional) ⇒ Object
get an option value by name either return value or call handler, can return nil ask interactively if requested/required.
-
#get_options_table(remove_from_remaining = true) ⇒ Object
get all original options on command line used to generate a config in config file.
- #highlight_current(value) ⇒ Object
-
#initialize(program_name, argv, app_banner) ⇒ Manager
constructor
A new instance of Manager.
-
#parse_options! ⇒ Object
removes already known options from the list.
- #prompt_user_input(prompt, sensitive) ⇒ Object
- #set_is_sensitive(option_symbol) ⇒ Object
-
#set_obj_attr(option_symbol, object, attr_symb, default_value = nil) ⇒ Object
define option with handler.
-
#set_option(option_symbol, value, where = "default") ⇒ Object
set an option value by name, either store value or call handler.
-
#symbol_to_option(symbol, opt_val) ⇒ Object
generate command line option from option symbol.
Constructor Details
#initialize(program_name, argv, app_banner) ⇒ Manager
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 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/aspera/cli/manager.rb', line 82 def initialize(program_name,argv,) # command line values not starting with '-' @unprocessed_cmd_line_arguments=[] # command line values starting with '-' =[] # a copy of all initial options =[] # option description: key = option symbol, value=hash, :type, :accessor, :value, :accepted ={} # do we ask missing options and arguments to user ? @ask_missing_mandatory=false # STDIN.isatty # ask optional options if not provided and in interactive @ask_missing_optional=false # those must be set before parse, parse consumes those defined only @unprocessed_defaults=[] @unprocessed_env=[] # Note: was initially inherited but it is prefered to have specific methods @parser=OptionParser.new @parser.program_name=program_name @parser.= # options can also be provided by env vars : --param-name -> ASLMCLI_PARAM_NAME env_prefix=program_name.upcase+OPTION_SEP_NAME ENV.each do |k,v| if k.start_with?(env_prefix) @unprocessed_env.push([k[env_prefix.length..-1].downcase.to_sym,v]) end end Log.log.debug("env=#{@unprocessed_env}".red) # banner is empty when help is generated for every plugin unless .empty? @parser.separator("") @parser.separator("OPTIONS: global") self.set_obj_attr(:interactive,self,:ask_missing_mandatory) self.set_obj_attr(:ask_options,self,:ask_missing_optional) self.add_opt_boolean(:interactive,"use interactive input of missing params") self.add_opt_boolean(:ask_options,"ask even optional options") self. end =[] @unprocessed_cmd_line_arguments=[] =true while !argv.empty? value=argv.shift if and value.start_with?('-') if value.eql?('--') =false else .push(value) end else @unprocessed_cmd_line_arguments.push(value) end end =.dup Log.log.debug("add_cmd_line_options:commands/args=#{@unprocessed_cmd_line_arguments},options=#{@unprocessed_cmd_line_options}".red) end |
Instance Attribute Details
#ask_missing_mandatory ⇒ Object
Returns the value of attribute ask_missing_mandatory.
78 79 80 |
# File 'lib/aspera/cli/manager.rb', line 78 def ask_missing_mandatory @ask_missing_mandatory end |
#ask_missing_optional ⇒ Object
Returns the value of attribute ask_missing_optional.
79 80 81 |
# File 'lib/aspera/cli/manager.rb', line 79 def ask_missing_optional @ask_missing_optional end |
#parser ⇒ Object (readonly)
Returns the value of attribute parser.
77 78 79 |
# File 'lib/aspera/cli/manager.rb', line 77 def parser @parser end |
Class Method Details
.cli_bad_arg(error_msg, choices) ⇒ Object
73 74 75 |
# File 'lib/aspera/cli/manager.rb', line 73 def self.cli_bad_arg(error_msg,choices) return CliBadArgument.new(error_msg+"\nUse:\n"+choices.map{|c| "- #{c.to_s}\n"}.sort.join('')) end |
.get_from_list(shortval, descr, allowed_values) ⇒ Object
find shortened string value in allowed symbol list
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/aspera/cli/manager.rb', line 62 def self.get_from_list(shortval,descr,allowed_values) # we accept shortcuts matching_exact=allowed_values.select{|i| i.to_s.eql?(shortval)} return matching_exact.first if matching_exact.length == 1 matching=allowed_values.select{|i| i.to_s.start_with?(shortval)} raise cli_bad_arg("unknown value for #{descr}: #{shortval}",allowed_values) if matching.empty? raise cli_bad_arg("ambigous shortcut for #{descr}: #{shortval}",matching) unless matching.length.eql?(1) return enum_to_bool(matching.first) if allowed_values.eql?(BOOLEAN_VALUES) return matching.first end |
.time_to_string(time) ⇒ Object
44 45 46 |
# File 'lib/aspera/cli/manager.rb', line 44 def self.time_to_string(time) time.strftime("%Y-%m-%d %H:%M:%S") end |
Instance Method Details
#add_opt_boolean(option_symbol, help, *on_args) ⇒ Object
329 330 331 |
# File 'lib/aspera/cli/manager.rb', line 329 def add_opt_boolean(option_symbol,help,*on_args) add_opt_list(option_symbol,BOOLEAN_VALUES,help,*on_args) end |
#add_opt_date(option_symbol, *on_args) ⇒ Object
define an option with date format
343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/aspera/cli/manager.rb', line 343 def add_opt_date(option_symbol,*on_args) declare_option(option_symbol,:value) Log.log.debug("add_opt_date #{option_symbol}") on_args.unshift(symbol_to_option(option_symbol,"DATE")) Log.log.debug("on_args=#{on_args}") @parser.on(*on_args) do |v| case v when 'now'; set_option(option_symbol,Manager.time_to_string(Time.now),"cmdline") when /^-([0-9]+)h/; set_option(option_symbol,Manager.time_to_string(Time.now-$1.to_i*3600),"cmdline") else set_option(option_symbol,v,"cmdline") end end end |
#add_opt_list(option_symbol, values, help, *on_args) ⇒ Object
define an option with restricted values
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/aspera/cli/manager.rb', line 312 def add_opt_list(option_symbol,values,help,*on_args) declare_option(option_symbol,:value) Log.log.debug("add_opt_list #{option_symbol}") on_args.unshift(symbol_to_option(option_symbol,'ENUM')) # this option value must be a symbol [option_symbol][:values]=values value=get_option(option_symbol) help_values=values.map{|i|i.eql?(value)?highlight_current(i):i}.join(', ') if values.eql?(BOOLEAN_VALUES) help_values=BOOLEAN_SIMPLE.map{|i|((i.eql?(:yes) and value) or (i.eql?(:no) and not value))?highlight_current(i):i}.join(', ') end on_args.push(values) on_args.push("#{help}: #{help_values}") Log.log.debug("on_args=#{on_args}") @parser.on(*on_args){|v|set_option(option_symbol,self.class.get_from_list(v.to_s,help,values),"cmdline")} end |
#add_opt_simple(option_symbol, *on_args) ⇒ Object
define an option with open values
334 335 336 337 338 339 340 |
# File 'lib/aspera/cli/manager.rb', line 334 def add_opt_simple(option_symbol,*on_args) declare_option(option_symbol,:value) Log.log.debug("add_opt_simple #{option_symbol}") on_args.unshift(symbol_to_option(option_symbol,"VALUE")) Log.log.debug("on_args=#{on_args}") @parser.on(*on_args) { |v| set_option(option_symbol,v,"cmdline") } end |
#add_opt_switch(option_symbol, *on_args, &block) ⇒ Object
define an option without value
358 359 360 361 362 363 |
# File 'lib/aspera/cli/manager.rb', line 358 def add_opt_switch(option_symbol,*on_args,&block) Log.log.debug("add_opt_on #{option_symbol}") on_args.unshift(symbol_to_option(option_symbol,nil)) Log.log.debug("on_args=#{on_args}") @parser.on(*on_args,&block) end |
#add_option_preset(preset_hash, op = :push) ⇒ Object
param must be hash
293 294 295 296 297 298 |
# File 'lib/aspera/cli/manager.rb', line 293 def add_option_preset(preset_hash,op=:push) Log.log.debug("add_option_preset=#{preset_hash}") raise "internal error: setting default with no hash: #{preset_hash.class}" if !preset_hash.is_a?(Hash) # incremental override preset_hash.each{|k,v|@unprocessed_defaults.send(op,[k.to_sym,v])} end |
#apply_options_preset(preset, where, force = false) ⇒ Object
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 |
# File 'lib/aspera/cli/manager.rb', line 409 def (preset,where,force=false) unprocessed=[] preset.each do |pair| k,v=*pair if .has_key?(k) # constrained parameters as string are revert to symbol if [k].has_key?(:values) and v.is_a?(String) v=self.class.get_from_list(v,k.to_s+" in #{where}",[k][:values]) end set_option(k,v,where) else unprocessed.push(pair) end end # keep only unprocessed values for next parse preset.clear preset.push(*unprocessed) end |
#command_or_arg_empty? ⇒ Boolean
check if there were unprocessed values to generate error
366 367 368 |
# File 'lib/aspera/cli/manager.rb', line 366 def command_or_arg_empty? return @unprocessed_cmd_line_arguments.empty? end |
#declare_option(option_symbol, type) ⇒ Object
declare option of type :accessor, or :value
209 210 211 212 213 214 215 216 217 218 |
# File 'lib/aspera/cli/manager.rb', line 209 def declare_option(option_symbol,type) Log.log.debug("declare_option: #{option_symbol}: #{type}: skip=#{@declared_options.has_key?(option_symbol)}".green) if .has_key?(option_symbol) raise "INTERNAL ERROR: option #{option_symbol} already declared. only accessor can be redeclared and ignored" unless [option_symbol][:type].eql?(:accessor) return end [option_symbol]={:type=>type} # by default passwords and secrets are sensitive, else specify when declaring the option set_is_sensitive(option_symbol) if !%w{password secret key}.select{|i| option_symbol.to_s.end_with?(i)}.empty? end |
#declared_options(all = true) ⇒ Object
return options as taken from config file and command line just before command execution
401 402 403 404 405 406 407 |
# File 'lib/aspera/cli/manager.rb', line 401 def (all=true) return .keys.inject({}) do |h,option_symb| v=get_option(option_symb) h[option_symb.to_s]=v if all or !v.nil? h end end |
#enum_to_bool(enum) ⇒ Object
59 |
# File 'lib/aspera/cli/manager.rb', line 59 def enum_to_bool(enum);TRUE_VALUES.include?(enum);end |
#final_errors ⇒ Object
unprocessed options or arguments ?
371 372 373 374 375 376 |
# File 'lib/aspera/cli/manager.rb', line 371 def final_errors result=[] result.push("unprocessed options: #{@unprocessed_cmd_line_options}") unless .empty? result.push("unprocessed values: #{@unprocessed_cmd_line_arguments}") unless @unprocessed_cmd_line_arguments.empty? return result end |
#get_interactive(type, descr, expected = :single) ⇒ Object
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 |
# File 'lib/aspera/cli/manager.rb', line 145 def get_interactive(type,descr,expected=:single) if !@ask_missing_mandatory if expected.is_a?(Array) raise self.class.cli_bad_arg("missing: #{descr}",expected) end raise CliBadArgument,"missing argument (#{expected}): #{descr}" end result=nil # Note: mandatory parenthesis here ! sensitive = (type.eql?(:option) and [descr.to_sym][:sensitive].eql?(true)) default_prompt="#{type}: #{descr}" # ask interactively case expected when :multiple result=[] puts " (one per line, end with empty line)" loop do entry=prompt_user_input(default_prompt,sensitive) break if entry.empty? result.push(ExtendedValue.instance.evaluate(entry)) end when :single result=ExtendedValue.instance.evaluate(prompt_user_input(default_prompt,sensitive)) else # one fixed result=self.class.get_from_list(prompt_user_input("#{expected.join(' ')}\n#{default_prompt}",sensitive),descr,expected) end return result end |
#get_next_argument(descr, expected = :single, is_type = :mandatory) ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/aspera/cli/manager.rb', line 182 def get_next_argument(descr,expected=:single,is_type=:mandatory) result=nil if !@unprocessed_cmd_line_arguments.empty? # there are values case expected when :single result=ExtendedValue.instance.evaluate(@unprocessed_cmd_line_arguments.shift) when :multiple result = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length).map{|v|ExtendedValue.instance.evaluate(v)} # if expecting list and only one arg of type array : it is the list if result.length.eql?(1) and result.first.is_a?(Array) result=result.first end else result=self.class.get_from_list(@unprocessed_cmd_line_arguments.shift,descr,expected) end else # no value provided if is_type.eql?(:mandatory) result=get_interactive(:argument,descr,expected) end end Log.log.debug("#{descr}=#{result}") return result end |
#get_next_command(command_list) ⇒ Object
174 |
# File 'lib/aspera/cli/manager.rb', line 174 def get_next_command(command_list); return get_next_argument('command',command_list); end |
#get_option(option_symbol, is_type = :optional) ⇒ Object
get an option value by name either return value or call handler, can return nil ask interactively if requested/required
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/aspera/cli/manager.rb', line 258 def get_option(option_symbol,is_type=:optional) result=nil if .has_key?(option_symbol) case [option_symbol][:type] when :accessor result=[option_symbol][:accessor].value when :value result=[option_symbol][:value] else raise "unknown type" end Log.log.debug("get #{option_symbol} (#{@declared_options[option_symbol][:type]}) : #{result}") end Log.log.debug("interactive=#{@ask_missing_mandatory}") if result.nil? if !@ask_missing_mandatory if is_type.eql?(:mandatory) raise CliBadArgument,"Missing mandatory option: #{option_symbol}" end else # ask_missing_mandatory if @ask_missing_optional or is_type.eql?(:mandatory) expected=:single #print "please enter: #{option_symbol.to_s}" if .has_key?(option_symbol) and [option_symbol].has_key?(:values) expected=[option_symbol][:values] end result=get_interactive(:option,option_symbol.to_s,expected) set_option(option_symbol,result,"interactive") end end end return result end |
#get_options_table(remove_from_remaining = true) ⇒ Object
get all original options on command line used to generate a config in config file
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
# File 'lib/aspera/cli/manager.rb', line 379 def (remove_from_remaining=true) result={} .each do |optionval| case optionval when /^--([^=]+)$/ # ignore when /^--([^=]+)=(.*)$/ name=$1 value=$2 name.gsub!(OPTION_SEP_LINE,OPTION_SEP_NAME) value=ExtendedValue.instance.evaluate(value) Log.log.debug("option #{name}=#{value}") result[name]=value .delete(optionval) if remove_from_remaining else raise CliBadArgument,"wrong option format: #{optionval}" end end return result end |
#highlight_current(value) ⇒ Object
307 308 309 |
# File 'lib/aspera/cli/manager.rb', line 307 def highlight_current(value) STDOUT.isatty ? value.to_s.red.bold : "[#{value}]" end |
#parse_options! ⇒ Object
removes already known options from the list
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/aspera/cli/manager.rb', line 429 def Log.log.debug("parse_options!".red) # first conf file, then env var (@unprocessed_defaults,"file") (@unprocessed_env,"env") # command line override =[] begin # remove known options one by one, exception if unknown Log.log.debug("before parse".red) @parser.parse!() Log.log.debug("After parse".red) rescue OptionParser::InvalidOption => e Log.log.debug("InvalidOption #{e}".red) # save for later processing .push(e.args.first) retry end Log.log.debug("remains: #{unknown_options}") # set unprocessed options for next time = end |
#prompt_user_input(prompt, sensitive) ⇒ Object
139 140 141 142 143 |
# File 'lib/aspera/cli/manager.rb', line 139 def prompt_user_input(prompt,sensitive) return STDIN.getpass("#{prompt}> ") if sensitive print "#{prompt}> " return STDIN.gets.chomp end |
#set_is_sensitive(option_symbol) ⇒ Object
220 221 222 |
# File 'lib/aspera/cli/manager.rb', line 220 def set_is_sensitive(option_symbol) [option_symbol][:sensitive]=true end |
#set_obj_attr(option_symbol, object, attr_symb, default_value = nil) ⇒ Object
define option with handler
225 226 227 228 229 230 |
# File 'lib/aspera/cli/manager.rb', line 225 def set_obj_attr(option_symbol,object,attr_symb,default_value=nil) Log.log.debug("set attr obj #{option_symbol} (#{object},#{attr_symb})") declare_option(option_symbol,:accessor) [option_symbol][:accessor]=AttrAccessor.new(object,attr_symb) set_option(option_symbol,default_value,"default obj attr") if !default_value.nil? end |
#set_option(option_symbol, value, where = "default") ⇒ Object
set an option value by name, either store value or call handler
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/aspera/cli/manager.rb', line 233 def set_option(option_symbol,value,where="default") if ! .has_key?(option_symbol) Log.log.debug("set unknown option: #{option_symbol}") raise "ERROR" #declare_option(option_symbol) end value=ExtendedValue.instance.evaluate(value) Log.log.debug("set_option(#{where}) #{option_symbol}=#{value}") if [option_symbol][:values].eql?(BOOLEAN_VALUES) value=enum_to_bool(value) end Log.log.debug("set #{option_symbol}=#{value} (#{@declared_options[option_symbol][:type]}) : #{where}".blue) case [option_symbol][:type] when :accessor [option_symbol][:accessor].value=value when :value [option_symbol][:value]=value else # nil or other raise "error" end end |
#symbol_to_option(symbol, opt_val) ⇒ Object
generate command line option from option symbol
301 302 303 304 305 |
# File 'lib/aspera/cli/manager.rb', line 301 def symbol_to_option(symbol,opt_val) result='--'+symbol.to_s.gsub(OPTION_SEP_NAME,OPTION_SEP_LINE) result=result+'='+opt_val unless opt_val.nil? return result end |