bson 5.0.0 → 5.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +91 -7
  3. data/Rakefile +63 -39
  4. data/ext/bson/read.c +18 -3
  5. data/ext/bson/util.c +5 -2
  6. data/ext/bson/write.c +4 -0
  7. data/lib/bson/array.rb +1 -1
  8. data/lib/bson/binary.rb +16 -1
  9. data/lib/bson/code.rb +1 -1
  10. data/lib/bson/code_with_scope.rb +1 -1
  11. data/lib/bson/db_pointer.rb +1 -1
  12. data/lib/bson/decimal128/builder.rb +1 -1
  13. data/lib/bson/decimal128.rb +1 -1
  14. data/lib/bson/ext_json.rb +1 -1
  15. data/lib/bson/float.rb +1 -1
  16. data/lib/bson/hash.rb +1 -1
  17. data/lib/bson/int32.rb +1 -1
  18. data/lib/bson/int64.rb +1 -1
  19. data/lib/bson/integer.rb +1 -1
  20. data/lib/bson/max_key.rb +1 -1
  21. data/lib/bson/min_key.rb +1 -1
  22. data/lib/bson/object.rb +2 -2
  23. data/lib/bson/object_id.rb +12 -2
  24. data/lib/bson/regexp.rb +1 -1
  25. data/lib/bson/symbol.rb +2 -2
  26. data/lib/bson/time.rb +1 -1
  27. data/lib/bson/timestamp.rb +1 -1
  28. data/lib/bson/undefined.rb +1 -1
  29. data/lib/bson/version.rb +2 -1
  30. data/spec/bson/binary_spec.rb +46 -7
  31. data/spec/bson/object_id_spec.rb +14 -0
  32. metadata +7 -80
  33. checksums.yaml.gz.sig +0 -0
  34. data/spec/shared/LICENSE +0 -20
  35. data/spec/shared/bin/get-mongodb-download-url +0 -17
  36. data/spec/shared/bin/s3-copy +0 -45
  37. data/spec/shared/bin/s3-upload +0 -69
  38. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  39. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  40. data/spec/shared/lib/mrss/constraints.rb +0 -378
  41. data/spec/shared/lib/mrss/docker_runner.rb +0 -298
  42. data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
  43. data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
  44. data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
  45. data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
  46. data/spec/shared/lib/mrss/session_registry.rb +0 -69
  47. data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
  48. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  49. data/spec/shared/lib/mrss/utils.rb +0 -37
  50. data/spec/shared/share/Dockerfile.erb +0 -321
  51. data/spec/shared/share/haproxy-1.conf +0 -16
  52. data/spec/shared/share/haproxy-2.conf +0 -17
  53. data/spec/shared/shlib/config.sh +0 -27
  54. data/spec/shared/shlib/distro.sh +0 -74
  55. data/spec/shared/shlib/server.sh +0 -416
  56. data/spec/shared/shlib/set_env.sh +0 -169
  57. data.tar.gz.sig +0 -0
  58. metadata.gz.sig +0 -2
