Class: Aspera::Cli::OptionValue

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

Overview

Description of option, how to manage

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(option:, description: nil, allowed: Type::STRING, handler: nil, deprecation: nil, schema: nil) ⇒ OptionValue

allowed:

  • nil No validation, so just a string
  • Class The single allowed Class
  • Array<Class> Multiple allowed classes
  • Array<Symbol> List of allowed values

Parameters:

  • option (Symbol)

    Name of option

  • description (String, nil) (defaults to: nil)

    Description for help; if nil, derived from schema

  • allowed (nil, Class, Array<Class>, Array<Symbol>) (defaults to: Type::STRING)

    Allowed values

  • handler (Hash, nil) (defaults to: nil)

    Accessor: keys: :o(object) and :m(method); nil for local storage

  • deprecation (String) (defaults to: nil)

    Deprecation message

  • schema (String) (defaults to: nil)

    Declaration of schema



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/aspera/cli/parser.rb', line 106

def initialize(option:, description: nil, allowed: Type::STRING, handler: nil, deprecation: nil, schema: nil)
  Log.log.trace1 { "option: #{option}, allowed: #{allowed}" }
  @option = option
  @description = description
  @group = nil
  @block = nil
  # by default passwords and secrets are sensitive, else specify when declaring the option
  @sensitive = SecretHider.instance.secret?(@option, '')
  @deprecation = deprecation
  @schema = schema
  # Start with local storage; bind_handler wires the delegation if a handler is given.
  @object = nil
  @read_method = nil
  @write_method = nil
  @access = :local
  bind_handler(handler) unless handler.nil?
  @types = nil
  @values = nil
  allowed = infer_allowed_from_schema(schema, allowed) if schema
  apply_allowed(allowed) unless allowed.nil?
  Log.log.trace1 { "declare: #{@option}: #{@access} #{@object.class}.#{@read_method}".green }
end

Instance Attribute Details

#blockObject

[Proc, nil] Block to call for flag options (TYPES_NONE)



93
94
95
# File 'lib/aspera/cli/parser.rb', line 93

def block
  @block
end

#deprecationObject (readonly)

[Array(Class)] List of allowed types



87
88
89
# File 'lib/aspera/cli/parser.rb', line 87

def deprecation
  @deprecation
end

#groupObject

