1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 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 138 139 140 141 142 143 144 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 173 174 175 176 177 178 179 180 181 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 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 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 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
|
# frozen_string_literal: true
# rubocop:todo all
# Copyright (C) 2020 MongoDB Inc.
#
# Licensed under the Apache License, Version 2.0 (the 'License');
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an 'AS IS' BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Mongo
class URI
# Performs mapping between URI options and Ruby options.
#
# This class contains:
#
# - The mapping defining how URI options are converted to Ruby options.
# - The mapping from downcased URI option names to canonical-cased URI
# option names.
# - Methods to perform conversion of URI option values to Ruby option
# values (the convert_* methods). These generally warn and return nil
# when input given is invalid.
# - Methods to perform conversion of Ruby option values to standardized
# MongoClient options (revert_* methods). These assume the input is valid
# and generally do not perform validation.
#
# URI option names are case insensitive. Ruby options are specified as
# symbols (though in Client options use indifferent access).
#
# @api private
class OptionsMapper
include Loggable
# Instantates the options mapper.
#
# @option opts [ Logger ] :logger A custom logger to use.
def initialize(**opts)
@options = opts
end
# @return [ Hash ] The options.
attr_reader :options
# 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.
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
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
# Converts Ruby options provided to "standardized MongoClient options".
#
# @param [ Hash ] opts Ruby options to convert.
#
# @return [ Hash ] Standardized MongoClient options.
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
# Converts Ruby options provided to their representation in a URI string.
#
# @param [ Hash ] opts Ruby options to convert.
#
# @return [ Hash ] URI string hash.
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
private
# 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.
def apply_transform(key, value, type)
if type
send("convert_#{type}", key, value)
else
value
end
end
# 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.
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
# Hash for storing map of URI option parameters to conversion strategies
URI_OPTION_MAP = {}
# @return [ Hash<String, String> ] Map from lowercased to canonical URI
# option names.
URI_OPTION_CANONICAL_NAMES = {}
# 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.
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
# Replica Set Options
uri_option 'replicaSet', :replica_set
# Timeout Options
uri_option 'connectTimeoutMS', :connect_timeout, type: :ms
uri_option 'socketTimeoutMS', :socket_timeout, type: :ms
uri_option 'serverSelectionTimeoutMS', :server_selection_timeout, type: :ms
uri_option 'localThresholdMS', :local_threshold, type: :ms
uri_option 'heartbeatFrequencyMS', :heartbeat_frequency, type: :ms
uri_option 'maxIdleTimeMS', :max_idle_time, type: :ms
uri_option 'timeoutMS', :timeout_ms, type: :integer
# Write Options
uri_option 'w', :w, group: :write_concern, type: :w
uri_option 'journal', :j, group: :write_concern, type: :bool
uri_option 'fsync', :fsync, group: :write_concern, type: :bool
uri_option 'wTimeoutMS', :wtimeout, group: :write_concern, type: :integer
# Read Options
uri_option 'readPreference', :mode, group: :read, type: :read_mode
uri_option 'readPreferenceTags', :tag_sets, group: :read, type: :read_tags
uri_option 'maxStalenessSeconds', :max_staleness, group: :read, type: :max_staleness
# Pool options
uri_option 'maxConnecting', :max_connecting, type: :integer
uri_option 'minPoolSize', :min_pool_size, type: :integer
uri_option 'maxPoolSize', :max_pool_size, type: :integer
uri_option 'waitQueueTimeoutMS', :wait_queue_timeout, type: :ms
# Security Options
uri_option 'ssl', :ssl, type: :repeated_bool
uri_option 'tls', :ssl, type: :repeated_bool
uri_option 'tlsAllowInvalidCertificates', :ssl_verify_certificate,
type: :inverse_bool
uri_option 'tlsAllowInvalidHostnames', :ssl_verify_hostname,
type: :inverse_bool
uri_option 'tlsCAFile', :ssl_ca_cert
uri_option 'tlsCertificateKeyFile', :ssl_cert
uri_option 'tlsCertificateKeyFilePassword', :ssl_key_pass_phrase
uri_option 'tlsInsecure', :ssl_verify, type: :inverse_bool
uri_option 'tlsDisableOCSPEndpointCheck', :ssl_verify_ocsp_endpoint,
type: :inverse_bool
# Topology options
uri_option 'directConnection', :direct_connection, type: :bool
uri_option 'connect', :connect, type: :symbol
uri_option 'loadBalanced', :load_balanced, type: :bool
uri_option 'srvMaxHosts', :srv_max_hosts, type: :integer
uri_option 'srvServiceName', :srv_service_name
# Auth Options
uri_option 'authSource', :auth_source
uri_option 'authMechanism', :auth_mech, type: :auth_mech
uri_option 'authMechanismProperties', :auth_mech_properties, type: :auth_mech_props
# Client Options
uri_option 'appName', :app_name
uri_option 'compressors', :compressors, type: :array
uri_option 'readConcernLevel', :level, group: :read_concern, type: :symbol
uri_option 'retryReads', :retry_reads, type: :bool
uri_option 'retryWrites', :retry_writes, type: :bool
uri_option 'zlibCompressionLevel', :zlib_compression_level, type: :zlib_compression_level
# 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.
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
# Reverts a boolean type.
#
# @param [ true | false | nil ] value The boolean to revert.
#
# @return [ true | false | nil ] The passed value.
def revert_bool(value)
value
end
# Stringifies a boolean type.
#
# @param [ true | false | nil ] value The boolean.
#
# @return [ String | nil ] The string.
def stringify_bool(value)
revert_bool(value)&.to_s
end
# 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.
def convert_repeated_bool(name, value)
[convert_bool(name, value)]
end
# 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.
def revert_repeated_bool(value)
value
end
# Stringifies a repeated boolean type.
#
# @param [ Array<true | false> | nil ] value The repeated boolean.
#
# @return [ Array<true | false> | nil ] The string.
def stringify_repeated_bool(value)
rep = revert_repeated_bool(value)
if rep&.is_a?(Array)
rep.join(",")
else
rep
end
end
# 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).
def convert_inverse_bool(name, value)
b = convert_bool(name, value)
if b.nil?
nil
else
!b
end
end
# Reverts and inverts a boolean type.
#
# @param [ true | false | nil ] value The boolean to revert and invert.
#
# @return [ true | false | nil ] The inverted boolean.
def revert_inverse_bool(value)
value.nil? ? nil : !value
end
# Inverts and stringifies a boolean.
#
# @param [ true | false | nil ] value The boolean.
#
# @return [ String | nil ] The string.
def stringify_inverse_bool(value)
revert_inverse_bool(value)&.to_s
end
# 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.
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
# Reverts an integer.
#
# @param [ Integer | nil ] value The integer.
#
# @return [ Integer | nil ] The passed value.
def revert_integer(value)
value
end
# Stringifies an integer.
#
# @param [ Integer | nil ] value The integer.
#
# @return [ String | nil ] The string.
def stringify_integer(value)
revert_integer(value)&.to_s
end
# 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
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
# Reverts an ms.
#
# @param [ Float ] value The float.
#
# @return [ Integer ] The number multiplied by 1000 as an integer.
def revert_ms(value)
(value * 1000).round
end
# Stringifies an ms.
#
# @param [ Float ] value The float.
#
# @return [ String ] The string.
def stringify_ms(value)
revert_ms(value).to_s
end
# 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.
def convert_symbol(name, value)
value.to_sym
end
# Reverts a symbol.
#
# @param [ Symbol ] value The symbol.
#
# @return [ String ] The passed value as a string.
def revert_symbol(value)
value.to_s
end
alias :stringify_symbol :revert_symbol
# 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.
def convert_array(name, value)
value.split(',')
end
# Reverts an array.
#
# @param [ Array<String> ] value An array of strings.
#
# @return [ Array<String> ] The passed value.
def revert_array(value)
value
end
# Stringifies an array.
#
# @param [ Array<String> ] value An array of strings.
#
# @return [ String ] The array joined by commas.
def stringify_array(value)
value.join(',')
end
# 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.
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
# 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.
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
# Stringifies auth mechanism.
#
# @param [ Symbol ] value The auth mechanism.
#
# @return [ String | nil ] The auth mechanism as a string.
def stringify_auth_mech(value)
revert_auth_mech(value) rescue nil
end
# 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.
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
# Reverts auth mechanism properties.
#
# @param [ Hash | nil ] value The auth mech properties.
#
# @return [ Hash | nil ] The passed value.
def revert_auth_mech_props(value)
value
end
# Stringifies auth mechanism properties.
#
# @param [ Hash | nil ] value The auth mech properties.
#
# @return [ String | nil ] The string.
def stringify_auth_mech_props(value)
return if value.nil?
value.map { |k, v| "#{k}:#{v}" }.join(',')
end
# 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).
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
# Reverts max staleness.
#
# @param [ Integer | nil ] value The max staleness.
#
# @return [ Integer | nil ] The passed value.
def revert_max_staleness(value)
value
end
# Stringifies max staleness.
#
# @param [ Integer | nil ] value The max staleness.
#
# @return [ String | nil ] The string.
def stringify_max_staleness(value)
revert_max_staleness(value)&.to_s
end
# 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.
def convert_read_mode(name, value)
READ_MODE_MAP[value.downcase] || value
end
# Reverts read mode.
#
# @param [ Symbol | String ] value The read mode.
#
# @return [ String ] The read mode as a string.
def revert_read_mode(value)
value.to_s.gsub(/_(\w)/) { $1.upcase }
end
alias :stringify_read_mode :revert_read_mode
# 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.
def convert_read_tags(name, value)
converted = convert_read_set(name, value)
if converted
[converted]
else
nil
end
end
# Reverts read tags.
#
# @param [ Array<Hash> | nil ] value The read tags.
#
# @return [ Array<Hash> | nil ] The passed value.
def revert_read_tags(value)
value
end
# Stringifies read tags.
#
# @param [ Array<Hash> | nil ] value The read tags.
#
# @return [ String | nil ] The joined string of read tags.
def stringify_read_tags(value)
value&.map { |ar| ar.map { |k, v| "#{k}:#{v}" }.join(',') }
end
# 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.
def convert_read_set(name, value)
hash_extractor('readPreferenceTags', value)
end
# 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.
def convert_w(name, value)
case value
when 'majority'
:majority
when /\A[0-9]+\z/
value.to_i
else
value
end
end
# Reverts write concern.
#
# @param [ Integer | Symbol | String ] value The write concern.
#
# @return [ Integer | String ] The write concern as a string.
def revert_w(value)
case value
when Symbol
value.to_s
else
value
end
end
# Stringifies write concern.
#
# @param [ Integer | Symbol | String ] value The write concern.
#
# @return [ String ] The write concern as a string.
def stringify_w(value)
revert_w(value)&.to_s
end
# 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).
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
# Reverts zlib compression level
#
# @param [ Integer | nil ] value The write concern.
#
# @return [ Integer | nil ] The passed value.
def revert_zlib_compression_level(value)
value
end
# Stringifies zlib compression level
#
# @param [ Integer | nil ] value The write concern.
#
# @return [ String | nil ] The string.
def stringify_zlib_compression_level(value)
revert_zlib_compression_level(value)&.to_s
end
# 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.
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
end
end
end
|