aspera-cli 4.5.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -0
  3. data/README.md +1894 -1574
  4. data/bin/ascli +21 -1
  5. data/bin/asession +38 -34
  6. data/docs/test_env.conf +14 -3
  7. data/examples/aoc.rb +17 -15
  8. data/examples/dascli +26 -0
  9. data/examples/faspex4.rb +42 -35
  10. data/examples/proxy.pac +1 -1
  11. data/examples/transfer.rb +38 -37
  12. data/lib/aspera/aoc.rb +245 -205
  13. data/lib/aspera/ascmd.rb +111 -90
  14. data/lib/aspera/ats_api.rb +16 -14
  15. data/lib/aspera/cli/basic_auth_plugin.rb +19 -18
  16. data/lib/aspera/cli/extended_value.rb +50 -39
  17. data/lib/aspera/cli/formater.rb +161 -135
  18. data/lib/aspera/cli/info.rb +18 -0
  19. data/lib/aspera/cli/listener/line_dump.rb +4 -2
  20. data/lib/aspera/cli/listener/logger.rb +3 -1
  21. data/lib/aspera/cli/listener/progress.rb +20 -21
  22. data/lib/aspera/cli/listener/progress_multi.rb +29 -31
  23. data/lib/aspera/cli/main.rb +194 -183
  24. data/lib/aspera/cli/manager.rb +213 -206
  25. data/lib/aspera/cli/plugin.rb +71 -49
  26. data/lib/aspera/cli/plugins/alee.rb +8 -7
  27. data/lib/aspera/cli/plugins/aoc.rb +675 -558
  28. data/lib/aspera/cli/plugins/ats.rb +116 -109
  29. data/lib/aspera/cli/plugins/bss.rb +35 -34
  30. data/lib/aspera/cli/plugins/config.rb +722 -542
  31. data/lib/aspera/cli/plugins/console.rb +28 -22
  32. data/lib/aspera/cli/plugins/cos.rb +28 -37
  33. data/lib/aspera/cli/plugins/faspex.rb +281 -227
  34. data/lib/aspera/cli/plugins/faspex5.rb +129 -84
  35. data/lib/aspera/cli/plugins/node.rb +426 -232
  36. data/lib/aspera/cli/plugins/orchestrator.rb +106 -98
  37. data/lib/aspera/cli/plugins/preview.rb +196 -191
  38. data/lib/aspera/cli/plugins/server.rb +131 -126
  39. data/lib/aspera/cli/plugins/shares.rb +49 -36
  40. data/lib/aspera/cli/plugins/sync.rb +27 -28
  41. data/lib/aspera/cli/transfer_agent.rb +84 -79
  42. data/lib/aspera/cli/version.rb +3 -1
  43. data/lib/aspera/colors.rb +37 -28
  44. data/lib/aspera/command_line_builder.rb +84 -63
  45. data/lib/aspera/cos_node.rb +68 -34
  46. data/lib/aspera/data_repository.rb +4 -2
  47. data/lib/aspera/environment.rb +61 -46
  48. data/lib/aspera/fasp/agent_base.rb +36 -31
  49. data/lib/aspera/fasp/agent_connect.rb +44 -37
  50. data/lib/aspera/fasp/agent_direct.rb +101 -104
  51. data/lib/aspera/fasp/agent_httpgw.rb +91 -90
  52. data/lib/aspera/fasp/agent_node.rb +36 -33
  53. data/lib/aspera/fasp/agent_trsdk.rb +28 -31
  54. data/lib/aspera/fasp/error.rb +3 -1
  55. data/lib/aspera/fasp/error_info.rb +81 -54
  56. data/lib/aspera/fasp/installation.rb +171 -151
  57. data/lib/aspera/fasp/listener.rb +2 -0
  58. data/lib/aspera/fasp/parameters.rb +105 -111
  59. data/lib/aspera/fasp/parameters.yaml +305 -249
  60. data/lib/aspera/fasp/resume_policy.rb +20 -20
  61. data/lib/aspera/fasp/transfer_spec.rb +27 -0
  62. data/lib/aspera/fasp/uri.rb +31 -29
  63. data/lib/aspera/faspex_gw.rb +95 -118
  64. data/lib/aspera/hash_ext.rb +12 -13
  65. data/lib/aspera/id_generator.rb +11 -9
  66. data/lib/aspera/keychain/encrypted_hash.rb +73 -57
  67. data/lib/aspera/keychain/macos_security.rb +27 -29
  68. data/lib/aspera/log.rb +40 -39
  69. data/lib/aspera/nagios.rb +24 -22
  70. data/lib/aspera/node.rb +38 -30
  71. data/lib/aspera/oauth.rb +217 -248
  72. data/lib/aspera/open_application.rb +9 -7
  73. data/lib/aspera/persistency_action_once.rb +15 -14
  74. data/lib/aspera/persistency_folder.rb +15 -18
  75. data/lib/aspera/preview/file_types.rb +266 -270
  76. data/lib/aspera/preview/generator.rb +94 -92
  77. data/lib/aspera/preview/image_error.png +0 -0
  78. data/lib/aspera/preview/options.rb +20 -17
  79. data/lib/aspera/preview/utils.rb +99 -102
  80. data/lib/aspera/preview/video_error.png +0 -0
  81. data/lib/aspera/{proxy_auto_config.erb.js → proxy_auto_config.js} +23 -31
  82. data/lib/aspera/proxy_auto_config.rb +114 -21
  83. data/lib/aspera/rest.rb +144 -142
  84. data/lib/aspera/rest_call_error.rb +3 -2
  85. data/lib/aspera/rest_error_analyzer.rb +31 -31
  86. data/lib/aspera/rest_errors_aspera.rb +18 -16
  87. data/lib/aspera/secret_hider.rb +68 -0
  88. data/lib/aspera/ssh.rb +20 -16
  89. data/lib/aspera/sync.rb +57 -54
  90. data/lib/aspera/temp_file_manager.rb +20 -14
  91. data/lib/aspera/timer_limiter.rb +10 -8
  92. data/lib/aspera/uri_reader.rb +14 -15
  93. data/lib/aspera/web_auth.rb +85 -80
  94. data.tar.gz.sig +0 -0
  95. metadata +169 -40
  96. metadata.gz.sig +2 -0
  97. data/bin/dascli +0 -13
  98. data/docs/Makefile +0 -63
  99. data/docs/README.erb.md +0 -4221
  100. data/docs/README.md +0 -13
  101. data/docs/diagrams.txt +0 -49
  102. data/docs/doc_tools.rb +0 -58
  103. data/lib/aspera/cli/plugins/shares2.rb +0 -114
  104. data/lib/aspera/fasp/default.rb +0 -17
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'aspera/colors'
2
4
  require 'aspera/log'