[String] Help section group name (set by Parser#group)



91
92
93
# File 'lib/aspera/cli/parser.rb', line 91

def group
  @group
end

#optionObject (readonly)

[Array(Class)] List of allowed types



87
88
89
# File 'lib/aspera/cli/parser.rb', line 87

def option
  @option
end

#schemaObject (readonly)

[Array(Class)] List of allowed types



87
88
89
# File 'lib/aspera/cli/parser.rb', line 87

def schema
  @schema
end

#sensitiveObject (readonly)

[Array(Class)] List of allowed types



87
88
89
# File 'lib/aspera/cli/parser.rb', line 87

def sensitive
  @sensitive
end

#typesObject (readonly)

[Array(Class)] List of allowed types



87
88
89
# File 'lib/aspera/cli/parser.rb', line 87

def types
  @types
end

#valuesObject

[Array] List of allowed values (Symbols and specific values)



89
90
91
# File 'lib/aspera/cli/parser.rb', line 89

def values
  @values
end

Instance Method Details

#assign_value(value, where:, warn_deprecation: true) ⇒ nil

Assign value to option. Value can be a String, then evaluated with ExtendedValue, or directly a value.

Parameters:

  • value (String, Object)

    Value to assign to option

  • where (String)

    Where the value is assigned from

  • warn_deprecation (Boolean) (defaults to: true)

    Emit deprecation warning (false for internal transfers)

Returns:

  • (nil)


254
255
256
257
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
291
292
293
294
295
296
# File 'lib/aspera/cli/parser.rb', line 254

def assign_value(value, where:, warn_deprecation: true)
  Aspera.assert(!@deprecation, type: :warn) { "Option #{@option} is deprecated: #{@deprecation}" } if warn_deprecation
  new_value = ExtendedValue.instance.evaluate(value, context: "option: #{@option}", allowed: @types)
  Log.log.trace1 { "#{where}: #{@option} <- (#{new_value.class})#{new_value}" }
  # Per-type coercion: String input from CLI/env/preset is normalized to the expected type.
  # Centralized here so all sources (CLI dispatch, preset, env) go through the same path.
  case @types
  when Type::ENUM
    new_value = Parser.get_from_list(new_value, @option, @values) if new_value.is_a?(String)
  when Type::BOOLEAN
    new_value = Parser.get_from_list(new_value, @option, BoolValue::ALL) if new_value.is_a?(String)
    new_value = BoolValue.true?(new_value)
  when Type::INTEGER
    new_value = Integer(new_value)
  when Type::STRING_ARRAY
    new_value = [new_value] if new_value.is_a?(String)
  when Type::SYMBOL_ARRAY
    new_value = [new_value] if new_value.is_a?(String)
    Aspera.assert_array_all(new_value, String, type: BadArgument)
    new_value = new_value.map { |v| Parser.get_from_list(v, @option, @values) }
  else
    # nil (setting nil on a Hash/Array option resets to empty container)
    new_value = {} if new_value.nil? && @types&.first.eql?(Hash)
    new_value = [] if new_value.nil? && @types&.first.eql?(Array)
  end
  # Skip type validation for the special 'help' value on Hash options: store it as-is
  # so that get_option(schema:) can raise SchemaRequest with the contextual schema later.
  # Note: set_option already raises SchemaRequest when @schema is set, so this path is
  # only reached when @schema is nil (e.g. --query=help before schema is known).
  if new_value.eql?(Parser::HELP) && @types&.include?(Hash)
    store(new_value)
    return
  end
  Aspera.assert_type(new_value, *@types, type: BadArgument) { "Option #{@option}" } if @types
  if new_value.is_a?(Hash) || new_value.is_a?(Array)
    current_value = value(log: false)
    new_value = current_value.deep_merge(new_value) if new_value.is_a?(Hash) && current_value.is_a?(Hash) && !current_value.empty?
    new_value = current_value + new_value if new_value.is_a?(Array) && current_value.is_a?(Array) && !current_value.empty?
  end
  store(new_value)
  Log.log.trace1 { v = value(log: false); "#{@option} <- (#{v.class})#{v}" } # rubocop:disable Style/Semicolon
  nil
end

#bind_handler(handler) ⇒ nil

Wire (or re-wire) the getter/setter delegation for this option. Safe to call after construction - used by Parser#set_handler to bind a composed instance variable that did not exist at class-load time (Category C handlers).

Parameters:

  • handler (Hash)

    Accessor hash with keys :o (object) and :m (method symbol)

Returns:

  • (nil)


199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/aspera/cli/parser.rb', line 199

def bind_handler(handler)
  Aspera.assert_type(handler, Hash) { 'handler' }
  # Capture any value already stored locally before switching to delegated storage.
  # This transfers defaults (and any preset values already applied) to the new target.
  pending_value = @access.eql?(:local) ? @object : nil
  @object       = handler[:o]
  @read_method  = handler[:m]
  @write_method = "#{@read_method}=".to_sym
  @access = if @object.respond_to?(@write_method)
    :attr   # delegation via attr_accessor-style m / m=
  else
    :custom # delegation via generic 3-arg method m(sym, :get/:set, val)
  end
  Aspera.assert(@object.respond_to?(@read_method)) { "#{@object} does not respond to #{@read_method}" }
  Log.log.trace1 { "bind_handler: #{@option}: #{@access} #{@object.class}.#{@read_method}".green }
  # Push the pending local value to the new target if one was stored
  assign_value(pending_value, where: 'bind_handler', warn_deprecation: false) unless pending_value.nil?
  nil
end

#clearnil

Reset stored value to nil

Returns:

  • (nil)


230
231
232
# File 'lib/aspera/cli/parser.rb', line 230

def clear
  @object = nil
end

#descriptionString

Returns description of the option: explicit one, or first line of schema description.

Returns:

  • (String)

    description of the option: explicit one, or first line of schema description



220
221
222
223
224
225
226
# File 'lib/aspera/cli/parser.rb', line 220

def description
  return @description unless @description.nil?
  return if @schema.nil?
  schema_node = Schema::Registry.instance.reader(@schema).current
  first_line = (schema_node['title'] || schema_node['description'].to_s).lines.first.to_s.strip
  first_line.end_with?('.') ? first_line[0..-2] : first_line
end

#value(log: true) ⇒ Object

Get current option value

Parameters:

  • log (Boolean) (defaults to: true)

    whether to log the value retrieval

Returns:

  • (Object)

    current value



237
238
239
240
241
242
243
244
245
246
# File 'lib/aspera/cli/parser.rb', line 237

def value(log: true)
  current_value =
    case @access
    when :local  then @object
    when :attr   then @object.send(@read_method)
    when :custom then @object.send(@read_method, @option, :get)
    end
  Log.log.trace1 { "#{@option} -> (#{current_value.class})#{current_value}" } if log
  current_value
end