mongoid 7.2.1 → 7.2.5

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 (65) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +1 -1
  5. data/lib/mongoid.rb +1 -0
  6. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +1 -1
  7. data/lib/mongoid/association/proxy.rb +1 -1
  8. data/lib/mongoid/association/referenced/has_many/enumerable.rb +1 -1
  9. data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
  10. data/lib/mongoid/attributes.rb +8 -1
  11. data/lib/mongoid/criteria.rb +1 -1
  12. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  13. data/lib/mongoid/interceptable.rb +1 -1
  14. data/lib/mongoid/matcher.rb +19 -43
  15. data/lib/mongoid/matcher/elem_match.rb +2 -1
  16. data/lib/mongoid/matcher/expression.rb +5 -14
  17. data/lib/mongoid/matcher/field_expression.rb +4 -5
  18. data/lib/mongoid/reloadable.rb +5 -0
  19. data/lib/mongoid/validatable/associated.rb +1 -1
  20. data/lib/mongoid/validatable/presence.rb +3 -3
  21. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  22. data/lib/mongoid/version.rb +1 -1
  23. data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
  24. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
  25. data/spec/integration/app_spec.rb +139 -82
  26. data/spec/integration/document_spec.rb +21 -0
  27. data/spec/integration/matcher_operator_data/elem_match.yml +46 -0
  28. data/spec/integration/matcher_operator_data/implicit_traversal.yml +96 -0
  29. data/spec/lite_spec_helper.rb +2 -3
  30. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +17 -4
  31. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +17 -0
  32. data/spec/mongoid/attributes_spec.rb +241 -0
  33. data/spec/mongoid/clients/options_spec.rb +2 -0
  34. data/spec/mongoid/contextual/atomic_spec.rb +17 -4
  35. data/spec/mongoid/criteria_spec.rb +4 -0
  36. data/spec/mongoid/document_fields_spec.rb +26 -0
  37. data/spec/mongoid/document_query_spec.rb +51 -0
  38. data/spec/mongoid/errors/mongoid_error_spec.rb +20 -8
  39. data/spec/mongoid/matcher/extract_attribute_data/numeric_keys.yml +104 -0
  40. data/spec/mongoid/matcher/extract_attribute_data/traversal.yml +68 -88
  41. data/spec/mongoid/matcher/extract_attribute_spec.rb +3 -13
  42. data/spec/mongoid/persistable/settable_spec.rb +30 -0
  43. data/spec/mongoid/persistable_spec.rb +2 -2
  44. data/spec/shared/bin/get-mongodb-download-url +17 -0
  45. data/spec/shared/bin/s3-copy +45 -0
  46. data/spec/shared/bin/s3-upload +69 -0
  47. data/spec/shared/lib/mrss/cluster_config.rb +19 -4
  48. data/spec/shared/lib/mrss/constraints.rb +46 -8
  49. data/spec/shared/lib/mrss/docker_runner.rb +10 -1
  50. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  51. data/spec/shared/lib/mrss/server_version_registry.rb +79 -33
  52. data/spec/shared/lib/mrss/spec_organizer.rb +32 -2
  53. data/spec/shared/lib/mrss/utils.rb +15 -0
  54. data/spec/shared/share/Dockerfile.erb +122 -29
  55. data/spec/shared/share/haproxy-1.conf +16 -0
  56. data/spec/shared/share/haproxy-2.conf +17 -0
  57. data/spec/shared/shlib/server.sh +58 -11
  58. data/spec/shared/shlib/set_env.sh +4 -1
  59. data/spec/spec_helper.rb +1 -1
  60. data/spec/support/models/address.rb +4 -0
  61. data/spec/support/models/mop.rb +10 -0
  62. data/spec/support/models/person.rb +9 -0
  63. data/spec/support/spec_config.rb +8 -0
  64. metadata +555 -527
  65. metadata.gz.sig +0 -0
@@ -25,20 +25,10 @@ describe 'Matcher.extract_attribute' do
25
25
  Mongoid::Matcher.extract_attribute(document, key)
