mongoid 7.5.1 → 7.5.3
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
- checksums.yaml.gz.sig +0 -0
- data/lib/mongoid/association/embedded/batchable.rb +20 -3
- data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
- data/lib/mongoid/cacheable.rb +2 -2
- data/lib/mongoid/config.rb +6 -0
- data/lib/mongoid/contextual/mongo.rb +24 -7
- data/lib/mongoid/document.rb +8 -1
- data/lib/mongoid/persistence_context.rb +57 -6
- data/lib/mongoid/shardable.rb +35 -11
- data/lib/mongoid/version.rb +1 -1
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +21 -0
- data/spec/mongoid/association/embedded/embeds_many_models.rb +121 -0
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +30 -0
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +20 -0
- data/spec/mongoid/attributes_spec.rb +44 -0
- data/spec/mongoid/cacheable_spec.rb +3 -3
- data/spec/mongoid/clients_spec.rb +30 -0
- data/spec/mongoid/config_spec.rb +7 -0
- data/spec/mongoid/contextual/mongo_spec.rb +23 -3
- data/spec/mongoid/persistence_context_spec.rb +26 -1
- data/spec/mongoid/shardable_models.rb +14 -0
- data/spec/mongoid/shardable_spec.rb +157 -51
- data/spec/mongoid_spec.rb +7 -1
- data/spec/shared/lib/mrss/lite_constraints.rb +8 -0
- data/spec/shared/shlib/server.sh +5 -5
- data.tar.gz.sig +0 -0
- metadata +633 -628
- metadata.gz.sig +0 -0
@@ -1012,6 +1012,36 @@ describe Mongoid::Clients do
|
|
1012
1012
|
end
|
1013
1013
|
end
|
1014
1014
|
end
|
1015
|
+
|
1016
|
+
context 'when requesting named client' do
|
1017
|
+
let(:secondary_client) do
|
1018
|
+
double(Mongo::Client).tap do |client|
|
1019
|
+
allow(client).to receive(:cluster).and_return(double("cluster"))
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
before do
|
1024
|
+
expect(Mongoid::Clients::Factory).to receive(:create)
|
1025
|
+
.with(:secondary)
|
1026
|
+
.and_return(secondary_client)
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
after do
|
1030
|
+
Mongoid::Clients.clients.delete(:secondary)
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
it 'does not close the client' do
|
1034
|
+
expect(secondary_client).not_to receive(:close)
|
1035
|
+
|
1036
|
+
Band.with(client: :default) do |klass|
|
1037
|
+
klass.mongo_client
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
Band.with(client: :secondary) do |klass|
|
1041
|
+
klass.mongo_client
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
end
|
1015
1045
|
end
|
1016
1046
|
|
1017
1047
|
context "when overriding the default database" do
|
data/spec/mongoid/config_spec.rb
CHANGED
@@ -335,6 +335,13 @@ describe Mongoid::Config do
|
|
335
335
|
it_behaves_like "a config option"
|
336
336
|
end
|
337
337
|
|
338
|
+
context 'when setting the legacy_attributes option in the config' do
|
339
|
+
let(:option) { :legacy_attributes }
|
340
|
+
let(:default) { true }
|
341
|
+
|
342
|
+
it_behaves_like "a config option"
|
343
|
+
end
|
344
|
+
|
338
345
|
describe "#load!" do
|
339
346
|
|
340
347
|
before(:all) do
|
@@ -1554,9 +1554,9 @@ describe Mongoid::Contextual::Mongo do
|
|
1554
1554
|
expect(context.send(method)).to eq(depeche_mode)
|
1555
1555
|
end
|
1556
1556
|
|
1557
|
-
context
|
1557
|
+
context "when calling ##{method}" do
|
1558
1558
|
|
1559
|
-
it 'returns the
|
1559
|
+
it 'returns the requested document, sorted by _id' do
|
1560
1560
|
expect(context.send(method)).to eq(depeche_mode)
|
1561
1561
|
expect(context.last).to eq(rolling_stones)
|
1562
1562
|
end
|
@@ -1571,7 +1571,7 @@ describe Mongoid::Contextual::Mongo do
|
|
1571
1571
|
expect(context.send(method, opts)).to eq(depeche_mode)
|
1572
1572
|
end
|
1573
1573
|
|
1574
|
-
context
|
1574
|
+
context "when calling ##{method}" do
|
1575
1575
|
|
1576
1576
|
it 'doesn\'t apply a sort on _id' do
|
1577
1577
|
expect(context.send(method, opts)).to eq(depeche_mode)
|
@@ -1883,6 +1883,16 @@ describe Mongoid::Contextual::Mongo do
|
|
1883
1883
|
end
|
1884
1884
|
end
|
1885
1885
|
|
1886
|
+
context "when given an empty hash" do
|
1887
|
+
let(:context) { described_class.new(criteria) }
|
1888
|
+
let(:criteria) { Band.criteria }
|
1889
|
+
let(:docs) { context.send(method, {}) }
|
1890
|
+
|
1891
|
+
it "behaves as if limit is nil" do
|
1892
|
+
expect(docs).to eq(depeche_mode)
|
1893
|
+
end
|
1894
|
+
end
|
1895
|
+
|
1886
1896
|
context "when calling #first then #last" do
|
1887
1897
|
|
1888
1898
|
let(:context) do
|
@@ -2358,6 +2368,16 @@ describe Mongoid::Contextual::Mongo do
|
|
2358
2368
|
end
|
2359
2369
|
end
|
2360
2370
|
end
|
2371
|
+
|
2372
|
+
context "when given an empty hash" do
|
2373
|
+
let(:context) { described_class.new(criteria) }
|
2374
|
+
let(:criteria) { Band.criteria }
|
2375
|
+
let(:docs) { context.last({}) }
|
2376
|
+
|
2377
|
+
it "behaves as if limit is nil" do
|
2378
|
+
expect(docs).to eq(rolling_stones)
|
2379
|
+
end
|
2380
|
+
end
|
2361
2381
|
end
|
2362
2382
|
|
2363
2383
|
describe "#initialize" do
|
@@ -39,7 +39,6 @@ describe Mongoid::PersistenceContext do
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
42
|
describe '.get' do
|
44
43
|
|
45
44
|
let(:options) do
|
@@ -134,6 +133,32 @@ describe Mongoid::PersistenceContext do
|
|
134
133
|
end
|
135
134
|
end
|
136
135
|
end
|
136
|
+
|
137
|
+
context 'with reusable client' do
|
138
|
+
let(:options) do
|
139
|
+
{client: :some_client}
|
140
|
+
end
|
141
|
+
|
142
|
+
let(:cluster) do
|
143
|
+
double(Mongo::Cluster)
|
144
|
+
end
|
145
|
+
|
146
|
+
let(:client) do
|
147
|
+
double(Mongo::Client).tap do |client|
|
148
|
+
allow(client).to receive(:cluster).and_return(cluster)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
before do
|
153
|
+
expect(Mongoid::Clients).to receive(:with_name).with(:some_client).and_return(client)
|
154
|
+
expect(client).not_to receive(:close)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'does not close the client' do
|
158
|
+
described_class.set(object, options)
|
159
|
+
described_class.clear(object, cluster.dup)
|
160
|
+
end
|
161
|
+
end
|
137
162
|
end
|
138
163
|
|
139
164
|
describe '#initialize' do
|
@@ -59,3 +59,17 @@ end
|
|
59
59
|
class SmNotSharded
|
60
60
|
include Mongoid::Document
|
61
61
|
end
|
62
|
+
|
63
|
+
class SmReviewAuthor
|
64
|
+
include Mongoid::Document
|
65
|
+
embedded_in :review, class_name: "SmReview", touch: false
|
66
|
+
field :name, type: String
|
67
|
+
end
|
68
|
+
|
69
|
+
class SmReview
|
70
|
+
include Mongoid::Document
|
71
|
+
|
72
|
+
embeds_one :author, class_name: "SmReviewAuthor"
|
73
|
+
|
74
|
+
shard_key "author.name" => 1
|
75
|
+
end
|
@@ -27,11 +27,11 @@ describe Mongoid::Shardable do
|
|
27
27
|
context 'when full syntax is used' do
|
28
28
|
context 'with symbol value' do
|
29
29
|
it 'sets shard key fields to symbol value' do
|
30
|
-
SmProducer.shard_key_fields.
|
30
|
+
expect(SmProducer.shard_key_fields).to be == %i(age gender)
|
31
31
|
end
|
32
32
|
|
33
33
|
it 'sets shard config' do
|
34
|
-
SmProducer.shard_config.
|
34
|
+
expect(SmProducer.shard_config).to be == {
|
35
35
|
key: {age: 1, gender: 'hashed'},
|
36
36
|
options: {
|
37
37
|
unique: true,
|
@@ -41,37 +41,37 @@ describe Mongoid::Shardable do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'keeps hashed as string' do
|
44
|
-
SmProducer.shard_config[:key][:gender].
|
44
|
+
expect(SmProducer.shard_config[:key][:gender]).to be == 'hashed'
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
context 'with string value' do
|
49
49
|
it 'sets shard key fields to symbol value' do
|
50
|
-
SmActor.shard_key_fields.
|
50
|
+
expect(SmActor.shard_key_fields).to be == %i(age gender hello)
|
51
51
|
end
|
52
52
|
|
53
53
|
it 'sets shard config' do
|
54
|
-
SmActor.shard_config.
|
54
|
+
expect(SmActor.shard_config).to be == {
|
55
55
|
key: {age: 1, gender: 'hashed', hello: 'hashed'},
|
56
56
|
options: {},
|
57
57
|
}
|
58
58
|
end
|
59
59
|
|
60
60
|
it 'sets hashed to string' do
|
61
|
-
SmActor.shard_config[:key][:gender].
|
61
|
+
expect(SmActor.shard_config[:key][:gender]).to be == 'hashed'
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
65
|
context 'when passed association name' do
|
66
66
|
it 'uses foreign key as shard key in shard config' do
|
67
|
-
SmDriver.shard_config.
|
67
|
+
expect(SmDriver.shard_config).to be == {
|
68
68
|
key: {age: 1, agency_id: 'hashed'},
|
69
69
|
options: {},
|
70
70
|
}
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'uses foreign key as shard key in shard key fields' do
|
74
|
-
SmDriver.shard_key_fields.
|
74
|
+
expect(SmDriver.shard_key_fields).to be == %i(age agency_id)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -79,26 +79,26 @@ describe Mongoid::Shardable do
|
|
79
79
|
context 'when shorthand syntax is used' do
|
80
80
|
context 'with symbol value' do
|
81
81
|
it 'sets shard key fields to symbol value' do
|
82
|
-
SmMovie.shard_key_fields.
|
82
|
+
expect(SmMovie.shard_key_fields).to be == %i(year)
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
86
|
context 'with string value' do
|
87
87
|
it 'sets shard key fields to symbol value' do
|
88
|
-
SmTrailer.shard_key_fields.
|
88
|
+
expect(SmTrailer.shard_key_fields).to be == %i(year)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
92
|
context 'when passed association name' do
|
93
93
|
it 'uses foreign key as shard key in shard config' do
|
94
|
-
SmDirector.shard_config.
|
94
|
+
expect(SmDirector.shard_config).to be == {
|
95
95
|
key: {agency_id: 1},
|
96
96
|
options: {},
|
97
97
|
}
|
98
98
|
end
|
99
99
|
|
100
100
|
it 'uses foreign key as shard key in shard key fields' do
|
101
|
-
SmDirector.shard_key_fields.
|
101
|
+
expect(SmDirector.shard_key_fields).to be == %i(agency_id)
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|
@@ -106,41 +106,80 @@ describe Mongoid::Shardable do
|
|
106
106
|
|
107
107
|
describe '#shard_key_selector' do
|
108
108
|
subject { instance.shard_key_selector }
|
109
|
-
|
110
|
-
|
109
|
+
|
110
|
+
context 'when key is an immediate attribute' do
|
111
|
+
let(:klass) { Band }
|
112
|
+
let(:value) { 'a-brand-name' }
|
111
113
|
|
112
|
-
|
114
|
+
before { klass.shard_key(:name) }
|
113
115
|
|
114
|
-
|
115
|
-
|
116
|
+
context 'when record is new' do
|
117
|
+
let(:instance) { klass.new(name: value) }
|
116
118
|
|
117
|
-
|
119
|
+
it { is_expected.to eq({ 'name' => value }) }
|
118
120
|
|
119
|
-
|
120
|
-
|
121
|
+
context 'changing shard key value' do
|
122
|
+
let(:new_value) { 'a-new-value' }
|
121
123
|
|
122
|
-
|
123
|
-
|
124
|
+
before do
|
125
|
+
instance.name = new_value
|
126
|
+
end
|
127
|
+
|
128
|
+
it { is_expected.to eq({ 'name' => new_value }) }
|
124
129
|
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'when record is persisted' do
|
133
|
+
let(:instance) { klass.create!(name: value) }
|
134
|
+
|
135
|
+
it { is_expected.to eq({ 'name' => value }) }
|
136
|
+
|
137
|
+
context 'changing shard key value' do
|
138
|
+
let(:new_value) { 'a-new-value' }
|
139
|
+
|
140
|
+
before do
|
141
|
+
instance.name = new_value
|
142
|
+
end
|
125
143
|
|
126
|
-
|
144
|
+
it { is_expected.to eq({ 'name' => new_value }) }
|
145
|
+
end
|
127
146
|
end
|
128
147
|
end
|
129
148
|
|
130
|
-
context 'when
|
131
|
-
let(:
|
149
|
+
context 'when key is an embedded attribute' do
|
150
|
+
let(:klass) { SmReview }
|
151
|
+
let(:value) { 'Arthur Conan Doyle' }
|
152
|
+
let(:key) { 'author.name' }
|
132
153
|
|
133
|
-
|
154
|
+
context 'when record is new' do
|
155
|
+
let(:instance) { klass.new(author: { name: value }) }
|
134
156
|
|
135
|
-
|
136
|
-
let(:new_value) { 'a-new-value' }
|
157
|
+
it { is_expected.to eq({ key => value }) }
|
137
158
|
|
138
|
-
|
139
|
-
|
159
|
+
context 'changing shard key value' do
|
160
|
+
let(:new_value) { 'Jules Verne' }
|
161
|
+
|
162
|
+
before do
|
163
|
+
instance.author.name = new_value
|
164
|
+
end
|
165
|
+
|
166
|
+
it { is_expected.to eq({ key => new_value }) }
|
140
167
|
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'when record is persisted' do
|
171
|
+
let(:instance) { klass.create!(author: { name: value }) }
|
172
|
+
|
173
|
+
it { is_expected.to eq({ key => value }) }
|
174
|
+
|
175
|
+
context 'changing shard key value' do
|
176
|
+
let(:new_value) { 'Jules Verne' }
|
177
|
+
|
178
|
+
before do
|
179
|
+
instance.author.name = new_value
|
180
|
+
end
|
141
181
|
|
142
|
-
|
143
|
-
subject.should == { 'name' => new_value }
|
182
|
+
it { is_expected.to eq({ 'author.name' => new_value }) }
|
144
183
|
end
|
145
184
|
end
|
146
185
|
end
|
@@ -148,42 +187,109 @@ describe Mongoid::Shardable do
|
|
148
187
|
|
149
188
|
describe '#shard_key_selector_in_db' do
|
150
189
|
subject { instance.shard_key_selector_in_db }
|
151
|
-
let(:klass) { Band }
|
152
|
-
let(:value) { 'a-brand-name' }
|
153
190
|
|
154
|
-
|
191
|
+
context 'when key is an immediate attribute' do
|
192
|
+
let(:klass) { Band }
|
193
|
+
let(:value) { 'a-brand-name' }
|
155
194
|
|
156
|
-
|
157
|
-
let(:instance) { klass.new(name: value) }
|
195
|
+
before { klass.shard_key(:name) }
|
158
196
|
|
159
|
-
|
197
|
+
context 'when record is new' do
|
198
|
+
let(:instance) { klass.new(name: value) }
|
160
199
|
|
161
|
-
|
162
|
-
|
200
|
+
it { is_expected.to eq({ 'name' => value }) }
|
201
|
+
|
202
|
+
context 'changing shard key value' do
|
203
|
+
let(:new_value) { 'a-new-value' }
|
204
|
+
|
205
|
+
before do
|
206
|
+
instance.name = new_value
|
207
|
+
end
|
208
|
+
|
209
|
+
it { is_expected.to eq({ 'name' => new_value }) }
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context 'when record is persisted' do
|
214
|
+
let(:instance) { klass.create!(name: value) }
|
215
|
+
|
216
|
+
it { is_expected.to eq({ 'name' => value }) }
|
217
|
+
|
218
|
+
context 'changing shard key value' do
|
219
|
+
let(:new_value) { 'a-new-value' }
|
220
|
+
|
221
|
+
before do
|
222
|
+
instance.name = new_value
|
223
|
+
end
|
224
|
+
|
225
|
+
it { is_expected.to eq({ 'name' => value }) }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context "when record is not found" do
|
230
|
+
let!(:instance) { klass.create!(name: value) }
|
163
231
|
|
164
232
|
before do
|
165
|
-
instance.
|
233
|
+
instance.destroy
|
166
234
|
end
|
167
235
|
|
168
|
-
it
|
169
|
-
|
236
|
+
it "raises a DocumentNotFound error" do
|
237
|
+
expect do
|
238
|
+
instance.reload
|
239
|
+
end.to raise_error(Mongoid::Errors::DocumentNotFound)
|
170
240
|
end
|
171
241
|
end
|
172
242
|
end
|
173
243
|
|
174
|
-
context 'when
|
175
|
-
let(:
|
244
|
+
context 'when key is an embedded attribute' do
|
245
|
+
let(:klass) { SmReview }
|
246
|
+
let(:value) { 'Arthur Conan Doyle' }
|
247
|
+
let(:key) { 'author.name' }
|
176
248
|
|
177
|
-
|
249
|
+
context 'when record is new' do
|
250
|
+
let(:instance) { klass.new(author: { name: value }) }
|
178
251
|
|
179
|
-
|
180
|
-
let(:new_value) { 'a-new-value' }
|
252
|
+
it { is_expected.to eq({ key => value }) }
|
181
253
|
|
182
|
-
|
183
|
-
|
254
|
+
context 'changing shard key value' do
|
255
|
+
let(:new_value) { 'Jules Verne' }
|
256
|
+
|
257
|
+
before do
|
258
|
+
instance.author.name = new_value
|
259
|
+
end
|
260
|
+
|
261
|
+
it { is_expected.to eq({ key => new_value }) }
|
184
262
|
end
|
263
|
+
end
|
185
264
|
|
186
|
-
|
265
|
+
context 'when record is persisted' do
|
266
|
+
let(:instance) { klass.create!(author: { name: value }) }
|
267
|
+
|
268
|
+
it { is_expected.to eq({ key => value }) }
|
269
|
+
|
270
|
+
context 'changing shard key value' do
|
271
|
+
let(:new_value) { 'Jules Verne' }
|
272
|
+
|
273
|
+
before do
|
274
|
+
instance.author.name = new_value
|
275
|
+
end
|
276
|
+
|
277
|
+
it { is_expected.to eq({ key => value }) }
|
278
|
+
end
|
279
|
+
|
280
|
+
context "when record is not found" do
|
281
|
+
let!(:instance) { klass.create!(author: { name: value }) }
|
282
|
+
|
283
|
+
before do
|
284
|
+
instance.destroy
|
285
|
+
end
|
286
|
+
|
287
|
+
it "raises a DocumentNotFound error" do
|
288
|
+
expect do
|
289
|
+
instance.reload
|
290
|
+
end.to raise_error(Mongoid::Errors::DocumentNotFound)
|
291
|
+
end
|
292
|
+
end
|
187
293
|
end
|
188
294
|
end
|
189
295
|
end
|
data/spec/mongoid_spec.rb
CHANGED
@@ -51,8 +51,14 @@ describe Mongoid do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
it "disconnects from all active clients" do
|
54
|
+
method_name = if Gem::Version.new(Mongo::VERSION) >= Gem::Version.new('2.19.0')
|
55
|
+
:close
|
56
|
+
else
|
57
|
+
:disconnect!
|
58
|
+
end
|
59
|
+
|
54
60
|
clients.each do |client|
|
55
|
-
expect(client.cluster).to receive(
|
61
|
+
expect(client.cluster).to receive(method_name).and_call_original
|
56
62
|
end
|
57
63
|
Mongoid.disconnect_clients
|
58
64
|
end
|
@@ -209,6 +209,14 @@ module Mrss
|
|
209
209
|
end
|
210
210
|
end
|
211
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
|
+
|
212
220
|
# This is a macro for retrying flaky tests on CI that occasionally fail.
|
213
221
|
# Note that the tests will only be retried on CI.
|
214
222
|
#
|
data/spec/shared/shlib/server.sh
CHANGED
@@ -71,12 +71,12 @@ prepare_server_from_url() {
|
|
71
71
|
export PATH="$BINDIR":$PATH
|
72
72
|
}
|
73
73
|
|
74
|
-
|
74
|
+
install_mlaunch_venv() {
|
75
75
|
python3 -V || true
|
76
|
-
if ! python3 -m
|
76
|
+
if ! python3 -m venv -h >/dev/null; then
|
77
77
|
# Current virtualenv fails with
|
78
78
|
# https://github.com/pypa/virtualenv/issues/1630
|
79
|
-
python3 -m pip install
|
79
|
+
python3 -m pip install venv --user
|
80
80
|
fi
|
81
81
|
if test "$USE_SYSTEM_PYTHON_PACKAGES" = 1 &&
|
82
82
|
python3 -m pip list |grep mtools
|
@@ -85,13 +85,13 @@ install_mlaunch_virtualenv() {
|
|
85
85
|
:
|
86
86
|
else
|
87
87
|
venvpath="$MONGO_ORCHESTRATION_HOME"/venv
|
88
|
-
python3 -m
|
88
|
+
python3 -m venv $venvpath
|
89
89
|
. $venvpath/bin/activate
|
90
90
|
# [mlaunch] does not work:
|
91
91
|
# https://github.com/rueckstiess/mtools/issues/856
|
92
92
|
# dateutil dependency is missing in mtools: https://github.com/rueckstiess/mtools/issues/864
|
93
93
|
#pip install 'mtools==1.7' 'pymongo==4.1' python-dateutil psutil
|
94
|
-
|
94
|
+
|
95
95
|
# dateutil dependency is missing in mtools: https://github.com/rueckstiess/mtools/issues/864
|
96
96
|
pip install 'mtools-legacy[mlaunch]' 'pymongo<4' python-dateutil
|
97
97
|
fi
|
data.tar.gz.sig
CHANGED
Binary file
|