mongo 2.24.0 → 2.25.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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mongo_console +2 -2
  3. data/lib/mongo/address/validator.rb +1 -1
  4. data/lib/mongo/address.rb +1 -3
  5. data/lib/mongo/auth/aws/conversation.rb +0 -4
  6. data/lib/mongo/auth/aws/credentials_retriever.rb +3 -8
  7. data/lib/mongo/auth/base.rb +1 -1
  8. data/lib/mongo/auth/sasl_conversation_base.rb +1 -1
  9. data/lib/mongo/auth/scram_conversation_base.rb +1 -8
  10. data/lib/mongo/auth/stringprep.rb +1 -6
  11. data/lib/mongo/auth/user/view.rb +1 -1
  12. data/lib/mongo/bulk_write/result.rb +10 -2
  13. data/lib/mongo/bulk_write/result_combiner.rb +11 -4
  14. data/lib/mongo/bulk_write.rb +1 -1
  15. data/lib/mongo/client.rb +82 -12
  16. data/lib/mongo/cluster/reapers/cursor_reaper.rb +12 -5
  17. data/lib/mongo/cluster/sdam_flow.rb +4 -4
  18. data/lib/mongo/cluster/topology/base.rb +1 -1
  19. data/lib/mongo/cluster.rb +12 -28
  20. data/lib/mongo/collection/view/aggregation/behavior.rb +1 -1
  21. data/lib/mongo/collection/view/aggregation.rb +2 -4
  22. data/lib/mongo/collection/view/builder/aggregation.rb +3 -5
  23. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  24. data/lib/mongo/collection/view/change_stream.rb +14 -9
  25. data/lib/mongo/collection/view/map_reduce.rb +2 -11
  26. data/lib/mongo/collection/view/readable.rb +11 -7
  27. data/lib/mongo/collection/view/writable.rb +4 -4
  28. data/lib/mongo/collection/view.rb +8 -4
  29. data/lib/mongo/collection.rb +1 -1
  30. data/lib/mongo/config.rb +5 -0
  31. data/lib/mongo/crypt/auto_encrypter.rb +1 -1
  32. data/lib/mongo/crypt/binary.rb +7 -4
  33. data/lib/mongo/crypt/binding.rb +88 -10
  34. data/lib/mongo/crypt/context.rb +50 -19
  35. data/lib/mongo/crypt/encryption_io.rb +11 -5
  36. data/lib/mongo/crypt/handle.rb +17 -5
  37. data/lib/mongo/crypt/kms/credentials.rb +70 -31
  38. data/lib/mongo/crypt/kms/kmip/master_document.rb +30 -1
  39. data/lib/mongo/crypt/kms/master_key_document.rb +11 -6
  40. data/lib/mongo/crypt/kms.rb +12 -1
  41. data/lib/mongo/cursor.rb +8 -2
  42. data/lib/mongo/database/cursor_command_view.rb +95 -0
  43. data/lib/mongo/database.rb +144 -18
  44. data/lib/mongo/error/bulk_write_error.rb +29 -1
  45. data/lib/mongo/error/invalid_server_preference.rb +3 -0
  46. data/lib/mongo/error/invalid_uri.rb +5 -1
  47. data/lib/mongo/error/operation_failure.rb +42 -3
  48. data/lib/mongo/error/parser.rb +3 -3
  49. data/lib/mongo/error/sdam_error_detection.rb +2 -3
  50. data/lib/mongo/grid/fs_bucket.rb +27 -21
  51. data/lib/mongo/grid/stream/write.rb +2 -2
  52. data/lib/mongo/index/view.rb +13 -7
  53. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -0
  54. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +1 -0
  55. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -0
  56. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +1 -0
  57. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -0
  58. data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -0
  59. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -0
  60. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +1 -0
  61. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -0
  62. data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -0
  63. data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -0
  64. data/lib/mongo/monitoring/event/command_failed.rb +1 -0
  65. data/lib/mongo/monitoring/event/command_started.rb +1 -0
  66. data/lib/mongo/monitoring/event/command_succeeded.rb +1 -0
  67. data/lib/mongo/monitoring/event/secure.rb +12 -17
  68. data/lib/mongo/monitoring/event/server_closed.rb +1 -0
  69. data/lib/mongo/monitoring/event/server_description_changed.rb +1 -0
  70. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +1 -0
  71. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -0
  72. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +1 -0
  73. data/lib/mongo/monitoring/event/server_opening.rb +1 -0
  74. data/lib/mongo/monitoring/event/topology_changed.rb +1 -0
  75. data/lib/mongo/monitoring/event/topology_closed.rb +1 -0
  76. data/lib/mongo/monitoring/event/topology_opening.rb +1 -0
  77. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +2 -0
  78. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +3 -0
  79. data/lib/mongo/operation/create/op_msg.rb +1 -0
  80. data/lib/mongo/operation/create_index/op_msg.rb +2 -1
  81. data/lib/mongo/operation/cursor_command/op_msg.rb +37 -0
  82. data/lib/mongo/operation/cursor_command/result.rb +60 -0
  83. data/lib/mongo/operation/cursor_command.rb +33 -0
  84. data/lib/mongo/operation/delete/op_msg.rb +1 -0
  85. data/lib/mongo/operation/drop/op_msg.rb +1 -0
  86. data/lib/mongo/operation/drop_database/op_msg.rb +1 -0
  87. data/lib/mongo/operation/drop_index/op_msg.rb +1 -0
  88. data/lib/mongo/operation/find/builder/command.rb +1 -1
  89. data/lib/mongo/operation/insert/bulk_result.rb +2 -4
  90. data/lib/mongo/operation/insert/op_msg.rb +1 -0
  91. data/lib/mongo/operation/result.rb +14 -26
  92. data/lib/mongo/operation/shared/causal_consistency_supported.rb +4 -2
  93. data/lib/mongo/operation/shared/executable.rb +2 -10
  94. data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
  95. data/lib/mongo/operation/shared/sessions_supported.rb +3 -5
  96. data/lib/mongo/operation/update/bulk_result.rb +1 -1
  97. data/lib/mongo/operation/update/op_msg.rb +1 -0
  98. data/lib/mongo/operation/write_command/op_msg.rb +2 -0
  99. data/lib/mongo/operation.rb +1 -0
  100. data/lib/mongo/options/redacted.rb +2 -2
  101. data/lib/mongo/protocol/compressed.rb +15 -11
  102. data/lib/mongo/protocol/message.rb +16 -16
  103. data/lib/mongo/protocol/registry.rb +1 -1
  104. data/lib/mongo/protocol/serializers.rb +1 -3
  105. data/lib/mongo/query_cache.rb +3 -5
  106. data/lib/mongo/server/connection.rb +3 -1
  107. data/lib/mongo/server/connection_common.rb +9 -1
  108. data/lib/mongo/server/connection_pool/generation_manager.rb +1 -1
  109. data/lib/mongo/server/connection_pool.rb +79 -18
  110. data/lib/mongo/server/description/features.rb +2 -2
  111. data/lib/mongo/server/description.rb +24 -2
  112. data/lib/mongo/server/monitor/connection.rb +3 -2
  113. data/lib/mongo/server/monitor.rb +133 -30
  114. data/lib/mongo/server/pending_connection.rb +1 -0
  115. data/lib/mongo/server/push_monitor.rb +16 -2
  116. data/lib/mongo/server.rb +27 -7
  117. data/lib/mongo/server_selector/base.rb +16 -6
  118. data/lib/mongo/server_selector/nearest.rb +3 -0
  119. data/lib/mongo/server_selector/primary.rb +3 -0
  120. data/lib/mongo/server_selector/primary_preferred.rb +3 -0
  121. data/lib/mongo/server_selector/secondary.rb +3 -0
  122. data/lib/mongo/server_selector/secondary_preferred.rb +7 -1
  123. data/lib/mongo/session.rb +56 -24
  124. data/lib/mongo/socket/ssl.rb +3 -3
  125. data/lib/mongo/socket.rb +1 -7
  126. data/lib/mongo/srv/monitor.rb +11 -2
  127. data/lib/mongo/srv/resolver.rb +1 -1
  128. data/lib/mongo/srv/result.rb +5 -3
  129. data/lib/mongo/timeout.rb +10 -15
  130. data/lib/mongo/tracing/open_telemetry/command_tracer.rb +43 -2
  131. data/lib/mongo/uri/options_mapper.rb +21 -9
  132. data/lib/mongo/uri/srv_protocol.rb +4 -4
  133. data/lib/mongo/uri.rb +56 -8
  134. data/lib/mongo/utils.rb +1 -1
  135. data/lib/mongo/version.rb +1 -1
  136. data/lib/mongo.rb +1 -0
  137. metadata +6 -4
  138. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +0 -173
  139. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +0 -1164