@@ -1,238 +0,0 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
- module Mrss
5
- module LiteConstraints
6
-
7
- # Constrain tests that use TimeoutInterrupt to MRI (and Unix).
8
- def require_mri
9
- before(:all) do
10
- unless SpecConfig.instance.mri?
11
- skip "MRI required, we have #{SpecConfig.instance.platform}"
12
- end
13
- end
14
- end
15
-
16
- def require_jruby
17
- before(:all) do
18
- unless BSON::Environment.jruby?
19
- skip "JRuby required, we have #{SpecConfig.instance.platform}"
20
- end
21
- end
22
- end
23
-
24
- # This is for marking tests that fail on JRuby that should
25
- # in principle work (as opposed to being fundamentally incompatible
26
- # with JRuby).
27
- # Often times these failures happen only in Evergreen.
28
- def fails_on_jruby(version=nil)
29
- before(:all) do
30
- if BSON::Environment.jruby?
31
- if version
32
- min_parts = version.split('.').map(&:to_i)
33
- actual_parts = JRUBY_VERSION.split('.').map(&:to_i)[0...min_parts.length]
34
- actual = actual_parts.join('.')
35
- if actual <= version
36
- skip "Fails on jruby through #{version}"
37
- end
38
- else
39
- skip "Fails on jruby"
40
- end
41
- end
42
- end
43
- end
44
-
45
- # Indicates that the respective test uses the internet in some capacity,
46
- # for example the test resolves SRV DNS records.
47
- def require_external_connectivity
48
- before(:all) do
49
- if ENV['EXTERNAL_DISABLED']
50
- skip "Test requires external connectivity"
51
- end
52
- end
53
- end
54
-
55
- def require_mongo_kerberos
56
- before(:all) do
57
- # TODO Use a more generic environment variable name if/when
58
- # Mongoid tests get Kerberos configurations.
59
- unless %w(1 yes true).include?(ENV['MONGO_RUBY_DRIVER_KERBEROS']&.downcase)
60
- skip 'Set MONGO_RUBY_DRIVER_KERBEROS=1 in environment to run Kerberos unit tests'
61
- end
62
- require 'mongo_kerberos'
63
- end
64
- end
65
-
66
- def require_linting
67
- before(:all) do
68
- unless Mongo::Lint.enabled?
69
- skip "Linting is not enabled"
70
- end
71
- end
72
- end
73
-
74
- # Some tests will fail if linting is enabled:
75
- # 1. Tests that pass invalid options to client, etc. which the linter
76
- # rejects.
77
- # 2. Tests that set expectations on topologies, server descriptions, etc.
78
- # (since setting expectations requires mutating said objects, and when
79
- # linting is on those objects are frozen).
80
- def require_no_linting
81
- before(:all) do
82
- if Mongo::Lint.enabled?
83
- skip "Linting is enabled"
84
- end
85
- end
86
- end
87
-
88
- def require_libmongocrypt
89
- before(:all) do
90
- # If FLE is set in environment, the entire test run is supposed to
91
- # include FLE therefore run the FLE tests.
92
- if (ENV['LIBMONGOCRYPT_PATH'] || '').empty? && (ENV['FLE'] || '').empty?
93
- skip 'Test requires path to libmongocrypt to be specified in LIBMONGOCRYPT_PATH env variable'
94
- end
95
- end
96
- end
97
-
98
- def min_libmongocrypt_version(version)
99
- require_libmongocrypt
100
- before(:all) do
101
- actual_version = Utils.parse_version(Mongo::Crypt::Binding.mongocrypt_version(nil))
102
- min_version = Utils.parse_version(version)
103
- unless actual_version >= min_version
104
- skip "libmongocrypt version #{min_version} required, but version #{actual_version} is available"
105
- end
106
- end
107
- end
108
-
109
- def require_no_libmongocrypt
110
- before(:all) do
111
- if ENV['LIBMONGOCRYPT_PATH']
112
- skip 'Test requires libmongocrypt to not be configured'
113
- end
114
- end
115
- end
116
-
117
- def require_aws_auth
118
- before(:all) do
119
- unless (ENV['AUTH'] || '') =~ /^aws/
120
- skip 'This test requires AUTH=aws* and an appropriately configured runtime environment'
121
- end
122
- end
123
- end
124
-
125
- def require_ec2_host
126
- before(:all) do
127
- if $have_aws.nil?
128
- $have_aws = begin
129
- require 'open-uri'
130
- begin
131
- Timeout.timeout(3.81) do
132
- URI.parse('http://169.254.169.254/latest/meta-data/profile').open.read
133
- end
134
- true
135
- # When trying to use the EC2 metadata endpoint on ECS:
136
- # Errno::EINVAL: Failed to open TCP connection to 169.254.169.254:80 (Invalid argument - connect(2) for "169.254.169.254" port 80)
137
- rescue Timeout::Error, Errno::ETIMEDOUT, Errno::EINVAL, OpenURI::HTTPError => $aws_error
138
- false
139
- end
140
- end
141
- end
142
- unless $have_aws
143
- skip "EC2 instance metadata is not available - assuming not running on an EC2 instance: #{$aws_error.class}: #{$aws_error}"
144
- end
145
- end
146
- end
147
-
148
- def require_stress
149
- before(:all) do
150
- if !SpecConfig.instance.stress?
151
- skip 'Set STRESS=1 in environment to run stress tests'
152
- end
153
- end
154
- end
155
-
156
- def require_fork
157
- before(:all) do
158
- if !SpecConfig.instance.fork?
159
- skip 'Set FORK=1 in environment to run fork tests'
160
- end
161
- end
162
- end
163
-
164
- def require_ocsp
165
- before(:all) do
166
- if !SpecConfig.instance.ocsp?
167
- skip 'Set OCSP=1 in environment to run OCSP tests'
168
- end
169
- end
170
- end
171
-
172
- def require_ocsp_verifier
173
- before(:all) do
174
- if !SpecConfig.instance.ocsp_verifier?
175
- skip 'Set OCSP_VERIFIER=1 in environment to run OCSP verifier tests'
176
- end
177
- end
178
- end
179
-
180
- def require_ocsp_connectivity
181
- before(:all) do
182
- if !SpecConfig.instance.ocsp_connectivity?
183
- skip 'Set OCSP_CONNECTIVITY=pass or OCSP_CONNECTIVITY=fail in environment to run OCSP connectivity tests'
184
- end
185
- end
186
- end
187
-
188
- def require_active_support
189
- before(:all) do
190
- if !SpecConfig.instance.active_support?
191
- skip 'This test requires ActiveSupport; set WITH_ACTIVE_SUPPORT=1 in environment'
192
- end
193
- end
194
- end
195
-
196
- def no_active_support
197
- before(:all) do
198
- if SpecConfig.instance.active_support?
199
- skip 'This test requires no ActiveSupport; unset WITH_ACTIVE_SUPPORT in environment'
200
- end
201
- end
202
- end
203
-
204
- def require_fallbacks
205
- before(:all) do
206
- unless %w(yes true 1).include?((ENV['TEST_I18N_FALLBACKS'] || '').downcase)
207
- skip 'Set TEST_I18N_FALLBACKS=1 environment variable to run these tests'
208
- end
209
- end
210
- end
211
-
212
- def require_no_fallbacks
213
- before(:all) do
214
- if %w(yes true 1).include?((ENV['TEST_I18N_FALLBACKS'] || '').downcase)
215
- skip 'Set TEST_I18N_FALLBACKS=0 environment variable to run these tests'
216
- end
217
- end
218
- end
219
-
220
- # This is a macro for retrying flaky tests on CI that occasionally fail.
221
- # Note that the tests will only be retried on CI.
222
- #
223
- # @param [ Integer ] :tries The number of times to retry.
224
- # @param [ Integer ] :sleep The number of seconds to sleep in between retries.
225
- # If nothing, or nil, is passed, we won't wait in between retries.
226
- def retry_test(tries: 3, sleep: nil)
227
- if %w(1 yes true).include?(ENV['CI'])
228
- around do |example|
229
- if sleep
230
- example.run_with_retry retry: tries, retry_wait: sleep
231
- else
232
- example.run_with_retry retry: tries
233
- end
234
- end
235
- end
236
- end
237
- end
238
- end
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
- autoload :JSON, 'json'
5
- require 'open-uri'
6
-
7
- module Mrss
8
- class ServerVersionRegistry
9
- class Error < StandardError
10
- end
11
-
12
- class UnknownVersion < Error
13
- end
14
-
15
- class MissingDownloadUrl < Error
16
- end
17
-
18
- class BrokenDownloadUrl < Error
19
- end
20
-
21
- def initialize(desired_version, arch)
22
- @desired_version, @arch = desired_version, arch
23
- end
24
-
25
- attr_reader :desired_version, :arch
26
-
27
- def target_arch
28
- # can't use RbConfig::CONFIG["arch"] because JRuby doesn't
29
- # return anything meaningful there.
30
- #
31
- # also, need to use `uname -a` instead of (e.g.) `uname -p`
32
- # because debian (at least) does not return anything meaningful
33
- # for `uname -p`.
34
- uname = `uname -a`.strip
35
- @target_arch ||= case uname
36
- when /aarch/ then "aarch64"
37
- when /x86/ then "x86_64"
38
- else raise "unsupported architecture #{uname.inspect}"
39
- end
40
- end
41
-
42
- def download_url
43
- @download_url ||= begin
44
- version, version_ok = detect_version(current_catalog)
45
- if version.nil?
46
- version, full_version_ok = detect_version(full_catalog)
47
- version_ok ||= full_version_ok
48
- end
49
- if version.nil?
50
- if version_ok
51
- raise MissingDownloadUrl, "No downloads for version #{desired_version}"
52
- else
53
- raise UnknownVersion, "No version #{desired_version}"
54
- end
55
- end
56
- dl = version['downloads'].detect do |dl|
57
- dl['archive']['url'].index("enterprise-#{arch}") &&
58
- dl['arch'] == target_arch
59
- end
60
- unless dl
61
- raise MissingDownloadUrl, "No download for #{arch} for #{version['version']}"
62
- end
63
- url = dl['archive']['url']
64
- end
65
- end
66
-
67
- private
68
-
69
- def uri_open(*args)
70
- if RUBY_VERSION < '2.5'
71
- open(*args)
72
- else
73
- URI.open(*args)
74
- end
75
- end
76
-
77
- def detect_version(catalog)
78
- candidate_versions = catalog['versions'].select do |version|
79
- version['version'].start_with?(desired_version) &&
80
- !version['version'].include?('-')
81
- end
82
- version_ok = !candidate_versions.empty?
83
- # Sometimes the download situation is borked and there is a release
84
- # with no downloads... skip those.
85
- version = candidate_versions.detect do |version|
86
- !version['downloads'].empty?
87
- end
88
- # Allow RC releases if there isn't a GA release.
89
- if version.nil?
90
- candidate_versions = catalog['versions'].select do |version|
91
- version['version'].start_with?(desired_version)
92
- end
93
- version_ok ||= !candidate_versions.empty?
94
- version = candidate_versions.detect do |version|
95
- !version['downloads'].empty?
96
- end
97
- end
98
- [version, version_ok]
99
- end
100
-
101
- def current_catalog
102
- @current_catalog ||= begin
103
- JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
104
- end
105
- end
106
-
107
- def full_catalog
108
- @full_catalog ||= begin
109
- JSON.load(uri_open('http://downloads.mongodb.org/full.json').read)
110
- end
111
- end
112
- end
113
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
- require 'singleton'
5
-
6
- module Mrss
7
-
8
- def self.patch_mongo_for_session_registry
9
-
10
- Mongo::Client.class_eval do
11
- alias :get_session_without_tracking :get_session
12
-
13
- def get_session(options = {})
14
- get_session_without_tracking(options).tap do |session|
15
- SessionRegistry.instance.register(session) if session&.materialized?
16
- end
17
- end
18
- end
19
-
20
- Mongo::Session.class_eval do
21
- alias :end_session_without_tracking :end_session
22
-
23
- def end_session
24
- SessionRegistry.instance.unregister(self)
25
- end_session_without_tracking
26
- end
27
-
28
- alias :materialize_if_needed_without_tracking :materialize_if_needed
29
-
30
- def materialize_if_needed
31
- materialize_if_needed_without_tracking.tap do
32
- SessionRegistry.instance.register(self)
33
- end
34
- end
35
- end
36
- end
37
- end
38
-
39
- module Mrss
40
- class SessionRegistry
41
- include Singleton
42
-
43
- def initialize
44
- @registry = {}
45
- end
46
-
47
- def register(session)
48
- @registry[session.session_id] = session if session
49
- end
50
-
51
- def unregister(session)
52
- return if session.ended? || !session.materialized?
53
- @registry.delete(session.session_id)
54
- end
55
-
56
- def verify_sessions_ended!
57
- @registry.delete_if { |_, session| session.ended? }
58
-
59
- unless @registry.empty?
60
- sessions = @registry.map { |_, session| session }
61
- raise "Session registry contains live sessions: #{sessions.join(', ')}"
62
- end
63
- end
64
-
65
- def clear_registry
66
- @registry = {}
67
- end
68
- end
69
- end
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
- require 'singleton'
5
-
6
- module Mrss
7
-
8
- def self.patch_mongo_for_session_registry
9
-
10
- Mongo::Client.class_eval do
11
- alias :get_session_without_tracking :get_session
12
-
13
- def get_session(options = {})
14
- get_session_without_tracking(options).tap do |session|
15
- SessionRegistry.instance.register(session)
16
- end
17
- end
18
- end
19
-
20
- Mongo::Session.class_eval do
21
- alias :end_session_without_tracking :end_session
22
-
23
- def end_session
24
- SessionRegistry.instance.unregister(self)
25
- end_session_without_tracking
26
- end
27
- end
28
- end
29
- end
30
-
31
- module Mrss
32
- class SessionRegistry
33
- include Singleton
34
-
35
- def initialize
36
- @registry = {}
37
- end
38
-
39
- def register(session)
40
- @registry[session.session_id] = session if session
41
- end
42
-
43
- def unregister(session)
44
- @registry.delete(session.session_id) unless session.ended?
45
- end
46
-
47
- def verify_sessions_ended!
48
- @registry.delete_if { |_, session| session.ended? }
49
-
50
- unless @registry.empty?
51
- sessions = @registry.map { |_, session| session }
52
- raise "Session registry contains live sessions: #{sessions.join(', ')}"
53
- end
54
- end
55
-
56
- def clear_registry
57
- @registry = {}
58
- end
59
- end
60
- end
@@ -1,179 +0,0 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
- autoload :JSON, 'json'
5
- autoload :FileUtils, 'fileutils'
6
- autoload :Find, 'find'
7
-
8
- module Mrss
9
-
10
- autoload :ChildProcessHelper, 'mrss/child_process_helper'
11
-
12
- # Organizes and runs all of the tests in the test suite in batches.
13
- #
14
- # Organizing the tests in batches serves two purposes:
15
- #
16
- # 1. This allows running unit tests before integration tests, therefore
17
- # in theory revealing failures quicker on average.
18
- # 2. This allows running some tests that have high intermittent failure rate
19
- # in their own test process.
20
- #
21
- # This class aggregates RSpec results after the test runs.
22
- class SpecOrganizer
23
-
24
- class BucketsNotPrioritized < StandardError
25
- end
26
-
27
- def initialize(root: nil, classifiers:, priority_order:,
28
- spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil,
29
- randomize: false
30
- )
31
- @spec_root = spec_root || File.join(root, 'spec')
32
- @classifiers = classifiers
33
- @priority_order = priority_order
34
- @rspec_json_path = rspec_json_path || File.join(root, 'tmp/rspec.json')
35
- @rspec_all_json_path = rspec_all_json_path || File.join(root, 'tmp/rspec-all.json')
36
- @randomize = !!randomize
37
- end
38
-
39
- attr_reader :spec_root, :classifiers, :priority_order
40
- attr_reader :rspec_json_path, :rspec_all_json_path
41
-
42
- def randomize?
43
- @randomize
44
- end
45
-
46
- def seed
47
- @seed ||= (rand * 100_000).to_i
48
- end
49
-
50
- def buckets
51
- @buckets ||= {}.tap do |buckets|
52
- Find.find(spec_root) do |path|
53
- next unless File.file?(path)
54
- next unless path =~ /_spec\.rb\z/
55
- rel_path = path[(spec_root.length + 1)..path.length]
56
-
57
- found = false
58
- classifiers.each do |(regexp, category)|
59
- if regexp =~ rel_path
60
- buckets[category] ||= []
61
- buckets[category] << File.join('spec', rel_path)
62
- found = true
63
- break
64
- end
65
- end
66
-
67
- unless found
68
- buckets[nil] ||= []
69
- buckets[nil] << File.join('spec', rel_path)
70
- end
71
- end
72
- end.freeze
73
- end
74
-
75
- def ordered_buckets
76
- @ordered_buckets ||= {}.tap do |ordered_buckets|
77
- buckets = self.buckets.dup
78
- priority_order.each do |category|
79
- files = buckets.delete(category)
80
- ordered_buckets[category] = files
81
- end
82
-
83
- if files = buckets.delete(nil)
84
- ordered_buckets[nil] = files
85
- end
86
-
87
- unless buckets.empty?
88
- raise BucketsNotPrioritized, "Some buckets were not prioritized: #{buckets.keys.map(&:to_s).join(', ')}"
89
- end
90
- end.freeze
91
- end
92
-
93
- def run
94
- run_buckets(*buckets.keys)
95
- end
96
-
97
- def run_buckets(*buckets)
98
- FileUtils.rm_f(rspec_all_json_path)
99
-
100
- buckets.each do |bucket|
101
- if bucket && !self.buckets[bucket]
102
- raise "Unknown bucket #{bucket}"
103
- end
104
- end
105
- buckets = Hash[self.buckets.select { |k, v| buckets.include?(k) }]
106
-
107
- failed = []
108
-
109
- priority_order.each do |category|
110
- if files = buckets.delete(category)
111
- unless run_files(category, files)
112
- failed << category
113
- end
114
- end
115
- end
116
- if files = buckets.delete(nil)
117
- unless run_files('remaining', files)
118
- failed << 'remaining'
119
- end
120
- end
121
-
122
- unless buckets.empty?
123
- raise "Some buckets were not executed: #{buckets.keys.map(&:to_s).join(', ')}"
124
- end
125
-
126
- if failed.any?
127
- raise "The following buckets failed: #{failed.map(&:to_s).join(', ')}"
128
- end
129
- end
130
-
131
- def run_files(category, paths)
132
- puts "Running #{category.to_s.gsub('_', ' ')} tests"
133
- FileUtils.rm_f(rspec_json_path)
134
- cmd = %w(rspec) + paths
135
- if randomize?
136
- cmd += %W(--order rand:#{seed})
137
- end
138
-
139
- begin
140
- puts "Running #{cmd.join(' ')}"
141
- ChildProcessHelper.check_call(cmd)
142
- ensure
143
- if File.exist?(rspec_json_path)
144
- if File.exist?(rspec_all_json_path)
145
- merge_rspec_results
146
- else
147
- FileUtils.cp(rspec_json_path, rspec_all_json_path)
148
- end
149
- end
150
- end
151
-
152
- true
153
- rescue ChildProcessHelper::SpawnError
154
- false
155
- end
156
-
157
- def merge_rspec_results
158
- all = JSON.parse(File.read(rspec_all_json_path))
159
- new = JSON.parse(File.read(rspec_json_path))
160
- all['examples'] += new.delete('examples')
161
- new.delete('summary').each do |k, v|
162
- all['summary'][k] += v
163
- end
164
- new.delete('version')
165
- new.delete('summary_line')
166
- # The spec organizer runs all buckets with the same seed, hence
167
- # we can drop the seed from new results.
168
- new.delete('seed')
169
- unless new.empty?
170
- raise "Unhandled rspec results keys: #{new.keys.join(', ')}"
171
- end
172
- # We do not merge summary lines, delete them from aggregated results
173
- all.delete('summary_line')
174
- File.open(rspec_all_json_path, 'w') do |f|
175
- f << JSON.dump(all)
176
- end
177
- end
178
- end
179
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
- # encoding: utf-8
3
-
4
- module Mrss
5
- module Utils
6
- extend self
7
-
8
- def print_backtrace(dest=STDERR)
9
- raise
10
- rescue => e
11
- dest.puts e.backtrace.join("\n")
12
- end
13
-
14
- # Parses the given version string, accounting for suffix information that
15
- # Gem::Version cannot successfully parse.
16
- #
17
- # @param [ String ] version the version to parse
18
- #
19
- # @return [ Gem::Version ] the parsed version
20
- #
21
- # @raise [ ArgumentError ] if the string cannot be parsed.
22
- def parse_version(version)
23
- Gem::Version.new(version)
24
- rescue ArgumentError
25
- match = version.match(/\A(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?(-[A-Za-z\+\d]+)?\z/)
26
- raise ArgumentError.new("Malformed version number string #{version}") if match.nil?
27
-
28
- Gem::Version.new(
29
- [
30
- match[:major],
31
- match[:minor],
32
- match[:patch]
33
- ].join('.')
34
- )
35
- end
36
- end
37
- end