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.
- checksums.yaml +4 -4
- data/bin/mongo_console +2 -2
- data/lib/mongo/address/validator.rb +1 -1
- data/lib/mongo/address.rb +1 -3
- data/lib/mongo/auth/aws/conversation.rb +0 -4
- data/lib/mongo/auth/aws/credentials_retriever.rb +3 -8
- data/lib/mongo/auth/base.rb +1 -1
- data/lib/mongo/auth/sasl_conversation_base.rb +1 -1
- data/lib/mongo/auth/scram_conversation_base.rb +1 -8
- data/lib/mongo/auth/stringprep.rb +1 -6
- data/lib/mongo/auth/user/view.rb +1 -1
- data/lib/mongo/bulk_write/result.rb +10 -2
- data/lib/mongo/bulk_write/result_combiner.rb +11 -4
- data/lib/mongo/bulk_write.rb +1 -1
- data/lib/mongo/client.rb +82 -12
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +12 -5
- data/lib/mongo/cluster/sdam_flow.rb +4 -4
- data/lib/mongo/cluster/topology/base.rb +1 -1
- data/lib/mongo/cluster.rb +12 -28
- data/lib/mongo/collection/view/aggregation/behavior.rb +1 -1
- data/lib/mongo/collection/view/aggregation.rb +2 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +3 -5
- data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +14 -9
- data/lib/mongo/collection/view/map_reduce.rb +2 -11
- data/lib/mongo/collection/view/readable.rb +11 -7
- data/lib/mongo/collection/view/writable.rb +4 -4
- data/lib/mongo/collection/view.rb +8 -4
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/config.rb +5 -0
- data/lib/mongo/crypt/auto_encrypter.rb +1 -1
- data/lib/mongo/crypt/binary.rb +7 -4
- data/lib/mongo/crypt/binding.rb +88 -10
- data/lib/mongo/crypt/context.rb +50 -19
- data/lib/mongo/crypt/encryption_io.rb +11 -5
- data/lib/mongo/crypt/handle.rb +17 -5
- data/lib/mongo/crypt/kms/credentials.rb +70 -31
- data/lib/mongo/crypt/kms/kmip/master_document.rb +30 -1
- data/lib/mongo/crypt/kms/master_key_document.rb +11 -6
- data/lib/mongo/crypt/kms.rb +12 -1
- data/lib/mongo/cursor.rb +8 -2
- data/lib/mongo/database/cursor_command_view.rb +95 -0
- data/lib/mongo/database.rb +144 -18
- data/lib/mongo/error/bulk_write_error.rb +29 -1
- data/lib/mongo/error/invalid_server_preference.rb +3 -0
- data/lib/mongo/error/invalid_uri.rb +5 -1
- data/lib/mongo/error/operation_failure.rb +42 -3
- data/lib/mongo/error/parser.rb +3 -3
- data/lib/mongo/error/sdam_error_detection.rb +2 -3
- data/lib/mongo/grid/fs_bucket.rb +27 -21
- data/lib/mongo/grid/stream/write.rb +2 -2
- data/lib/mongo/index/view.rb +13 -7
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -0
- data/lib/mongo/monitoring/event/command_failed.rb +1 -0
- data/lib/mongo/monitoring/event/command_started.rb +1 -0
- data/lib/mongo/monitoring/event/command_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/secure.rb +12 -17
- data/lib/mongo/monitoring/event/server_closed.rb +1 -0
- data/lib/mongo/monitoring/event/server_description_changed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/server_opening.rb +1 -0
- data/lib/mongo/monitoring/event/topology_changed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_closed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_opening.rb +1 -0
- data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +2 -0
- data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +3 -0
- data/lib/mongo/operation/create/op_msg.rb +1 -0
- data/lib/mongo/operation/create_index/op_msg.rb +2 -1
- data/lib/mongo/operation/cursor_command/op_msg.rb +37 -0
- data/lib/mongo/operation/cursor_command/result.rb +60 -0
- data/lib/mongo/operation/cursor_command.rb +33 -0
- data/lib/mongo/operation/delete/op_msg.rb +1 -0
- data/lib/mongo/operation/drop/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_database/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_index/op_msg.rb +1 -0
- data/lib/mongo/operation/find/builder/command.rb +1 -1
- data/lib/mongo/operation/insert/bulk_result.rb +2 -4
- data/lib/mongo/operation/insert/op_msg.rb +1 -0
- data/lib/mongo/operation/result.rb +14 -26
- data/lib/mongo/operation/shared/causal_consistency_supported.rb +4 -2
- data/lib/mongo/operation/shared/executable.rb +2 -10
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +3 -5
- data/lib/mongo/operation/update/bulk_result.rb +1 -1
- data/lib/mongo/operation/update/op_msg.rb +1 -0
- data/lib/mongo/operation/write_command/op_msg.rb +2 -0
- data/lib/mongo/operation.rb +1 -0
- data/lib/mongo/options/redacted.rb +2 -2
- data/lib/mongo/protocol/compressed.rb +15 -11
- data/lib/mongo/protocol/message.rb +16 -16
- data/lib/mongo/protocol/registry.rb +1 -1
- data/lib/mongo/protocol/serializers.rb +1 -3
- data/lib/mongo/query_cache.rb +3 -5
- data/lib/mongo/server/connection.rb +3 -1
- data/lib/mongo/server/connection_common.rb +9 -1
- data/lib/mongo/server/connection_pool/generation_manager.rb +1 -1
- data/lib/mongo/server/connection_pool.rb +79 -18
- data/lib/mongo/server/description/features.rb +2 -2
- data/lib/mongo/server/description.rb +24 -2
- data/lib/mongo/server/monitor/connection.rb +3 -2
- data/lib/mongo/server/monitor.rb +133 -30
- data/lib/mongo/server/pending_connection.rb +1 -0
- data/lib/mongo/server/push_monitor.rb +16 -2
- data/lib/mongo/server.rb +27 -7
- data/lib/mongo/server_selector/base.rb +16 -6
- data/lib/mongo/server_selector/nearest.rb +3 -0
- data/lib/mongo/server_selector/primary.rb +3 -0
- data/lib/mongo/server_selector/primary_preferred.rb +3 -0
- data/lib/mongo/server_selector/secondary.rb +3 -0
- data/lib/mongo/server_selector/secondary_preferred.rb +7 -1
- data/lib/mongo/session.rb +56 -24
- data/lib/mongo/socket/ssl.rb +3 -3
- data/lib/mongo/socket.rb +1 -7
- data/lib/mongo/srv/monitor.rb +11 -2
- data/lib/mongo/srv/resolver.rb +1 -1
- data/lib/mongo/srv/result.rb +5 -3
- data/lib/mongo/timeout.rb +10 -15
- data/lib/mongo/tracing/open_telemetry/command_tracer.rb +43 -2
- data/lib/mongo/uri/options_mapper.rb +21 -9
- data/lib/mongo/uri/srv_protocol.rb +4 -4
- data/lib/mongo/uri.rb +56 -8
- data/lib/mongo/utils.rb +1 -1
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo.rb +1 -0
- metadata +6 -4
- data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +0 -173
- 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
|
|
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 += @
|
|
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
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.
|
|
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.
|
|
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
|