26
26
  end
27
27
 
28
- let(:expected_exists) { spec['exists'] }
29
- let(:expected_value) { spec['value'] }
30
- let(:expected_expanded) { spec['expanded'] }
28
+ let(:expected) { spec.fetch('result') }
31
29
 
32
- it 'has the expected exists flag' do
33
- actual[0].should == expected_exists
34
- end
35
-
36
- it 'has the expected value' do
37
- actual[1].should == expected_value
38
- end
39
-
40
- it 'has the expected expanded flag' do
41
- actual[2].should == expected_expanded
30
+ it 'has the expected result' do
31
+ actual.should == expected
42
32
  end
43
33
  end
44
34
  end
@@ -512,4 +512,34 @@ describe Mongoid::Persistable::Settable do
512
512
  end
513
513
  end
514
514
  end
515
+
516
+ context "when the field being set was projected out" do
517
+ let(:full_agent) do
518
+ Agent.create!(title: "Double-Oh Eight")
519
+ end
520
+
521
+ let(:agent) do
522
+ Agent.where(_id: full_agent.id).only(:dob).first
523
+ end
524
+
525
+ context 'field exists in database' do
526
+ it "raises MissingAttributeError" do
527
+ lambda do
528
+ agent.set(title: '008')
529
+ end.should raise_error(ActiveModel::MissingAttributeError)
530
+
531
+ expect(agent.reload.title).to eq 'Double-Oh Eight'
532
+ end
533
+ end
534
+
535
+ context 'field does not exist in database' do
536
+ it "raises MissingAttributeError" do
537
+ lambda do
538
+ agent.set(number: '008')
539
+ end.should raise_error(ActiveModel::MissingAttributeError)
540
+
541
+ expect(agent.reload.read_attribute(:number)).to be nil
542
+ end
543
+ end
544
+ end
515
545
  end
@@ -178,8 +178,8 @@ describe Mongoid::Persistable do
178
178
 
179
179
  before do
180
180
  class Band
181
- def my_updates(*args)
182
- atomically(*args) do |d|
181
+ def my_updates(**args)
182
+ atomically(**args) do |d|
183
183
  d.set(name: "Placebo")
184
184
  d.unset(:origin)
185
185
  end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ desired_version, arch = ARGV