3
5
  require 'aspera/cli/extended_value'
@@ -14,7 +16,7 @@ module Aspera
14
16
 
15
17
  class CliNoSuchId < CliError
16
18
  def initialize(res_type,res_id)
17
- msg="No such #{res_type} identifier: #{res_id}"
19
+ msg = "No such #{res_type} identifier: #{res_id}"
18
20
  super(msg)
19
21
  end
20
22
  end
@@ -24,16 +26,16 @@ module Aspera
24
26
  #attr_accessor :object
25
27
  #attr_accessor :attr_symb
26
28
  def initialize(object,attr_symb)
27
- @object=object
28
- @attr_symb=attr_symb
29
+ @object = object
30
+ @attr_symb = attr_symb
29
31
  end
30
32
 
31
33
  def value
32
- @object.send(@attr_symb.to_s)
34
+ return @object.send(@attr_symb)
33
35
  end
34
36
 
35
37
  def value=(val)
36
- @object.send(@attr_symb.to_s+'=',val)
38
+ @object.send("#{@attr_symb}=",val)
37
39
  end
38
40
  end
39
41
 
@@ -41,88 +43,93 @@ module Aspera
41
43
  # arguments options start with '-', others are commands
42
44
  # resolves on extended value syntax
43
45
  class Manager
44
- def self.time_to_string(time)
45
- time.strftime("%Y-%m-%d %H:%M:%S")
46
- end
47
-
48
46
  # boolean options are set to true/false from the following values
49
- TRUE_VALUES=[:yes,true]
50
- BOOLEAN_VALUES=TRUE_VALUES.clone.push(:no,false)
51
- BOOLEAN_SIMPLE=[:yes,:no]
47
+ TRUE_VALUES = [:yes,true].freeze
48
+ BOOLEAN_VALUES = [TRUE_VALUES,:no,false].flatten.freeze
49
+ BOOLEAN_SIMPLE = %i[yes no].freeze
52
50
  # option name separator on command line
53
- OPTION_SEP_LINE='-'
51
+ OPTION_SEP_LINE = '-'
54
52
  # option name separator in code (symbol)
