class Mongo::URI::OptionsMapper

Performs mapping between URI options and Ruby options.

This class contains:

URI option names are case insensitive. Ruby options are specified as symbols (though in Client options use indifferent access).

@api private

Constants

URI_OPTION_CANONICAL_NAMES

@return [ Hash<String, String> ] Map from lowercased to canonical URI

option names.
URI_OPTION_MAP

Hash for storing map of URI option parameters to conversion strategies

Attributes

options[R]

@return [ Hash ] The options.

Public Class Methods

new(**opts) click to toggle source

Instantates the options mapper.

@option opts [ Logger ] :logger A custom logger to use.

# File lib/mongo/uri/options_mapper.rb, line 46
def initialize(**opts)
  @options = opts
end

Private Class Methods

uri_option(uri_key, name, **extra) click to toggle source

Simple internal dsl to register a MongoDB URI option in the URI_OPTION_MAP.

@param [ String ] uri_key The MongoDB URI option to register. @param [ Symbol ] name The name of the option in the driver. @param [ Hash ] extra Extra options.

* :group [ Symbol ] Nested hash where option will go.
* :type [ Symbol ] Name of function to transform value.
# File lib/mongo/uri/options_mapper.rb, line 260
def self.uri_option(uri_key, name, **extra)
  URI_OPTION_MAP[uri_key.downcase] = { name: name }.update(extra)
  URI_OPTION_CANONICAL_NAMES[uri_key.downcase] = uri_key
end

Public Instance Methods

add_uri_option(key, value, uri_options) click to toggle source

Adds an option to the uri options hash.

Acquires a target for the option based on group.
Transforms the value.
Merges the option into the target.

@param [ String ] key URI option name. @param [ String ] value The value of the option. @param [ Hash ] uri_options The base option target.

# File lib/mongo/uri/options_mapper.rb, line 62
def add_uri_option(key, value, uri_options)
  strategy = URI_OPTION_MAP[key.downcase]
  if strategy.nil?
    log_warn("Unsupported URI option '#{key}' on URI '#{@string}'. It will be ignored.")
    return
  end

  group = strategy[:group]
  target = if group
    uri_options[group] || {}
  else
    uri_options
  end
  value = apply_transform(key, value, strategy[:type])
  # Sometimes the value here would be nil, for example if we are processing
  # read preference tags or auth mechanism properties and all of the
  # data within is invalid. Ignore such options.
  unless value.nil?
    merge_uri_option(target, value, strategy[:name])
  end

  if group && !target.empty? && !uri_options.key?(group)
    uri_options[group] = target
  end
end
ruby_to_smc(opts) click to toggle source

Converts Ruby options provided to “standardized MongoClient options”.

@param [ Hash ] opts Ruby options to convert.

@return [ Hash ] Standardized MongoClient options.

# File lib/mongo/uri/options_mapper.rb, line 126
def ruby_to_smc(opts)
  rv = {}
  URI_OPTION_MAP.each do |uri_key, spec|
    if spec[:group]
      v = opts[spec[:group]]
      v = v && v[spec[:name]]
    else
      v = opts[spec[:name]]
    end
    unless v.nil?
      if type = spec[:type]
        v = send("revert_#{type}", v)
      end
      canonical_key = URI_OPTION_CANONICAL_NAMES[uri_key]
      unless canonical_key
        raise ArgumentError, "Option #{uri_key} is not known"
      end
      rv[canonical_key] = v
    end
  end
  # For options that default to true, remove the value if it is true.
  %w(retryReads retryWrites).each do |k|
    if rv[k]
      rv.delete(k)
    end
  end
  # Remove auth source when it is $external for mechanisms that default
  # (or require) that auth source.
  if %w(MONGODB-AWS).include?(rv['authMechanism']) && rv['authSource'] == '$external'
    rv.delete('authSource')
  end
  # ssl and tls are aliases, remove ssl ones
  rv.delete('ssl')
  # TODO remove authSource if it is the same as the database,
  # requires this method to know the database specified in the client.
  rv