4
+ if arch.nil?
5
+ STDERR.puts "Usage: get-mongodb-download-url desired-version arch"
6
+ exit 1
7
+ end
8
+
9
+ $: << File.join(File.dirname(__FILE__), '../lib')
10
+ require 'mrss/server_version_registry'
11
+
12
+ begin
13
+ puts Mrss::ServerVersionRegistry.new(desired_version, arch).download_url
14
+ rescue Mrss::ServerVersionRegistry::Error => exc
15
+ STDERR.puts "Error: #{exc}"
16
+ exit 2
17
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-copy options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for new files") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--from=BUCKET:PATH", "Bucket name and key (or path) to copy from") do |v|
21
+ options[:from] = v
22
+ end
23
+
24
+ opts.on("-t", "--to=BUCKET:PATH", "Bucket name and key (or path) to write to (may be specified more than once)") do |v|
25
+ options[:to] ||= []
26
+ options[:to] << v
27
+ end
28
+ end.parse!
29
+
30
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
31
+
32
+ bucket, key = options.fetch(:from).split(':', 2)
33
+
34
+ s3 = Aws::S3::Client.new
35
+
36
+ options.fetch(:to).each do |dest|
37
+ STDERR.puts "Copying to #{dest}"
38
+ dbucket, dkey = dest.split(':', 2)
39
+ s3.copy_object(
40
+ bucket: dbucket,
41
+ key: dkey,
42
+ copy_source: "/#{bucket}/#{key}",
43
+ **options[:params] || {},
44
+ )
45
+ end
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-upload options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for S3 upload") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--file=PATH", "Path to the file to upload, - to upload standard input") do |v|
21
+ options[:file] = v
22
+ end
23
+
24
+ opts.on("-w", "--write=BUCKET:PATH", "Bucket name and key (or path) to upload to") do |v|
25
+ options[:write] = v
26
+ end
27
+
28
+ opts.on("-c", "--copy=BUCKET:PATH", "Bucket name and key (or path) to copy to (may be specified more than once)") do |v|
29
+ options[:copy] ||= []
30
+ options[:copy] << v
31
+ end
32
+ end.parse!
33
+
34
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
35
+
36
+ def upload(f, options)
37
+ s3 = Aws::S3::Client.new
38
+ write = options.fetch(:write)
39
+ STDERR.puts "Writing #{write}"
40
+ bucket, key = write.split(':', 2)
41
+ s3.put_object(
42
+ body: f.read,
43
+ bucket: bucket,
44
+ key: key,
45
+ **options[:params] || {},
46
+ )
47
+ if copy = options[:copy]
48
+ copy.each do |dest|
49
+ STDERR.puts "Copying to #{dest}"
50
+ dbucket, dkey = dest.split(':', 2)
51
+ s3.copy_object(
52
+ bucket: dbucket,
53
+ key: dkey,
54
+ copy_source: "/#{bucket}/#{key}",
55
+ **options[:params] || {},
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ if options[:file] == '-'
62
+ upload(STDIN, options)
63
+ elsif options[:file]
64
+ File.open(options[:file]) do |f|
65
+ upload(f, options)
66
+ end
67
+ else
68
+ upload(STDIN, options)
69
+ end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  # ClusterConfig requires ClientRegistry class provided by the host project.
2
5
 
3
6
  require 'singleton'
@@ -12,6 +15,11 @@ module Mrss
12
15
  @single_server
13
16
  end
14
17
 
18
+ def sharded_ish?
19
+ determine_cluster_config
20
+ @topology == :sharded || @topology == :load_balanced
21
+ end
22
+
15
23
  def replica_set_name
16
24
  determine_cluster_config
17
25
  @replica_set_name
@@ -45,7 +53,7 @@ module Mrss
45
53
  raise "Deployment server version not known - check that connection to deployment succeeded"
46
54
  end
47
55
 
48
- if server_version >= '3.4' && topology != :sharded
56
+ if server_version >= '3.4' && !sharded_ish?
49
57
  fcv
50
58
  else
51
59
  if short_server_version == '4.1'
@@ -82,6 +90,11 @@ module Mrss
82
90
  @primary_description
83
91
  end
84
92
 
93
+ def server_parameters
94
+ determine_cluster_config
95
+ @server_parameters
96
+ end
97
+
85
98
  # Try running a command on the admin database to see if the mongod was
86
99
  # started with auth.
87
100
  def auth_enabled?
@@ -107,7 +120,7 @@ module Mrss
107
120
  :mmapv1
108
121
  else
109
122
  client = ClientRegistry.instance.global_client('root_authorized')
110
- if topology == :sharded
123
+ if sharded_ish?
111
124
  shards = client.use(:admin).command(listShards: 1).first
112
125
  if shards['shards'].empty?
113
126
  raise 'Shards are empty'
@@ -196,8 +209,10 @@ module Mrss
196
209
  @server_version = build_info['version']
197
210
  @enterprise = build_info['modules'] && build_info['modules'].include?('enterprise')
198
211
 
199
- if @topology != :sharded && short_server_version >= '3.4'
200
- rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
212
+ @server_parameters = client.use(:admin).command(getParameter: '*').first
213
+
214
+ if !sharded_ish? && short_server_version >= '3.4'
215
+ rv = @server_parameters['featureCompatibilityVersion']
201
216
  @fcv = rv['version'] || rv
202
217
  end
203
218
  end
@@ -52,7 +52,7 @@ module Mrss
52
52
  end
53
53
 
54
54
  def require_topology(*topologies)
55
- invalid_topologies = topologies - [:single, :replica_set, :sharded]
55
+ invalid_topologies = topologies - [:single, :replica_set, :sharded, :load_balanced]
56
56
 
57
57
  unless invalid_topologies.empty?
58
58
  raise ArgumentError, "Invalid topologies requested: #{invalid_topologies.join(', ')}"
@@ -82,7 +82,7 @@ module Mrss
82
82
  unless ClusterConfig.instance.server_version >= '4.0'
83
83
  skip 'Transactions tests in a replica set topology require server 4.0+'
84
84
  end
85
- when :sharded
85
+ when :sharded, :load_balanced
86
86
  unless ClusterConfig.instance.server_version >= '4.2'
87
87
  skip 'Transactions tests in a sharded cluster topology require server 4.2+'
88
88
  end
@@ -113,6 +113,14 @@ module Mrss
113
113
  end
114
114
  end
115
115
 
116
+ def require_retry_writes
117
+ before(:all) do
118
+ unless SpecConfig.instance.retry_writes?
119
+ skip "Retry writes is disabled"
120
+ end
121
+ end
122
+ end
123
+
116
124
  def require_no_retry_writes
117
125
  before(:all) do
118
126
  if SpecConfig.instance.retry_writes?
@@ -172,8 +180,8 @@ module Mrss
172
180
  skip "Zstd compression is enabled"
173
181
  end
174
182
  end
175
- end
176
-
183
+ end
184
+
177
185
  def require_no_compression
178
186
  before(:all) do
179
187
  if SpecConfig.instance.compressors
@@ -241,7 +249,9 @@ module Mrss
241
249
  # (mongos 4.0+ overrides the write concern)
242
250
  def require_set_write_concern
243
251
  before(:all) do
244
- if ClusterConfig.instance.topology == :sharded && ClusterConfig.instance.short_server_version >= '4.0'
252
+ if %i(sharded load_balanced).include?(ClusterConfig.instance.topology) &&
253
+ ClusterConfig.instance.short_server_version >= '4.0'
254
+ then
245
255
  skip "mongos 4.0+ overrides write concern"
246
256
  end
247
257
  end
@@ -265,7 +275,9 @@ module Mrss
265
275
 
266
276
  def require_wired_tiger
267
277
  before(:all) do
268
- if ClusterConfig.instance.storage_engine != :wired_tiger
278
+ # Storage detection fails for serverless instances. However, it is safe to
279
+ # assume that a serverless instance uses WiredTiger Storage Engine.
280
+ if !SpecConfig.instance.serverless? && ClusterConfig.instance.storage_engine != :wired_tiger
269
281
  skip 'Test requires WiredTiger storage engine'
270
282
  end
271
283
  end
@@ -274,7 +286,9 @@ module Mrss
274
286
  def require_wired_tiger_on_36
275
287
  before(:all) do
276
288
  if ClusterConfig.instance.short_server_version >= '3.6'
277
- if ClusterConfig.instance.storage_engine != :wired_tiger
289
+ # Storage detection fails for serverless instances. However, it is safe to
290
+ # assume that a serverless instance uses WiredTiger Storage Engine.
291
+ if !SpecConfig.instance.serverless? && ClusterConfig.instance.storage_engine != :wired_tiger
278
292
  skip 'Test requires WiredTiger storage engine on 3.6+ servers'
279
293
  end
280
294
  end
@@ -283,7 +297,7 @@ module Mrss
283
297
 
284
298
  def require_mmapv1
285
299
  before(:all) do
286
- if ClusterConfig.instance.storage_engine != :mmapv1
300
+ if SpecConfig.instance.serverless? || ClusterConfig.instance.storage_engine != :mmapv1
287
301
  skip 'Test requires MMAPv1 storage engine'
288
302
  end
289
303
  end
@@ -326,5 +340,29 @@ module Mrss
326
340
  end
327
341
  end
328
342
  end
343
+
344
+ def require_required_api_version
345
+ before(:all) do
346
+ unless ENV['API_VERSION_REQUIRED'] == '1'
347
+ skip 'Set API_VERSION_REQUIRED=1 to run this test'
348
+ end
349
+ end
350
+ end
351
+
352
+ def require_no_required_api_version
353
+ before(:all) do
354
+ if ENV['API_VERSION_REQUIRED'] == '1'
355
+ skip 'Cannot have API_VERSION_REQUIRED=1 to run this test'
356
+ end
357
+ end
358
+ end
359
+
360
+ def require_unix_socket
361
+ before(:all) do
362
+ if ENV['TOPOLOGY'] == 'load-balanced'
363
+ skip 'Load balancer does not listen on Unix sockets'
364
+ end
365
+ end
366
+ end
329
367
  end
330
368
  end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  require 'optparse'
2
5
  require 'erb'
3
6
  autoload :Dotenv, 'dotenv'
@@ -108,7 +111,7 @@ module Mrss
108
111
  '.'])