55
- OPTION_SEP_NAME='_'
56
-
57
- private_constant :TRUE_VALUES,:BOOLEAN_VALUES,:BOOLEAN_SIMPLE,:OPTION_SEP_LINE,:OPTION_SEP_NAME
58
-
59
- def enum_to_bool(enum);TRUE_VALUES.include?(enum);end
60
-
61
- # find shortened string value in allowed symbol list
62
- def self.get_from_list(shortval,descr,allowed_values)
63
- # we accept shortcuts
64
- matching_exact=allowed_values.select{|i| i.to_s.eql?(shortval)}
65
- return matching_exact.first if matching_exact.length == 1
66
- matching=allowed_values.select{|i| i.to_s.start_with?(shortval)}
67
- raise cli_bad_arg("unknown value for #{descr}: #{shortval}",allowed_values) if matching.empty?
68
- raise cli_bad_arg("ambigous shortcut for #{descr}: #{shortval}",matching) unless matching.length.eql?(1)
69
- return enum_to_bool(matching.first) if allowed_values.eql?(BOOLEAN_VALUES)
70
- return matching.first
71
- end
53
+ OPTION_SEP_NAME = '_'
54
+
55
+ private_constant :TRUE_VALUES,:BOOLEAN_VALUES,:OPTION_SEP_LINE,:OPTION_SEP_NAME
56
+
57
+ class << self
58
+ def enum_to_bool(enum)
59
+ raise "Value not valid for boolean: #{enum}/#{enum.class}" unless BOOLEAN_VALUES.include?(enum)
60
+ return TRUE_VALUES.include?(enum)
61
+ end
72
62
 
73
- def self.cli_bad_arg(error_msg,choices)
74
- return CliBadArgument.new(error_msg+"\nUse:\n"+choices.map{|c| "- #{c.to_s}\n"}.sort.join(''))
63
+ def time_to_string(time)
64
+ return time.strftime('%Y-%m-%d %H:%M:%S')
65
+ end
66
+
67
+ # find shortened string value in allowed symbol list
68
+ def get_from_list(shortval,descr,allowed_values)
69
+ # we accept shortcuts
70
+ matching_exact = allowed_values.select{|i| i.to_s.eql?(shortval)}
71
+ return matching_exact.first if matching_exact.length == 1
72
+ matching = allowed_values.select{|i| i.to_s.start_with?(shortval)}
73
+ raise CliBadArgument,bad_arg_message_multi("unknown value for #{descr}: #{shortval}",allowed_values) if matching.empty?
74
+ raise CliBadArgument,bad_arg_message_multi("ambigous shortcut for #{descr}: #{shortval}",matching) unless matching.length.eql?(1)
75
+ return enum_to_bool(matching.first) if allowed_values.eql?(BOOLEAN_VALUES)
76
+ return matching.first
77
+ end
78
+
79
+ def bad_arg_message_multi(error_msg,choices)
80
+ return [error_msg,'Use:',choices.map{|c|"- #{c}"}.sort].flatten.join("\n")
81
+ end
75
82
  end
76
83
 
77
84
  attr_reader :parser
78
- attr_accessor :ask_missing_mandatory
79
- attr_accessor :ask_missing_optional
85
+ attr_accessor :ask_missing_mandatory, :ask_missing_optional
86
+ attr_writer :fail_on_missing_mandatory
80
87
 
81
- #
82
- def initialize(program_name,argv=nil)
88
+ def initialize(program_name,argv: nil)
83
89
  # command line values not starting with '-'
84
- @unprocessed_cmd_line_arguments=[]
90
+ @unprocessed_cmd_line_arguments = []
85
91
  # command line values starting with '-'
86
- @unprocessed_cmd_line_options=[]
92
+ @unprocessed_cmd_line_options = []
87
93
  # a copy of all initial options
88
- @initial_cli_options=[]
94
+ @initial_cli_options = []
89
95
  # option description: key = option symbol, value=hash, :type, :accessor, :value, :accepted
90
- @declared_options={}
96
+ @declared_options = {}
91
97
  # do we ask missing options and arguments to user ?
92
- @ask_missing_mandatory=false # STDIN.isatty
98
+ @ask_missing_mandatory = false # STDIN.isatty
93
99
  # ask optional options if not provided and in interactive
94
- @ask_missing_optional=false
100
+ @ask_missing_optional = false
101
+ @fail_on_missing_mandatory = true
95
102
  # those must be set before parse, parse consumes those defined only
96
- @unprocessed_defaults=[]
97
- @unprocessed_env=[]
103
+ @unprocessed_defaults = []
104
+ @unprocessed_env = []
98
105
  # Note: was initially inherited but it is prefered to have specific methods
99
- @parser=OptionParser.new
100
- @parser.program_name=program_name
106
+ @parser = OptionParser.new
107
+ @parser.program_name = program_name
101
108
  # options can also be provided by env vars : --param-name -> ASLMCLI_PARAM_NAME
102
- env_prefix=program_name.upcase+OPTION_SEP_NAME
109
+ env_prefix = program_name.upcase + OPTION_SEP_NAME
103
110
  ENV.each do |k,v|
104
111
  if k.start_with?(env_prefix)
105
112
  @unprocessed_env.push([k[env_prefix.length..-1].downcase.to_sym,v])
106
113
  end
107
114
  end
108
115
  Log.log.debug("env=#{@unprocessed_env}".red)
109
- @unprocessed_cmd_line_options=[]
110
- @unprocessed_cmd_line_arguments=[]
116
+ @unprocessed_cmd_line_options = []
117
+ @unprocessed_cmd_line_arguments = []
111
118
  # argv is nil when help is generated for every plugin