end
ruby_to_string(opts) click to toggle source

Converts Ruby options provided to their representation in a URI string.

@param [ Hash ] opts Ruby options to convert.

@return [ Hash ] URI string hash.

# File lib/mongo/uri/options_mapper.rb, line 169
def ruby_to_string(opts)
  rv = {}
  URI_OPTION_MAP.each do |uri_key, spec|
    if spec[:group]
      v = opts[spec[:group]]
      v = v && v[spec[:name]]
    else
      v = opts[spec[:name]]
    end
    unless v.nil?
      if type = spec[:type]
        v = send("stringify_#{type}", v)
      end
      canonical_key = URI_OPTION_CANONICAL_NAMES[uri_key]
      unless canonical_key
        raise ArgumentError, "Option #{uri_key} is not known"
      end
      rv[canonical_key] = v
    end
  end
  # For options that default to true, remove the value if it is true.
  %w(retryReads retryWrites).each do |k|
    if rv[k]
      rv.delete(k)
    end
  end
  # Remove auth source when it is $external for mechanisms that default
  # (or require) that auth source.
  if %w(MONGODB-AWS).include?(rv['authMechanism']) && rv['authSource'] == '$external'
    rv.delete('authSource')
  end
  # ssl and tls are aliases, remove ssl ones
  rv.delete('ssl')
  # TODO remove authSource if it is the same as the database,
  # requires this method to know the database specified in the client.
  rv
end
smc_to_ruby(opts) click to toggle source
# File lib/mongo/uri/options_mapper.rb, line 88
def smc_to_ruby(opts)
  uri_options = {}

  opts.each do |key, value|
    strategy = URI_OPTION_MAP[key.downcase]
    if strategy.nil?
      log_warn("Unsupported URI option '#{key}' on URI '#{@string}'. It will be ignored.")
      return
    end

    group = strategy[:group]
    target = if group
      uri_options[group] || {}
    else
      uri_options
    end

    value = apply_transform(key, value, strategy[:type])
    # Sometimes the value here would be nil, for example if we are processing
    # read preference tags or auth mechanism properties and all of the
    # data within is invalid. Ignore such options.
    unless value.nil?
      merge_uri_option(target, value, strategy[:name])
    end

    if group && !target.empty? && !uri_options.key?(group)
      uri_options[group] = target
    end
  end

  uri_options
end

Private Instance Methods

apply_transform(key, value, type) click to toggle source

Applies URI value transformation by either using the default cast or a transformation appropriate for the given type.

@param [ String ] key URI option name. @param [ String ] value The value to be transformed. @param [ Symbol ] type The transform method.

# File lib/mongo/uri/options_mapper.rb, line 215
def apply_transform(key, value, type)
  if type
    send("convert_#{type}", key, value)
  else
    value
  end
end
convert_array(name, value) click to toggle source

Extract values from the string and put them into an array.

@param [ String ] name Name of the URI option being processed. @param [ String ] value The string to build an array from.

@return [ Array<String> ] The array built from the string.

# File lib/mongo/uri/options_mapper.rb, line 545
def convert_array(name, value)
  value.split(',')
end
convert_auth_mech(name, value) click to toggle source

Authentication mechanism transformation.

@param [ String ] name Name of the URI option being processed. @param [ String ] value The authentication mechanism.

@return [ Symbol ] The transformed authentication mechanism.

# File lib/mongo/uri/options_mapper.rb, line 573
def convert_auth_mech(name, value)
  auth_mech = AUTH_MECH_MAP[value.upcase]
  (auth_mech || value).tap do |mech|
    log_warn("#{value} is not a valid auth mechanism") unless auth_mech
  end
end
convert_auth_mech_props(name, value) click to toggle source

Auth mechanism properties extractor.

@param [ String ] name Name of the URI option being processed. @param [ String ] value The auth mechanism properties string.

@return [ Hash | nil ] The auth mechanism properties hash.