109
112
  end
110
113
 
111
- BASE_TEST_COMMAND = %w(docker run -i --tmpfs /tmpfs:exec).freeze
114
+ BASE_TEST_COMMAND = %w(docker run --rm -i --tmpfs /tmpfs:exec).freeze
112
115
 
113
116
  def run_tests
114
117
  run_command(BASE_TEST_COMMAND + tty_arg + extra_env + [image_tag] +
@@ -176,9 +179,11 @@ module Mrss
176
179
  BASE_IMAGES = {
177
180
  'debian81' => 'debian:jessie',
178
181
  'debian92' => 'debian:stretch',
182
+ 'debian10' => 'debian:buster',
179
183
  'ubuntu1404' => 'ubuntu:trusty',
180
184
  'ubuntu1604' => 'ubuntu:xenial',
181
185
  'ubuntu1804' => 'ubuntu:bionic',
186
+ 'ubuntu2004' => 'ubuntu:focal',
182
187
  'rhel62' => 'centos:6',
183
188
  'rhel70' => 'centos:7',
184
189
  }.freeze
@@ -195,6 +200,10 @@ module Mrss
195
200
  ruby == 'ruby-head'
196
201
  end
197
202
 
203
+ def system_ruby?
204
+ %w(1 true yes).include?(@env['SYSTEM_RUBY']&.downcase)
205
+ end
206
+
198
207
  def server_version
199
208
  @env['MONGODB_VERSION']
200
209
  end
@@ -171,5 +171,21 @@ module Mrss
171
171
  end
172
172
  end
173
173
  end
174
+
175
+ def require_active_support
176
+ before(:all) do
177
+ if !SpecConfig.instance.active_support?
178
+ skip 'This test requires ActiveSupport; set WITH_ACTIVE_SUPPORT=1 in environment'
179
+ end
180
+ end
181
+ end
182
+
183
+ def no_active_support
184
+ before(:all) do
185
+ if SpecConfig.instance.active_support?
186
+ skip 'This test requires no ActiveSupport; unset WITH_ACTIVE_SUPPORT in environment'
187
+ end
188
+ end
189
+ end
174
190
  end
175
191
  end
@@ -1,8 +1,23 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  autoload :JSON, 'json'
2
5
  require 'open-uri'
3
6
 
4
7
  module Mrss
5
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
+
6
21
  def initialize(desired_version, arch)
7
22
  @desired_version, @arch = desired_version, arch
8
23
  end
@@ -11,39 +26,16 @@ module Mrss
11
26
 
12
27
  def download_url
13
28
  @download_url ||= begin
14
- info = JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
15
- version = info['versions'].detect do |version|
16
- version['version'].start_with?(desired_version) &&
17
- !version['version'].include?('-') &&
18
- # Sometimes the download situation is borked and there is a release
19
- # with no downloads... skip those.
20
- !version['downloads'].empty?
21
- end
22
- # Allow RC releases if there isn't a GA release.
23
- version ||= info['versions'].detect do |version|
24
- version['version'].start_with?(desired_version) &&
25
- # Sometimes the download situation is borked and there is a release
26
- # with no downloads... skip those.
27
- !version['downloads'].empty?
29
+ version, version_ok = detect_version(current_catalog)
30
+ if version.nil?
31
+ version, full_version_ok = detect_version(full_catalog)
32
+ version_ok ||= full_version_ok
28
33
  end
29
34
  if version.nil?
30
- info = JSON.load(URI.parse('http://downloads.mongodb.org/full.json').open.read)
31
- versions = info['versions'].select do |version|
32
- version['version'].start_with?(desired_version) &&
33
- !version['downloads'].empty?
34
- end
35
- # Get rid of rc, beta etc. versions if there is a GA release.
36
- if versions.any? { |version| !version.include?('-') }
37
- versions.delete_if do |version|
38
- version['version'].include?('-')
39
- end
40
- end
41
- # Versions are ordered with newest first, take the first one i.e. the most
42
- # recent one.
43
- version = versions.first
44
- if version.nil?
45
- STDERR.puts "Error: no version #{desired_version}"
46
- exit 2
35
+ if version_ok
36
+ raise MissingDownloadUrl, "No downloads for version #{desired_version}"
37
+ else
38
+ raise UnknownVersion, "No version #{desired_version}"
47
39
  end
48
40
  end
49
41
  dl = version['downloads'].detect do |dl|
@@ -51,13 +43,31 @@ module Mrss
51
43
  dl['arch'] == 'x86_64'
52
44
  end
53
45
  unless dl
54
- STDERR.puts "Error: no download for #{arch} for #{version['version']}"
55
- exit 2
46
+ raise MissingDownloadUrl, "No download for #{arch} for #{version['version']}"
56
47
  end
57
48
  url = dl['archive']['url']
58
49
  end
50
+ rescue MissingDownloadUrl
51
+ if %w(4.7 4.7.0).include?(desired_version)
52
+ # 4.7.0 has no advertised downloads but it is downloadable and
53
+ # we do need it. Dirty hack below.
54
+ registry = self.class.new('4.4.3', arch)
55
+ registry.download_url.sub('4.4.3', '4.7.0').tap do |url|
56
+ # Sanity check - ensure the URL we hacked up is a valid one
57
+ io = uri_open(url)
58
+ begin
59
+ io.read(1)
60
+ ensure
61
+ io.close
62
+ end
63
+ end
64
+ else
65
+ raise
66
+ end
59
67
  end
60
68
 
69
+ private
70
+
61
71
  def uri_open(*args)
62
72
  if RUBY_VERSION < '2.5'
63
73
  open(*args)
@@ -65,5 +75,41 @@ module Mrss
65
75
  URI.open(*args)
66
76
  end
67
77
  end
78
+
79
+ def detect_version(catalog)
80
+ candidate_versions = catalog['versions'].select do |version|
81
+ version['version'].start_with?(desired_version) &&
82
+ !version['version'].include?('-')
83
+ end
84
+ version_ok = !candidate_versions.empty?
85
+ # Sometimes the download situation is borked and there is a release
86
+ # with no downloads... skip those.
87
+ version = candidate_versions.detect do |version|
88
+ !version['downloads'].empty?
89
+ end
90
+ # Allow RC releases if there isn't a GA release.
91
+ if version.nil?
92
+ candidate_versions = catalog['versions'].select do |version|
93
+ version['version'].start_with?(desired_version)
94
+ end
95
+ version_ok ||= !candidate_versions.empty?
96
+ version = candidate_versions.detect do |version|
97
+ !version['downloads'].empty?
98
+ end
99
+ end
100
+ [version, version_ok]
101
+ end
102
+
103
+ def current_catalog
104
+ @current_catalog ||= begin
105
+ JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
106
+ end
107
+ end
108
+
109
+ def full_catalog
110
+ @full_catalog ||= begin
111
+ JSON.load(uri_open('http://downloads.mongodb.org/full.json').read)
112
+ end
113
+ end
68
114
  end
69
115
  end