112
119
  unless argv.nil?
113
120
  @parser.separator('')
114
121
  @parser.separator('OPTIONS: global')
115
- self.set_obj_attr(:interactive,self,:ask_missing_mandatory)
116
- self.set_obj_attr(:ask_options,self,:ask_missing_optional)
117
- self.add_opt_boolean(:interactive,'use interactive input of missing params')
118
- self.add_opt_boolean(:ask_options,'ask even optional options')
119
- self.parse_options!
120
- process_options=true
122
+ set_obj_attr(:interactive,self,:ask_missing_mandatory)
123
+ set_obj_attr(:ask_options,self,:ask_missing_optional)
124
+ add_opt_boolean(:interactive,'use interactive input of missing params')
125
+ add_opt_boolean(:ask_options,'ask even optional options')
126
+ parse_options!
127
+ process_options = true
121
128
  while !argv.empty?
122
- value=argv.shift
123
- if process_options and value.start_with?('-')
129
+ value = argv.shift
130
+ if process_options && value.start_with?('-')
124
131
  if value.eql?('--')
125
- process_options=false
132
+ process_options = false
126
133
  else
127
134
  @unprocessed_cmd_line_options.push(value)
128
135
  end
@@ -131,76 +138,45 @@ module Aspera
131
138
  end
132
139
  end
133
140
  end
134
- @initial_cli_options=@unprocessed_cmd_line_options.dup
141
+ @initial_cli_options = @unprocessed_cmd_line_options.dup
135
142
  Log.log.debug("add_cmd_line_options:commands/args=#{@unprocessed_cmd_line_arguments},options=#{@unprocessed_cmd_line_options}".red)
136
143
  end
137
144
 
138
- def prompt_user_input(prompt,sensitive)
139
- return STDIN.getpass("#{prompt}> ") if sensitive
140
- print "#{prompt}> "
141
- return STDIN.gets.chomp
142
- end
143
-
144
- def get_interactive(type,descr,expected=:single)
145
- if !@ask_missing_mandatory
146
- if expected.is_a?(Array)
147
- raise self.class.cli_bad_arg("missing: #{descr}",expected)
148
- end
149
- raise CliBadArgument,"missing argument (#{expected}): #{descr}"
150
- end
151
- result=nil
152
- # Note: mandatory parenthesis here !
153
- sensitive = (type.eql?(:option) and @declared_options[descr.to_sym][:sensitive].eql?(true))
154
- default_prompt="#{type}: #{descr}"
155
- # ask interactively
156
- case expected
157
- when :multiple
158
- result=[]
159
- puts " (one per line, end with empty line)"
160
- loop do
161
- entry=prompt_user_input(default_prompt,sensitive)
162
- break if entry.empty?
163
- result.push(ExtendedValue.instance.evaluate(entry))
164
- end
165
- when :single
166
- result=ExtendedValue.instance.evaluate(prompt_user_input(default_prompt,sensitive))
167
- else # one fixed
168
- result=self.class.get_from_list(prompt_user_input("#{expected.join(' ')}\n#{default_prompt}",sensitive),descr,expected)
169
- end
170
- return result
171
- end
172
-
173
- def get_next_command(command_list); return get_next_argument('command',command_list); end
145
+ def get_next_command(command_list); return get_next_argument('command',expected: command_list); end
174
146
 
175
147
  # @param expected is
176
148
  # - Array of allowed value (single value)
177
149
  # - :multiple for remaining values
178
150
  # - :single for a single unconstrained value
179
- # @param is_type : :mandatory or :optional
151
+ # @param mandatory true/false
152
+ # @param type expected class for result
180
153
  # @return value, list or nil
181
- def get_next_argument(descr,expected=:single,is_type=:mandatory)
182
- result=nil
154
+ def get_next_argument(descr,expected: :single,mandatory: true, type: nil)
155
+ unless type.nil?
156
+ raise 'internal: type must be a Class' unless type.is_a?(Class)
157
+ descr="#{descr} (#{type})"
158
+ end
159
+ result = nil
183
160
  if !@unprocessed_cmd_line_arguments.empty?
184
161
  # there are values
185
162
  case expected
186
163
  when :single
187
- result=ExtendedValue.instance.evaluate(@unprocessed_cmd_line_arguments.shift)
164
+ result = ExtendedValue.instance.evaluate(@unprocessed_cmd_line_arguments.shift)
188
165
  when :multiple
189
166
  result = @unprocessed_cmd_line_arguments.shift(@unprocessed_cmd_line_arguments.length).map{|v|ExtendedValue.instance.evaluate(v)}
190
167
  # if expecting list and only one arg of type array : it is the list
191
- if result.length.eql?(1) and result.first.is_a?(Array)
192
- result=result.first
168
+ if result.length.eql?(1) && result.first.is_a?(Array)
169
+ result = result.first
193
170
  end