# File lib/mongo/uri/options_mapper.rb, line 613
def convert_auth_mech_props(name, value)
  properties = hash_extractor('authMechanismProperties', value)
  if properties
    properties.each do |k, v|
      if k.to_s.downcase == 'canonicalize_host_name' && v
        properties[k] = (v.downcase == 'true')
      end
    end
  end
  properties
end
convert_bool(name, value) click to toggle source

Converts value to a boolean.

Returns true for 'true', false for 'false', otherwise nil.

@param [ String ] name Name of the URI option being processed. @param [ String | true | false ] value URI option value.

@return [ true | false | nil ] Converted value.

# File lib/mongo/uri/options_mapper.rb, line 334
def convert_bool(name, value)
  case value
  when true, "true", 'TRUE'
    true
  when false, "false", 'FALSE'
    false
  else
    log_warn("invalid boolean option for #{name}: #{value}")
    nil
  end
end
convert_integer(name, value) click to toggle source

Converts value into an integer. Only converts positive integers.

If the value is not a valid integer, warns and returns nil.

@param [ String ] name Name of the URI option being processed. @param [ String | Integer ] value URI option value.

@return [ nil | Integer ] Converted value.

# File lib/mongo/uri/options_mapper.rb, line 441
def convert_integer(name, value)
  if value.is_a?(String) && /\A\d+\z/ !~ value
    log_warn("#{value} is not a valid integer for #{name}")
    return nil
  end

  value.to_i
end
convert_inverse_bool(name, value) click to toggle source

Parses a boolean value and returns its inverse.

@param [ String ] name Name of the URI option being processed. @param [ String | true | false ] value The URI option value.

@return [ true | false | nil ] The inverse of the boolean value parsed out, otherwise nil

(and a warning will be logged).
# File lib/mongo/uri/options_mapper.rb, line 405
def convert_inverse_bool(name, value)
  b = convert_bool(name, value)

  if b.nil?
    nil
  else
    !b
  end
end
convert_max_staleness(name, value) click to toggle source

Parses the max staleness value, which must be either “0” or an integer greater or equal to 90.

@param [ String ] name Name of the URI option being processed. @param [ String | Integer ] value The max staleness string.

@return [ Integer | nil ] The max staleness integer parsed out if it is valid, otherwise nil

(and a warning will be logged).
# File lib/mongo/uri/options_mapper.rb, line 652
def convert_max_staleness(name, value)
  int = if value.is_a?(String) && /\A-?\d+\z/ =~ value
    value.to_i
  elsif value.is_a?(Integer)
    value
  end

  if int.nil?
    log_warn("Invalid max staleness value: #{value}")
    return nil
  end

  if int == -1
    int = nil
  end

  if int && (int > 0 && int < 90 || int < 0)
    log_warn("max staleness should be either 0 or greater than 90: #{value}")
    int = nil
  end

  int
end
convert_ms(name, value) click to toggle source

Ruby's convention is to provide timeouts in seconds, not milliseconds and to use fractions where more precision is necessary. The connection string options are always in MS so we provide an easy conversion type.

@param [ String ] name Name of the URI option being processed. @param [ String | Integer | Float ] value The millisecond value.

@return [ Float ] The seconds value.

@since 2.0.0

# File lib/mongo/uri/options_mapper.rb, line 478
def convert_ms(name, value)
  case value
  when String
    if /\A-?\d+(\.\d+)?\z/ !~ value
      log_warn("Invalid ms value for #{name}: #{value}")
      return nil
    end
    if value.to_s[0] == '-'
      log_warn("#{name} cannot be a negative number")
      return nil
    end
  when Integer, Float
    if value < 0
      log_warn("#{name} cannot be a negative number")
      return nil
    end
  else
    raise ArgumentError, "Can only convert Strings, Integers, or Floats to ms. Given: #{value.class}"
  end

  value.to_f / 1000
end
convert_read_mode(name, value) click to toggle source

Read preference mode transformation.

@param [ String ] name Name of the URI option being processed. @param [ String ] value The read mode string value.

@return [ Symbol | String ] The read mode.

# File lib/mongo/uri/options_mapper.rb, line 700
def convert_read_mode(name, value)
  READ_MODE_MAP[value.downcase] || value