data/lib/mongo/uri.rb CHANGED
@@ -141,6 +141,19 @@ module Mongo
141
141
  # @since 2.5.0
142
142
  SCHEME_DELIM = '://'
143
143
 
144
+ # Placeholder used in place of cleartext credentials when a URI is
145
+ # rendered for display, logging, or error reporting.
146
+ CREDENTIALS_PLACEHOLDER = '<credentials>'
147
+
148
+ # Pattern matching the userinfo portion of a MongoDB connection string.
149
+ # Anchors at the start and is bounded to the authority component (stops at
150
+ # the first '/', '?', or '#'), but matches greedily up to the last '@' in
151
+ # that component so passwords containing an unescaped '@' are still fully
152
+ # redacted. Case-insensitive so an unusual scheme like 'MongoDB://' is
153
+ # redacted too — the parser will reject it, and the redactor must not be
154
+ # the thing that leaks the credentials in the resulting error.
155
+ USERINFO_REDACTION_REGEX = %r{\A(mongodb(?:\+srv)?://)[^/?#]*@}i.freeze
156
+
144
157
  # Error details for an invalid options format.
145
158
  #
146
159
  # @since 2.1.0
@@ -215,6 +228,24 @@ module Mongo
215
228
  # @since 2.1.0
216
229
  REPEATABLE_OPTIONS = %i[tag_sets ssl]
217
230
 
231
+ # Replace the userinfo portion of a MongoDB connection string with a
232
+ # placeholder so the result can safely be logged, displayed, or embedded
233
+ # in an exception message.
234
+ #
235
+ # The input is matched as a string, not parsed, so this is safe to call
236
+ # on malformed URIs (which is exactly when {InvalidURI} is raised).
237
+ #
238
+ # @param [ String ] string The raw URI string.
239
+ #
240
+ # @return [ String ] The URI with any userinfo replaced by
241
+ # {CREDENTIALS_PLACEHOLDER}, or the input unchanged if it is not a
242
+ # string or has no userinfo.
243
+ def self.redact(string)
244
+ return string unless string.is_a?(String)
245
+
246
+ string.sub(USERINFO_REDACTION_REGEX, "\\1#{CREDENTIALS_PLACEHOLDER}@")
247
+ end
248
+
218
249
  # Get either a URI object or a SRVProtocol URI object.
219
250
  #
220
251
  # @example Get the uri object.
@@ -320,20 +351,33 @@ module Mongo
320
351
  @database || Database::ADMIN
321
352
  end
322
353
 
323
- # Get the uri as a string.
354
+ # Get the uri as a string with any credentials redacted.
355
+ #
356
+ # Credentials are replaced with {CREDENTIALS_PLACEHOLDER} so the result is
357
+ # safe to log or display. Use {#credentials} to recover the original user
358
+ # and password.
324
359
  #
325
360
  # @example Get the uri as a string.
326
361
  # uri.to_s
327
362
  #
328
- # @return [ String ] The uri string.
363
+ # @return [ String ] The redacted uri string.
329
364
  def to_s
330
365
  reconstruct_uri
331
366
  end
332
367
 
368
+ # Return a redacted, human-readable representation of the URI. The
369
+ # default {Object#inspect} would dump {@string} and {@password} as
370
+ # instance variables, leaking credentials.
371
+ #
372
+ # @return [ String ] The redacted inspect string.
373
+ def inspect
374
+ "#<#{self.class.name}: #{reconstruct_uri}>"
375
+ end
376
+
333
377
  private
334
378
 
335
- # Reconstruct the URI from its parts. Invalid options are dropped and options
336
- # are converted to camelCase.
379
+ # Reconstruct the URI from its parts with credentials redacted. Invalid
380
+ # options are dropped and options are converted to camelCase.
337
381
  #
338
382
  # @return [ String ] the uri.
339
383
  def reconstruct_uri
@@ -349,9 +393,7 @@ module Mongo
349
393
  end.compact.join('&')
350
394
 
351
395
  uri = "#{scheme}#{SCHEME_DELIM}"
352
- uri += @user.to_s if @user
353
- uri += "#{AUTH_USER_PWD_DELIM}#{@password}" if @password
354
- uri += '@' if @user || @password
396
+ uri += "#{CREDENTIALS_PLACEHOLDER}@" if @user || @password
355
397
  uri += @query_hostname || servers
356
398
  uri += '/' if @database || !options.empty?
357
399
  uri += @database.to_s if @database
@@ -418,7 +460,7 @@ module Mongo
418
460
  end
419
461
 
420
462
  def parse_user!(string)
421
- if string && user = string.partition(AUTH_USER_PWD_DELIM)[0]
463
+ if string && (user = string.partition(AUTH_USER_PWD_DELIM)[0])
422
464
  raise_invalid_error!(UNESCAPED_USER_PWD) if UNSAFE.match?(user)
423
465
  user_decoded = decode(user)
424
466
  raise_invalid_error!(UNESCAPED_USER_PWD) if user_decoded =~ PERCENT_CHAR && encode(user_decoded) != user
@@ -461,6 +503,12 @@ module Mongo
461
503
  # 'tls' and 'ssl'. In order to fulfill this, we parse the values of each instance into an
462
504
  # array; assuming all values in the array are the same, we replace the array with that value.
463
505
  unless uri_options[:ssl].nil? || uri_options[:ssl].empty?
506
+ # A nil element means an invalid value (e.g. tls=yes) was supplied.
507
+ # Refuse it rather than silently connecting over plaintext (RUBY-3832).
508
+ if uri_options[:ssl].include?(nil)
509
+ raise_invalid_error_no_fmt!("invalid boolean value for 'tls' or 'ssl'; only 'true' and 'false' are accepted")
510
+ end
511
+
464
512
  unless uri_options[:ssl].uniq.length == 1
465
513
  raise_invalid_error_no_fmt!("all instances of 'tls' and 'ssl' must have the same value")
466
514
  end
data/lib/mongo/utils.rb CHANGED
@@ -73,7 +73,7 @@ module Mongo
73
73
  # @note server_api must have symbol keys or be a BSON::Document.
74
74
  module_function def transform_server_api(server_api)
75
75
  {}.tap do |doc|
76
- if version = server_api[:version]
76
+ if (version = server_api[:version])
77
77
  doc['apiVersion'] = version
78
78
  end
79
79
  doc['apiStrict'] = server_api[:strict] unless server_api[:strict].nil?
data/lib/mongo/version.rb CHANGED
@@ -5,5 +5,5 @@ module Mongo
5
5
  #
6
6
  # Note that this file is automatically updated via `rake candidate:create`.
7
7
  # Manual changes to this file will be overwritten by that rake task.
8
- VERSION = '2.24.0'
8
+ VERSION = '2.25.0'
9
9
  end
data/lib/mongo.rb CHANGED
@@ -101,6 +101,7 @@ module Mongo
101
101
  delegate_option Config, :broken_view_options
102
102
  delegate_option Config, :validate_update_replace
103
103
  delegate_option Config, :csfle_convert_to_ruby_types
104
+ delegate_option Config, :include_server_address_in_errors
104
105
  end
105
106
 
106
107
  # Clears the driver's OCSP response cache.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.24.0
4
+ version: 2.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MongoDB Ruby Team
@@ -90,8 +90,6 @@ files:
90
90
  - lib/mongo/auth/stringprep.rb
91
91
  - lib/mongo/auth/stringprep/profiles/sasl.rb
92
92
  - lib/mongo/auth/stringprep/tables.rb
93
- - lib/mongo/auth/stringprep/unicode_normalize/normalize.rb
94
- - lib/mongo/auth/stringprep/unicode_normalize/tables.rb
95
93
  - lib/mongo/auth/user.rb
96
94
  - lib/mongo/auth/user/view.rb
97
95
  - lib/mongo/auth/x509.rb
@@ -191,6 +189,7 @@ files:
191
189
  - lib/mongo/cursor/nontailable.rb
192
190
  - lib/mongo/cursor_host.rb
193
191
  - lib/mongo/database.rb
192
+ - lib/mongo/database/cursor_command_view.rb
194
193
  - lib/mongo/database/view.rb
195
194
  - lib/mongo/dbref.rb
196
195
  - lib/mongo/deprecations.rb
@@ -369,6 +368,9 @@ files:
369
368
  - lib/mongo/operation/create_search_indexes/op_msg.rb
370
369
  - lib/mongo/operation/create_user.rb
371
370
  - lib/mongo/operation/create_user/op_msg.rb
371
+ - lib/mongo/operation/cursor_command.rb
372
+ - lib/mongo/operation/cursor_command/op_msg.rb
373
+ - lib/mongo/operation/cursor_command/result.rb
372
374
  - lib/mongo/operation/delete.rb
373
375
  - lib/mongo/operation/delete/bulk_result.rb
374
376
  - lib/mongo/operation/delete/op_msg.rb
@@ -559,7 +561,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
559
561
  - !ruby/object:Gem::Version
560
562
  version: '0'
561
563
  requirements: []
562
- rubygems_version: 4.0.10
564
+ rubygems_version: 4.0.17
563
565
  specification_version: 4
564
566
  summary: Ruby driver for MongoDB
565
567
  test_files: []
@@ -1,173 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright Ayumu Nojima (野島 歩) and Martin J. Dürst (duerst@it.aoyama.ac.jp)
4
-
5
- # This file, the companion file tables.rb (autogenerated), and the module,
6
- # constants, and method defined herein are part of the implementation of the
7
- # built-in String class, not part of the standard library. They should
8
- # therefore never be gemified. They implement the methods
9
- # String#unicode_normalize, String#unicode_normalize!, and String#unicode_normalized?.
10
- #
11
- # They are placed here because they are written in Ruby. They are loaded on
12
- # demand when any of the three methods mentioned above is executed for the
13
- # first time. This reduces the memory footprint and startup time for scripts
14
- # and applications that do not use those methods.
15
- #
16
- # The name and even the existence of the module UnicodeNormalize and all of its
17
- # content are purely an implementation detail, and should not be exposed in
18
- # any test or spec or otherwise.
19
-
20
- require 'mongo/auth/stringprep/unicode_normalize/tables'
21
-
22
- # @api private
23
- module UnicodeNormalize # :nodoc:
24
- ## Constant for max hash capacity to avoid DoS attack
25
- MAX_HASH_LENGTH = 18_000 # enough for all test cases, otherwise tests get slow
26
-
27
- ## Regular Expressions and Hash Constants
28
- REGEXP_D = Regexp.compile(REGEXP_D_STRING, Regexp::EXTENDED)
29
- REGEXP_C = Regexp.compile(REGEXP_C_STRING, Regexp::EXTENDED)
30
- REGEXP_K = Regexp.compile(REGEXP_K_STRING, Regexp::EXTENDED)
31
- NF_HASH_D = Hash.new do |hash, key|
32
- hash.shift if hash.length > MAX_HASH_LENGTH # prevent DoS attack
33
- hash[key] = nfd_one(key)
34
- end
35
- NF_HASH_C = Hash.new do |hash, key|
36
- hash.shift if hash.length > MAX_HASH_LENGTH # prevent DoS attack
37
- hash[key] = nfc_one(key)
38
- end
39
-
40
- ## Constants For Hangul
41
- # for details such as the meaning of the identifiers below, please see
42
- # http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf, pp. 144/145
43
- SBASE = 0xAC00
44
- LBASE = 0x1100
45
- VBASE = 0x1161
46
- TBASE = 0x11A7
47
- LCOUNT = 19
48
- VCOUNT = 21
49
- TCOUNT = 28
50
- NCOUNT = VCOUNT * TCOUNT
51
- SCOUNT = LCOUNT * NCOUNT
52
-
53
- # Unicode-based encodings (except UTF-8)
54
- UNICODE_ENCODINGS = [ Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE,
55
- Encoding::GB18030, Encoding::UCS_2BE, Encoding::UCS_4BE ]
56
-
57
- ## Hangul Algorithm
58
- def self.hangul_decomp_one(target)
59
- syllable_index = target.ord - SBASE
60
- return target if syllable_index < 0 || syllable_index >= SCOUNT
61
-
62
- l = LBASE + (syllable_index / NCOUNT)
63
- v = VBASE + ((syllable_index % NCOUNT) / TCOUNT)
64
- t = TBASE + (syllable_index % TCOUNT)
65
- ((t == TBASE) ? [ l, v ] : [ l, v, t ]).pack('U*') + target[1..-1]
66
- end
67
-
68
- def self.hangul_comp_one(string)
69
- length = string.length
70
- if length > 1 and 0 <= (lead = string[0].ord - LBASE) and lead < LCOUNT and
71
- 0 <= (vowel = string[1].ord - VBASE) and vowel < VCOUNT
72
- lead_vowel = SBASE + (((lead * VCOUNT) + vowel) * TCOUNT)
73
- if length > 2 and 0 <= (trail = string[2].ord - TBASE) and trail < TCOUNT
74
- (lead_vowel + trail).chr(Encoding::UTF_8) + string[3..-1]
75
- else
76
- lead_vowel.chr(Encoding::UTF_8) + string[2..-1]
77
- end
78
- else
79
- string
80
- end
81
- end
82
-
83
- ## Canonical Ordering
84
- def self.canonical_ordering_one(string)
85
- sorting = string.each_char.collect { |c| [ c, CLASS_TABLE[c] ] }
86
- (sorting.length - 2).downto(0) do |i| # almost, but not exactly bubble sort
87
- (0..i).each do |j|
88
- later_class = sorting[j + 1].last
89
- sorting[j], sorting[j + 1] = sorting[j + 1], sorting[j] if 0 < later_class and later_class < sorting[j].last
90
- end
91
- end
92
- sorting.collect(&:first).join('')
93
- end
94
-
95
- ## Normalization Forms for Patterns (not whole Strings)
96
- def self.nfd_one(string)
97
- string = string.chars.map { |c| DECOMPOSITION_TABLE[c] || c }.join('')
98
- canonical_ordering_one(hangul_decomp_one(string))
99
- end
100
-
101
- def self.nfc_one(string)
102
- nfd_string = nfd_one string
103
- start = nfd_string[0]
104
- last_class = CLASS_TABLE[start] - 1
105
- accents = ''
106
- nfd_string[1..-1].each_char do |accent|
107
- accent_class = CLASS_TABLE[accent]
108
- if last_class < accent_class and composite = COMPOSITION_TABLE[start + accent]
109
- start = composite
110
- else
111
- accents << accent
112
- last_class = accent_class
113
- end
114
- end
115
- hangul_comp_one(start + accents)
116
- end
117
-
118
- def self.normalize(string, form = :nfc)
119
- encoding = string.encoding
120
- case encoding
121
- when Encoding::UTF_8
122
- case form
123
- when :nfc
124
- string.gsub REGEXP_C, NF_HASH_C
125
- when :nfd
126
- string.gsub REGEXP_D, NF_HASH_D
127
- when :nfkc
128
- string.gsub(REGEXP_K, KOMPATIBLE_TABLE).gsub(REGEXP_C, NF_HASH_C)
129
- when :nfkd
130
- string.gsub(REGEXP_K, KOMPATIBLE_TABLE).gsub(REGEXP_D, NF_HASH_D)
131
- else
132
- raise ArgumentError, "Invalid normalization form #{form}."
133
- end
134
- when Encoding::US_ASCII
135
- string
136
- when *UNICODE_ENCODINGS
137
- normalize(string.encode(Encoding::UTF_8), form).encode(encoding)
138
- else
139
- raise Encoding::CompatibilityError, "Unicode Normalization not appropriate for #{encoding}"
140
- end
141
- end
142
-
143
- def self.normalized?(string, form = :nfc)
144
- encoding = string.encoding
145
- case encoding
146
- when Encoding::UTF_8
147
- case form
148
- when :nfc
149
- string.scan REGEXP_C do |match|
150
- return false if NF_HASH_C[match] != match
151
- end
152
- true
153
- when :nfd
154
- string.scan REGEXP_D do |match|
155
- return false if NF_HASH_D[match] != match
156
- end
157
- true
158
- when :nfkc
159
- normalized?(string, :nfc) and string !~ REGEXP_K
160
- when :nfkd
161
- normalized?(string, :nfd) and string !~ REGEXP_K
162
- else
163
- raise ArgumentError, "Invalid normalization form #{form}."
164
- end
165
- when Encoding::US_ASCII
166
- true
167
- when *UNICODE_ENCODINGS
168
- normalized? string.encode(Encoding::UTF_8), form
169
- else
170
- raise Encoding::CompatibilityError, "Unicode Normalization not appropriate for #{encoding}"
171
- end
172
- end
173
- end # module