194
171
  else
195
- result=self.class.get_from_list(@unprocessed_cmd_line_arguments.shift,descr,expected)
172
+ result = self.class.get_from_list(@unprocessed_cmd_line_arguments.shift,descr,expected)
196
173
  end
197
- else
174
+ elsif mandatory
198
175
  # no value provided
199
- if is_type.eql?(:mandatory)
200
- result=get_interactive(:argument,descr,expected)
201
- end
176
+ result = get_interactive(:argument,descr,expected: expected)
202
177
  end
203
178
  Log.log.debug("#{descr}=#{result}")
179
+ raise "argument shall be #{type.name}" unless type.nil? || result.is_a?(type)
204
180
  return result
205
181
  end
206
182
 
@@ -208,147 +184,132 @@ module Aspera
208
184
  def declare_option(option_symbol,type)
209
185
  Log.log.debug("declare_option: #{option_symbol}: #{type}: skip=#{@declared_options.has_key?(option_symbol)}".green)
210
186
  if @declared_options.has_key?(option_symbol)
211
- raise "INTERNAL ERROR: option #{option_symbol} already declared. only accessor can be redeclared and ignored" unless @declared_options[option_symbol][:type].eql?(:accessor)
187
+ raise "INTERNAL ERROR: option #{option_symbol} already declared. only accessor can be redeclared and ignored" \
188
+ unless @declared_options[option_symbol][:type].eql?(:accessor)
212
189
  return
213
190
  end
214
- @declared_options[option_symbol]={:type=>type}
191
+ @declared_options[option_symbol] = {type: type}
215
192
  # by default passwords and secrets are sensitive, else specify when declaring the option
216
- set_is_sensitive(option_symbol) if !%w{password secret key}.select{|i| option_symbol.to_s.end_with?(i)}.empty?
217
- end
218
-
219
- def set_is_sensitive(option_symbol)
220
- @declared_options[option_symbol][:sensitive]=true
193
+ @declared_options[option_symbol][:sensitive] = true if !%w[password secret key].select{|i| option_symbol.to_s.end_with?(i)}.empty?
221
194
  end
222
195
 
223
196
  # define option with handler
224
197
  def set_obj_attr(option_symbol,object,attr_symb,default_value=nil)
225
198
  Log.log.debug("set attr obj #{option_symbol} (#{object},#{attr_symb})")
226
199
  declare_option(option_symbol,:accessor)
227
- @declared_options[option_symbol][:accessor]=AttrAccessor.new(object,attr_symb)
228
- set_option(option_symbol,default_value,"default obj attr") if !default_value.nil?
200
+ @declared_options[option_symbol][:accessor] = AttrAccessor.new(object,attr_symb)
201
+ set_option(option_symbol,default_value,'default obj attr') if !default_value.nil?
229
202
  end
230
203
 
231
204
  # set an option value by name, either store value or call handler
232
- def set_option(option_symbol,value,where="default")
233
- if ! @declared_options.has_key?(option_symbol)
205
+ def set_option(option_symbol,value,where='default')
206
+ if !@declared_options.has_key?(option_symbol)
234
207
  Log.log.debug("set unknown option: #{option_symbol}")
235
- raise "ERROR"
208
+ raise 'ERROR: cannot set undeclared option'
236
209
  #declare_option(option_symbol)
237
210
  end
238
- value=ExtendedValue.instance.evaluate(value)
239
- Log.log.debug("set_option(#{where}) #{option_symbol}=#{value}")
240
- if @declared_options[option_symbol][:values].eql?(BOOLEAN_VALUES)
241
- value=enum_to_bool(value)
242
- end
243
- Log.log.debug("set #{option_symbol}=#{value} (#{@declared_options[option_symbol][:type]}) : #{where}".blue)
211
+ value = ExtendedValue.instance.evaluate(value)
212
+ value = Manager.enum_to_bool(value) if @declared_options[option_symbol][:values].eql?(BOOLEAN_VALUES)
213
+ Log.log.debug("set #{option_symbol}=#{value} (#{@declared_options[option_symbol][:type]}) : #{where}")
244
214
  case @declared_options[option_symbol][:type]
245
215
  when :accessor
246
- @declared_options[option_symbol][:accessor].value=value
216
+ @declared_options[option_symbol][:accessor].value = value
247
217
  when :value
248
- @declared_options[option_symbol][:value]=value
218
+ @declared_options[option_symbol][:value] = value
249
219
  else # nil or other
250
- raise "error"
220
+ raise 'error'
251
221
  end
252
222
  end
253
223
 
254
224
  # get an option value by name
255
225
  # either return value or call handler, can return nil
256
226
  # ask interactively if requested/required