end
convert_read_set(name, value) click to toggle source

Read preference tag set extractor.

@param [ String ] name Name of the URI option being processed. @param [ String ] value The tag set string.

@return [ Hash ] The tag set hash.

# File lib/mongo/uri/options_mapper.rb, line 753
def convert_read_set(name, value)
  hash_extractor('readPreferenceTags', value)
end
convert_read_tags(name, value) click to toggle source

Read preference tags transformation.

@param [ String ] name Name of the URI option being processed. @param [ String ] value The string representing tag set.

@return [ Array<Hash> | nil ] Array with tag set.

# File lib/mongo/uri/options_mapper.rb, line 720
def convert_read_tags(name, value)
  converted = convert_read_set(name, value)
  if converted
    [converted]
  else
    nil
  end
end
convert_repeated_bool(name, value) click to toggle source

Converts the value into a boolean and returns it wrapped in an array.

@param [ String ] name Name of the URI option being processed. @param [ String ] value URI option value.

@return [ Array<true | false> | nil ] The boolean value parsed and wraped

in an array.
# File lib/mongo/uri/options_mapper.rb, line 371
def convert_repeated_bool(name, value)
  [convert_bool(name, value)]
end
convert_symbol(name, value) click to toggle source

Converts value into a symbol.

@param [ String ] name Name of the URI option being processed. @param [ String | Symbol ] value URI option value.

@return [ Symbol ] Converted value.

# File lib/mongo/uri/options_mapper.rb, line 525
def convert_symbol(name, value)
  value.to_sym
end
convert_w(name, value) click to toggle source

Converts value as a write concern.

If value is the word “majority”, returns the symbol :majority. If value is a number, returns the number as an integer. Otherwise returns the string value unchanged.

@param [ String ] name Name of the URI option being processed. @param [ String | Integer ] value URI option value.

@return [ Integer | Symbol | String ] Converted value.

# File lib/mongo/uri/options_mapper.rb, line 767
def convert_w(name, value)
  case value
  when 'majority'
    :majority
  when /\A[0-9]+\z/
    value.to_i
  else
    value
  end
end
convert_zlib_compression_level(name, value) click to toggle source

Parses the zlib compression level.

@param [ String ] name Name of the URI option being processed. @param [ String | Integer ] value The zlib compression level string.

@return [ Integer | nil ] The compression level value if it is between -1 and 9 (inclusive),

otherwise nil (and a warning will be logged).
# File lib/mongo/uri/options_mapper.rb, line 808
def convert_zlib_compression_level(name, value)
  i = if value.is_a?(String) && /\A-?\d+\z/ =~ value
    value.to_i
  elsif value.is_a?(Integer)
    value
  end

  if i && (i >= -1 && i <= 9)
    i
  else
    log_warn("#{value} is not a valid zlibCompressionLevel")
    nil
  end
end
hash_extractor(name, value) click to toggle source

Extract values from the string and put them into a nested hash.

@param [ String ] name Name of the URI option being processed. @param [ String ] value The string to build a hash from.

@return [ Hash ] The hash built from the string.

# File lib/mongo/uri/options_mapper.rb, line 847
def hash_extractor(name, value)
  h = {}
  value.split(',').each do |tag|
    k, v = tag.split(':')
    if v.nil?
      log_warn("Invalid hash value for #{name}: key `#{k}` does not have a value: #{value}")
      next
    end

    h[k.to_sym] = v
  end
  if h.empty?
    nil
  else
    h
  end
end
merge_uri_option(target, value, name) click to toggle source

Merges a new option into the target.

If the option exists at the target destination the merge will be an addition.

Specifically required to append an additional tag set to the array of tag sets without overwriting the original.

@param [ Hash ] target The destination. @param [ Object ] value The value to be merged. @param [ Symbol ] name The name of the option.

# File lib/mongo/uri/options_mapper.rb, line 234
def merge_uri_option(target, value, name)
  if target.key?(name)
    if REPEATABLE_OPTIONS.include?(name)
      target[name] += value
    else
      log_warn("Repeated option key: #{name}.")
    end
  else
    target.merge!(name => value)
  end
end
revert_array(value) click to toggle source

Reverts an array.

@param [ Array<String> ] value An array of strings.

@return [ Array<String> ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 554
def revert_array(value)
  value
end
revert_auth_mech(value) click to toggle source

Reverts auth mechanism.

@param [ Symbol ] value The auth mechanism.

@return [ String ] The auth mechanism as a string.

@raise [ ArgumentError ] if its an invalid auth mechanism.

# File lib/mongo/uri/options_mapper.rb, line 587
def revert_auth_mech(value)
  found = AUTH_MECH_MAP.detect do |k, v|
    v == value
  end
  if found
    found.first
  else
    raise ArgumentError, "Unknown auth mechanism #{value}"
  end
end
revert_auth_mech_props(value) click to toggle source

Reverts auth mechanism properties.

@param [ Hash | nil ] value The auth mech properties.

@return [ Hash | nil ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 630
def revert_auth_mech_props(value)
  value
end
revert_bool(value) click to toggle source

Reverts a boolean type.

@param [ true | false | nil ] value The boolean to revert.

@return [ true | false | nil ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 351
def revert_bool(value)
  value
end
revert_integer(value) click to toggle source

Reverts an integer.

@param [ Integer | nil ] value The integer.

@return [ Integer | nil ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 455
def revert_integer(value)
  value
end
revert_inverse_bool(value) click to toggle source

Reverts and inverts a boolean type.

@param [ true | false | nil ] value The boolean to revert and invert.

@return [ true | false | nil ] The inverted boolean.

# File lib/mongo/uri/options_mapper.rb, line 420
def revert_inverse_bool(value)
  value.nil? ? nil : !value
end
revert_max_staleness(value) click to toggle source

Reverts max staleness.

@param [ Integer | nil ] value The max staleness.

@return [ Integer | nil ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 681
def revert_max_staleness(value)
  value
end
revert_ms(value) click to toggle source

Reverts an ms.

@param [ Float ] value The float.

@return [ Integer ] The number multiplied by 1000 as an integer.

# File lib/mongo/uri/options_mapper.rb, line 506
def revert_ms(value)
  (value * 1000).round
end
revert_read_mode(value) click to toggle source

Reverts read mode.

@param [ Symbol | String ] value The read mode.

@return [ String ] The read mode as a string.

# File lib/mongo/uri/options_mapper.rb, line 709
def revert_read_mode(value)
  value.to_s.gsub(/_(\w)/) { $1.upcase }
end
Also aliased as: stringify_read_mode
revert_read_tags(value) click to toggle source

Reverts read tags.

@param [ Array<Hash> | nil ] value The read tags.

@return [ Array<Hash> | nil ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 734
def revert_read_tags(value)
  value
end
revert_repeated_bool(value) click to toggle source

Reverts a repeated boolean type.

@param [ Array<true | false> | true | false | nil ] value The repeated boolean to revert.

@return [ Array<true | false> | true | false | nil ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 380
def revert_repeated_bool(value)
  value
end
revert_symbol(value) click to toggle source

Reverts a symbol.

@param [ Symbol ] value The symbol.

@return [ String ] The passed value as a string.

# File lib/mongo/uri/options_mapper.rb, line 534
def revert_symbol(value)
  value.to_s
end
Also aliased as: stringify_symbol
revert_w(value) click to toggle source

Reverts write concern.

@param [ Integer | Symbol | String ] value The write concern.

@return [ Integer | String ] The write concern as a string.

# File lib/mongo/uri/options_mapper.rb, line 783
def revert_w(value)
  case value
  when Symbol
    value.to_s
  else
    value
  end
end
revert_zlib_compression_level(value) click to toggle source

Reverts zlib compression level

@param [ Integer | nil ] value The write concern.

@return [ Integer | nil ] The passed value.

# File lib/mongo/uri/options_mapper.rb, line 828
def revert_zlib_compression_level(value)
  value
end
stringify_array(value) click to toggle source

Stringifies an array.

@param [ Array<String> ] value An array of strings.

@return [ String ] The array joined by commas.

# File lib/mongo/uri/options_mapper.rb, line 563
def stringify_array(value)
  value.join(',')
end
stringify_auth_mech(value) click to toggle source

Stringifies auth mechanism.

@param [ Symbol ] value The auth mechanism.

@return [ String | nil ] The auth mechanism as a string.

# File lib/mongo/uri/options_mapper.rb, line 603
def stringify_auth_mech(value)
  revert_auth_mech(value) rescue nil
end
stringify_auth_mech_props(value) click to toggle source

Stringifies auth mechanism properties.

@param [ Hash | nil ] value The auth mech properties.

@return [ String | nil ] The string.

# File lib/mongo/uri/options_mapper.rb, line 639
def stringify_auth_mech_props(value)
  return if value.nil?
  value.map { |k, v| "#{k}:#{v}" }.join(',')
end
stringify_bool(value) click to toggle source

Stringifies a boolean type.

@param [ true | false | nil ] value The boolean.

@return [ String | nil ] The string.

# File lib/mongo/uri/options_mapper.rb, line 360
def stringify_bool(value)
  revert_bool(value)&.to_s
end
stringify_integer(value) click to toggle source

Stringifies an integer.

@param [ Integer | nil ] value The integer.

@return [ String | nil ] The string.

# File lib/mongo/uri/options_mapper.rb, line 464
def stringify_integer(value)
  revert_integer(value)&.to_s
end
stringify_inverse_bool(value) click to toggle source

Inverts and stringifies a boolean.

@param [ true | false | nil ] value The boolean.

@return [ String | nil ] The string.

# File lib/mongo/uri/options_mapper.rb, line 429
def stringify_inverse_bool(value)
  revert_inverse_bool(value)&.to_s
end
stringify_max_staleness(value) click to toggle source

Stringifies max staleness.

@param [ Integer | nil ] value The max staleness.

@return [ String | nil ] The string.

# File lib/mongo/uri/options_mapper.rb, line 690
def stringify_max_staleness(value)
  revert_max_staleness(value)&.to_s
end
stringify_ms(value) click to toggle source

Stringifies an ms.

@param [ Float ] value The float.

@return [ String ] The string.

# File lib/mongo/uri/options_mapper.rb, line 515
def stringify_ms(value)
  revert_ms(value).to_s
end
stringify_read_mode(value)
Alias for: revert_read_mode
stringify_read_tags(value) click to toggle source

Stringifies read tags.

@param [ Array<Hash> | nil ] value The read tags.

@return [ String | nil ] The joined string of read tags.

# File lib/mongo/uri/options_mapper.rb, line 743
def stringify_read_tags(value)
  value&.map { |ar| ar.map { |k, v| "#{k}:#{v}" }.join(',') }
end
stringify_repeated_bool(value) click to toggle source

Stringifies a repeated boolean type.

@param [ Array<true | false> | nil ] value The repeated boolean.

@return [ Array<true | false> | nil ] The string.

# File lib/mongo/uri/options_mapper.rb, line 389
def stringify_repeated_bool(value)
  rep = revert_repeated_bool(value)
  if rep&.is_a?(Array)
    rep.join(",")
  else
    rep
  end
end
stringify_symbol(value)
Alias for: revert_symbol
stringify_w(value) click to toggle source

Stringifies write concern.

@param [ Integer | Symbol | String ] value The write concern.

@return [ String ] The write concern as a string.

# File lib/mongo/uri/options_mapper.rb, line 797
def stringify_w(value)
  revert_w(value)&.to_s
end
stringify_zlib_compression_level(value) click to toggle source

Stringifies zlib compression level

@param [ Integer | nil ] value The write concern.

@return [ String | nil ] The string.

# File lib/mongo/uri/options_mapper.rb, line 837
def stringify_zlib_compression_level(value)
  revert_zlib_compression_level(value)&.to_s
end