257
- def get_option(option_symbol,is_type=:optional)
258
- result=nil
227
+ def get_option(option_symbol,is_type: :optional)
228
+ result = nil
259
229
  if @declared_options.has_key?(option_symbol)
260
230
  case @declared_options[option_symbol][:type]
261
231
  when :accessor
262
- result=@declared_options[option_symbol][:accessor].value
232
+ result = @declared_options[option_symbol][:accessor].value
263
233
  when :value
264
- result=@declared_options[option_symbol][:value]
234
+ result = @declared_options[option_symbol][:value]
265
235
  else
266
- raise "unknown type"
236
+ raise 'unknown type'
267
237
  end
268
238
  Log.log.debug("get #{option_symbol} (#{@declared_options[option_symbol][:type]}) : #{result}")
269
239
  end
270
- Log.log.debug("interactive=#{@ask_missing_mandatory}")
240
+ # do not fail for manual generation if option mandatory but not set
241
+ result = '' if result.nil? && !@fail_on_missing_mandatory
242
+ #Log.log.debug("interactive=#{@ask_missing_mandatory}")
271
243
  if result.nil?
272
244
  if !@ask_missing_mandatory
273
- if is_type.eql?(:mandatory)
274
- raise CliBadArgument,"Missing mandatory option: #{option_symbol}"
275
- end
276
- else # ask_missing_mandatory
277
- if @ask_missing_optional or is_type.eql?(:mandatory)
278
- expected=:single
279
- #print "please enter: #{option_symbol.to_s}"
280
- if @declared_options.has_key?(option_symbol) and @declared_options[option_symbol].has_key?(:values)
281
- expected=@declared_options[option_symbol][:values]
282
- end
283
- result=get_interactive(:option,option_symbol.to_s,expected)
284
- set_option(option_symbol,result,"interactive")
245
+ raise CliBadArgument,"Missing mandatory option: #{option_symbol}" if is_type.eql?(:mandatory)
246
+ elsif @ask_missing_optional || is_type.eql?(:mandatory)
247
+ # ask_missing_mandatory
248
+ expected = :single
249
+ #print "please enter: #{option_symbol.to_s}"
250
+ if @declared_options.has_key?(option_symbol) && @declared_options[option_symbol].has_key?(:values)
251
+ expected = @declared_options[option_symbol][:values]
285
252
  end
253
+ result = get_interactive(:option,option_symbol.to_s,expected: expected)
254
+ set_option(option_symbol,result,'interactive')
286
255
  end
287
256
  end
288
257
  return result
289
258
  end
290
259
 
291
260
  # param must be hash
292
- def add_option_preset(preset_hash,op=:push)
261
+ def add_option_preset(preset_hash,op: :push)
293
262
  Log.log.debug("add_option_preset=#{preset_hash}")
294
263
  raise "internal error: setting default with no hash: #{preset_hash.class}" if !preset_hash.is_a?(Hash)
295
264
  # incremental override
296
265
  preset_hash.each{|k,v|@unprocessed_defaults.send(op,[k.to_sym,v])}
297
266
  end
298
267
 
299
- # generate command line option from option symbol
300
- def symbol_to_option(symbol,opt_val)
301
- result='--'+symbol.to_s.gsub(OPTION_SEP_NAME,OPTION_SEP_LINE)
302
- result=result+'='+opt_val unless opt_val.nil?
303
- return result
304
- end
305
-
306
- def highlight_current(value)
307
- STDOUT.isatty ? value.to_s.red.bold : "[#{value}]"
308
- end
309
-
310
268
  # define an option with restricted values
311
269
  def add_opt_list(option_symbol,values,help,*on_args)
312
270
  declare_option(option_symbol,:value)
313
271
  Log.log.debug("add_opt_list #{option_symbol}")
314
272
  on_args.unshift(symbol_to_option(option_symbol,'ENUM'))
315
273
  # this option value must be a symbol
316
- @declared_options[option_symbol][:values]=values
317
- value=get_option(option_symbol)
318
- help_values=values.map{|i|i.eql?(value)?highlight_current(i):i}.join(', ')
274
+ @declared_options[option_symbol][:values] = values
275
+ value = get_option(option_symbol)
276
+ help_values = values.map{|i|i.eql?(value) ? highlight_current(i) : i}.join(', ')
319
277
  if values.eql?(BOOLEAN_VALUES)
320
- help_values=BOOLEAN_SIMPLE.map{|i|((i.eql?(:yes) and value) or (i.eql?(:no) and not value))?highlight_current(i):i}.join(', ')
278
+ help_values = BOOLEAN_SIMPLE.map{|i|(i.eql?(:yes) && value) || (i.eql?(:no) && !value) ? highlight_current(i) : i}.join(', ')
321
279
  end
322
280
  on_args.push(values)
323
281
  on_args.push("#{help}: #{help_values}")
324
282
  Log.log.debug("on_args=#{on_args}")
325
- @parser.on(*on_args){|v|set_option(option_symbol,self.class.get_from_list(v.to_s,help,values),"cmdline")}
283
+ @parser.on(*on_args){|v|set_option(option_symbol,self.class.get_from_list(v.to_s,help,values),'cmdline')}
326
284
  end
327
285
 
328
286
  def add_opt_boolean(option_symbol,help,*on_args)
329
287
  add_opt_list(option_symbol,BOOLEAN_VALUES,help,*on_args)
288
+ # if default was defined for obj, it may still be enum (yes/no) instead of boolean
289
+ default_value=get_option(option_symbol)
290
+ set_option(option_symbol,default_value,'opt boolean') unless default_value.nil?
330
291
  end
331
292
 
332
293
  # define an option with open values
333
294
  def add_opt_simple(option_symbol,*on_args)
334
295
  declare_option(option_symbol,:value)
335
296
  Log.log.debug("add_opt_simple #{option_symbol}")
336
- on_args.unshift(symbol_to_option(option_symbol,"VALUE"))
297
+ on_args.unshift(symbol_to_option(option_symbol,'VALUE'))
337
298
  Log.log.debug("on_args=#{on_args}")
338
- @parser.on(*on_args) { |v| set_option(option_symbol,v,"cmdline") }
299
+ @parser.on(*on_args) { |v| set_option(option_symbol,v,'cmdline') }
339
300
  end
340
301
 
341
302
  # define an option with date format
342
303
  def add_opt_date(option_symbol,*on_args)
343
304
  declare_option(option_symbol,:value)
344
305
  Log.log.debug("add_opt_date #{option_symbol}")
345
- on_args.unshift(symbol_to_option(option_symbol,"DATE"))
306
+ on_args.unshift(symbol_to_option(option_symbol,'DATE'))
346
307
  Log.log.debug("on_args=#{on_args}")
347
308
  @parser.on(*on_args) do |v|
348
309
  case v
349
- when 'now'; set_option(option_symbol,Manager.time_to_string(Time.now),"cmdline")
350
- when /^-([0-9]+)h/; set_option(option_symbol,Manager.time_to_string(Time.now-$1.to_i*3600),"cmdline")
351
- else set_option(option_symbol,v,"cmdline")
310
+ when 'now' then set_option(option_symbol,Manager.time_to_string(Time.now),'cmdline')
311
+ when /^-([0-9]+)h/ then set_option(option_symbol,Manager.time_to_string(Time.now - (3600 * Regexp.last_match(1).to_i)),'cmdline')
312
+ else set_option(option_symbol,v,'cmdline')
352
313
  end
353
314
  end
354
315
  end
@@ -368,26 +329,26 @@ module Aspera
368
329
 
369
330
  # unprocessed options or arguments ?
370
331
  def final_errors
371
- result=[]
332
+ result = []
372
333
  result.push("unprocessed options: #{@unprocessed_cmd_line_options}") unless @unprocessed_cmd_line_options.empty?
373
334
  result.push("unprocessed values: #{@unprocessed_cmd_line_arguments}") unless @unprocessed_cmd_line_arguments.empty?
374
335
  return result
375
336
  end
376
337
 
377
338
  # get all original options on command line used to generate a config in config file
378
- def get_options_table(remove_from_remaining=true)
379
- result={}
339
+ def get_options_table(remove_from_remaining: true)
340
+ result = {}
380
341
  @initial_cli_options.each do |optionval|
381
342
  case optionval
382
343
  when /^--([^=]+)$/
383
344
  # ignore
384
345
  when /^--([^=]+)=(.*)$/
385
- name=$1
386
- value=$2
346
+ name = Regexp.last_match(1)
347
+ value = Regexp.last_match(2)
387
348
  name.gsub!(OPTION_SEP_LINE,OPTION_SEP_NAME)
388
- value=ExtendedValue.instance.evaluate(value)
349
+ value = ExtendedValue.instance.evaluate(value)
389
350
  Log.log.debug("option #{name}=#{value}")
390
- result[name]=value
351
+ result[name] = value
391
352
  @unprocessed_cmd_line_options.delete(optionval) if remove_from_remaining
392
353
  else
393
354
  raise CliBadArgument,"wrong option format: #{optionval}"
@@ -397,46 +358,28 @@ module Aspera
397
358
  end
398
359
 
399
360
  # return options as taken from config file and command line just before command execution
400
- def declared_options(all=true)
401
- return @declared_options.keys.inject({}) do |h,option_symb|
402
- v=get_option(option_symb)
403
- h[option_symb.to_s]=v if all or !v.nil?
404
- h
405
- end
406
- end
407
-
408
- def apply_options_preset(preset,where,force=false)
409
- unprocessed=[]
410
- preset.each do |pair|
411
- k,v=*pair
412
- if @declared_options.has_key?(k)
413
- # constrained parameters as string are revert to symbol
414
- if @declared_options[k].has_key?(:values) and v.is_a?(String)
415
- v=self.class.get_from_list(v,k.to_s+" in #{where}",@declared_options[k][:values])
416
- end
417
- set_option(k,v,where)
418
- else
419
- unprocessed.push(pair)
420
- end
361
+ def declared_options(only_defined: false)
362
+ result = {}
363
+ @declared_options.keys.each do |option_symb|
364
+ v = get_option(option_symb)
365
+ result[option_symb.to_s] = v unless only_defined && v.nil?
421
366
  end
422
- # keep only unprocessed values for next parse
423
- preset.clear
424
- preset.push(*unprocessed)
367
+ return result
425
368
  end
426
369
 
427
370
  # removes already known options from the list
428
371
  def parse_options!
429
- Log.log.debug("parse_options!".red)
372
+ Log.log.debug('parse_options!'.red)
430
373
  # first conf file, then env var
431
- apply_options_preset(@unprocessed_defaults,"file")
432
- apply_options_preset(@unprocessed_env,"env")
374
+ apply_options_preset(@unprocessed_defaults,'file')
375
+ apply_options_preset(@unprocessed_env,'env')
433
376
  # command line override
434
- unknown_options=[]
377
+ unknown_options = []
435
378
  begin
436
379
  # remove known options one by one, exception if unknown
437
- Log.log.debug("before parse".red)
380
+ Log.log.debug('before parse'.red)
438
381
  @parser.parse!(@unprocessed_cmd_line_options)
439
- Log.log.debug("After parse".red)
382
+ Log.log.debug('After parse'.red)
440
383
  rescue OptionParser::InvalidOption => e
441
384
  Log.log.debug("InvalidOption #{e}".red)
442
385
  # save for later processing
@@ -445,7 +388,71 @@ module Aspera
445
388
  end
446
389
  Log.log.debug("remains: #{unknown_options}")
447
390
  # set unprocessed options for next time
448
- @unprocessed_cmd_line_options=unknown_options
391
+ @unprocessed_cmd_line_options = unknown_options
392
+ end
393
+
394
+ private
395
+
396
+ def prompt_user_input(prompt,sensitive)
397
+ return $stdin.getpass("#{prompt}> ") if sensitive
398
+ print("#{prompt}> ")
399
+ return $stdin.gets.chomp
400
+ end
401
+
402
+ def get_interactive(type,descr,expected: :single)
403
+ if !@ask_missing_mandatory
404
+ raise CliBadArgument,self.class.bad_arg_message_multi("missing: #{descr}",expected) if expected.is_a?(Array)
405
+ raise CliBadArgument,"missing argument (#{expected}): #{descr}"
406
+ end
407
+ result = nil
408
+ sensitive = type.eql?(:option) && @declared_options[descr.to_sym][:sensitive]
409
+ default_prompt = "#{type}: #{descr}"
410
+ # ask interactively
411
+ case expected
412
+ when :multiple
413
+ result = []
414
+ puts(' (one per line, end with empty line)')
415
+ loop do
416
+ entry = prompt_user_input(default_prompt,sensitive)
417
+ break if entry.empty?
418
+ result.push(ExtendedValue.instance.evaluate(entry))
419
+ end
420
+ when :single
421
+ result = ExtendedValue.instance.evaluate(prompt_user_input(default_prompt,sensitive))
422
+ else # one fixed
423
+ result = self.class.get_from_list(prompt_user_input("#{expected.join(' ')}\n#{default_prompt}",sensitive),descr,expected)
424
+ end
425
+ return result
426
+ end
427
+
428
+ # generate command line option from option symbol
429
+ def symbol_to_option(symbol,opt_val)
430
+ result = '--' + symbol.to_s.gsub(OPTION_SEP_NAME,OPTION_SEP_LINE)
431
+ result = result + '=' + opt_val unless opt_val.nil?
432
+ return result
433
+ end
434
+
435
+ def highlight_current(value)
436
+ $stdout.isatty ? value.to_s.red.bold : "[#{value}]"
437
+ end
438
+
439
+ def apply_options_preset(preset,where)
440
+ unprocessed = []
441
+ preset.each do |pair|
442
+ k,v = *pair
443
+ if @declared_options.has_key?(k)
444
+ # constrained parameters as string are revert to symbol
445
+ if @declared_options[k].has_key?(:values) && v.is_a?(String)
446
+ v = self.class.get_from_list(v,k.to_s + " in #{where}",@declared_options[k][:values])
447
+ end
448
+ set_option(k,v,where)
449
+ else
450
+ unprocessed.push(pair)
451
+ end
452
+ end
453
+ # keep only unprocessed values for next parse
454
+ preset.clear
455
+ preset.push(*unprocessed)
449
456
  end
450
457
  end
451
458
  end