google-cloud-firestore 2.4.0 → 2.6.1
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/AUTHENTICATION.md +2 -1
- data/CHANGELOG.md +35 -0
- data/CONTRIBUTING.md +4 -5
- data/LOGGING.md +1 -1
- data/lib/google/cloud/firestore/batch.rb +3 -4
- data/lib/google/cloud/firestore/client.rb +17 -23
- data/lib/google/cloud/firestore/collection_group.rb +136 -0
- data/lib/google/cloud/firestore/collection_reference.rb +9 -6
- data/lib/google/cloud/firestore/collection_reference_list.rb +3 -3
- data/lib/google/cloud/firestore/convert.rb +151 -173
- data/lib/google/cloud/firestore/document_reference.rb +9 -2
- data/lib/google/cloud/firestore/document_reference/list.rb +5 -6
- data/lib/google/cloud/firestore/field_path.rb +2 -2
- data/lib/google/cloud/firestore/field_value.rb +7 -2
- data/lib/google/cloud/firestore/query.rb +88 -19
- data/lib/google/cloud/firestore/query_partition.rb +80 -0
- data/lib/google/cloud/firestore/resource_path.rb +58 -0
- data/lib/google/cloud/firestore/service.rb +18 -1
- data/lib/google/cloud/firestore/transaction.rb +5 -5
- data/lib/google/cloud/firestore/version.rb +1 -1
- data/lib/google/cloud/firestore/watch/inventory.rb +9 -8
- data/lib/google/cloud/firestore/watch/listener.rb +3 -4
- metadata +9 -6
@@ -24,7 +24,12 @@ module Google
|
|
24
24
|
##
|
25
25
|
# @private Helper module for converting Protobuf values.
|
26
26
|
module Convert
|
27
|
-
# rubocop:disable
|
27
|
+
# rubocop:disable Metrics/AbcSize
|
28
|
+
# rubocop:disable Metrics/BlockLength
|
29
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
30
|
+
# rubocop:disable Metrics/MethodLength
|
31
|
+
# rubocop:disable Metrics/ModuleLength
|
32
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
28
33
|
module ClassMethods
|
29
34
|
def time_to_timestamp time
|
30
35
|
return nil if time.nil?
|
@@ -32,9 +37,10 @@ module Google
|
|
32
37
|
# Force the object to be a Time object.
|
33
38
|
time = time.to_time
|
34
39
|
|
35
|
-
Google::Protobuf::Timestamp.new
|
40
|
+
Google::Protobuf::Timestamp.new(
|
36
41
|
seconds: time.to_i,
|
37
|
-
nanos:
|
42
|
+
nanos: time.nsec
|
43
|
+
)
|
38
44
|
end
|
39
45
|
|
40
46
|
def timestamp_to_time timestamp
|
@@ -100,9 +106,10 @@ module Google
|
|
100
106
|
elsif Google::Cloud::Firestore::DocumentReference === obj
|
101
107
|
Google::Cloud::Firestore::V1::Value.new reference_value: obj.path
|
102
108
|
elsif Array === obj
|
103
|
-
values = obj.map { |o| raw_to_value
|
104
|
-
Google::Cloud::Firestore::V1::Value.new(
|
105
|
-
Google::Cloud::Firestore::V1::ArrayValue.new(values: values)
|
109
|
+
values = obj.map { |o| raw_to_value o }
|
110
|
+
Google::Cloud::Firestore::V1::Value.new(
|
111
|
+
array_value: Google::Cloud::Firestore::V1::ArrayValue.new(values: values)
|
112
|
+
)
|
106
113
|
elsif Hash === obj
|
107
114
|
# keys have been changed to strings before the hash gets here
|
108
115
|
geo_pairs = hash_is_geo_point? obj
|
@@ -112,8 +119,9 @@ module Google
|
|
112
119
|
)
|
113
120
|
else
|
114
121
|
fields = hash_to_fields obj
|
115
|
-
Google::Cloud::Firestore::V1::Value.new(
|
116
|
-
Google::Cloud::Firestore::V1::MapValue.new(fields: fields)
|
122
|
+
Google::Cloud::Firestore::V1::Value.new(
|
123
|
+
map_value: Google::Cloud::Firestore::V1::MapValue.new(fields: fields)
|
124
|
+
)
|
117
125
|
end
|
118
126
|
elsif obj.respond_to?(:read) && obj.respond_to?(:rewind)
|
119
127
|
obj.rewind
|
@@ -129,9 +137,7 @@ module Google
|
|
129
137
|
return false unless hash.keys.count == 2
|
130
138
|
|
131
139
|
pairs = hash.map { |k, v| [String(k), v] }.sort
|
132
|
-
if pairs.map(&:first) == ["latitude", "longitude"]
|
133
|
-
pairs
|
134
|
-
end
|
140
|
+
pairs if pairs.map(&:first) == ["latitude", "longitude"]
|
135
141
|
end
|
136
142
|
|
137
143
|
def hash_to_geo_point hash, pairs = nil
|
@@ -140,47 +146,39 @@ module Google
|
|
140
146
|
raise ArgumentError, "value is not a geo point" unless pairs
|
141
147
|
|
142
148
|
Google::Type::LatLng.new(
|
143
|
-
latitude:
|
144
|
-
longitude: pairs.last.last
|
149
|
+
latitude: pairs.first.last,
|
150
|
+
longitude: pairs.last.last
|
145
151
|
)
|
146
152
|
end
|
147
153
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
151
|
-
if is_field_value_nested data, :delete
|
154
|
+
def write_for_create doc_path, data
|
155
|
+
if field_value_nested? data, :delete
|
152
156
|
raise ArgumentError, "DELETE not allowed on create"
|
153
157
|
end
|
154
158
|
raise ArgumentError, "data is required" unless data.is_a? Hash
|
155
159
|
|
156
160
|
data, field_paths_and_values = remove_field_value_from data
|
157
161
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
if field_paths_and_values.any?
|
170
|
-
transform_write = transform_write doc_path, field_paths_and_values
|
171
|
-
|
172
|
-
if data.empty?
|
173
|
-
transform_write.current_document = \
|
174
|
-
Google::Cloud::Firestore::V1::Precondition.new(exists: false)
|
175
|
-
end
|
176
|
-
|
177
|
-
writes << transform_write
|
178
|
-
end
|
162
|
+
doc = Google::Cloud::Firestore::V1::Document.new(
|
163
|
+
name: doc_path,
|
164
|
+
fields: hash_to_fields(data)
|
165
|
+
)
|
166
|
+
precondition = Google::Cloud::Firestore::V1::Precondition.new exists: false
|
167
|
+
Google::Cloud::Firestore::V1::Write.new(
|
168
|
+
update: doc,
|
169
|
+
current_document: precondition,
|
170
|
+
update_transforms: field_transforms(field_paths_and_values)
|
171
|
+
)
|
172
|
+
end
|
179
173
|
|
180
|
-
|
174
|
+
def field_transforms paths
|
175
|
+
return nil if paths.empty?
|
176
|
+
paths.map do |field_path, field_value|
|
177
|
+
to_field_transform field_path, field_value
|
178
|
+
end.to_a
|
181
179
|
end
|
182
180
|
|
183
|
-
def
|
181
|
+
def write_for_set doc_path, data, merge: nil
|
184
182
|
raise ArgumentError, "data is required" unless data.is_a? Hash
|
185
183
|
|
186
184
|
if merge
|
@@ -195,11 +193,9 @@ module Google
|
|
195
193
|
end
|
196
194
|
allow_empty = false
|
197
195
|
end
|
198
|
-
return
|
196
|
+
return write_for_set_merge doc_path, data, field_paths, allow_empty
|
199
197
|
end
|
200
198
|
|
201
|
-
writes = []
|
202
|
-
|
203
199
|
data, delete_paths = remove_field_value_from data, :delete
|
204
200
|
if delete_paths.any?
|
205
201
|
raise ArgumentError, "DELETE not allowed on set"
|
@@ -207,30 +203,25 @@ module Google
|
|
207
203
|
|
208
204
|
data, field_paths_and_values = remove_field_value_from data
|
209
205
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
206
|
+
doc = Google::Cloud::Firestore::V1::Document.new(
|
207
|
+
name: doc_path,
|
208
|
+
fields: hash_to_fields(data)
|
209
|
+
)
|
210
|
+
Google::Cloud::Firestore::V1::Write.new(
|
211
|
+
update: doc,
|
212
|
+
update_transforms: field_transforms(field_paths_and_values)
|
214
213
|
)
|
215
|
-
|
216
|
-
if field_paths_and_values.any?
|
217
|
-
writes << transform_write(doc_path, field_paths_and_values)
|
218
|
-
end
|
219
|
-
|
220
|
-
writes
|
221
214
|
end
|
222
215
|
|
223
|
-
def
|
216
|
+
def write_for_set_merge doc_path, data, field_paths, allow_empty
|
224
217
|
raise ArgumentError, "data is required" unless data.is_a? Hash
|
225
218
|
|
226
219
|
validate_field_paths! field_paths
|
227
220
|
|
228
|
-
writes = []
|
229
|
-
|
230
221
|
# Ensure provided field paths are valid.
|
231
222
|
all_valid = identify_leaf_nodes data
|
232
223
|
all_valid_check = field_paths.map do |verify_path|
|
233
|
-
if all_valid.include?
|
224
|
+
if all_valid.include? verify_path
|
234
225
|
true
|
235
226
|
else
|
236
227
|
found_in_all_valid = all_valid.select do |fp|
|
@@ -271,32 +262,25 @@ module Google
|
|
271
262
|
# Restore delete paths
|
272
263
|
field_paths += delete_field_paths_and_values.keys
|
273
264
|
|
274
|
-
if data.empty? && !allow_empty
|
275
|
-
|
276
|
-
raise ArgumentError, "data required for set with merge"
|
277
|
-
end
|
265
|
+
if data.empty? && !allow_empty && field_paths_and_values.empty? && delete_field_paths_and_values.empty?
|
266
|
+
raise ArgumentError, "data required for set with merge"
|
278
267
|
end
|
279
268
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
end
|
293
|
-
|
294
|
-
writes
|
269
|
+
doc = Google::Cloud::Firestore::V1::Document.new(
|
270
|
+
name: doc_path,
|
271
|
+
fields: hash_to_fields(data)
|
272
|
+
)
|
273
|
+
doc_mask = Google::Cloud::Firestore::V1::DocumentMask.new(
|
274
|
+
field_paths: field_paths.map(&:formatted_string).sort
|
275
|
+
)
|
276
|
+
Google::Cloud::Firestore::V1::Write.new(
|
277
|
+
update: doc,
|
278
|
+
update_mask: doc_mask,
|
279
|
+
update_transforms: field_transforms(field_paths_and_values)
|
280
|
+
)
|
295
281
|
end
|
296
282
|
|
297
|
-
def
|
298
|
-
writes = []
|
299
|
-
|
283
|
+
def write_for_update doc_path, data, update_time: nil
|
300
284
|
raise ArgumentError, "data is required" unless data.is_a? Hash
|
301
285
|
|
302
286
|
# Convert data to use FieldPath
|
@@ -308,11 +292,11 @@ module Google
|
|
308
292
|
# Duplicate field paths check
|
309
293
|
validate_field_paths! new_data_pairs.map(&:first)
|
310
294
|
|
311
|
-
delete_paths, new_data_pairs = new_data_pairs.partition do |
|
295
|
+
delete_paths, new_data_pairs = new_data_pairs.partition do |_field_path, value|
|
312
296
|
value.is_a?(FieldValue) && value.type == :delete
|
313
297
|
end
|
314
298
|
|
315
|
-
root_field_paths_and_values, new_data_pairs = new_data_pairs.partition do |
|
299
|
+
root_field_paths_and_values, new_data_pairs = new_data_pairs.partition do |_field_path, value|
|
316
300
|
value.is_a? FieldValue
|
317
301
|
end
|
318
302
|
|
@@ -325,7 +309,7 @@ module Google
|
|
325
309
|
data, nested_deletes = remove_field_value_from data, :delete
|
326
310
|
raise ArgumentError, "DELETE cannot be nested" if nested_deletes.any?
|
327
311
|
|
328
|
-
data, nested_field_paths_and_values
|
312
|
+
data, nested_field_paths_and_values = remove_field_value_from data
|
329
313
|
|
330
314
|
field_paths_and_values = root_field_paths_and_values.merge nested_field_paths_and_values
|
331
315
|
|
@@ -338,34 +322,31 @@ module Google
|
|
338
322
|
raise ArgumentError, "data is required"
|
339
323
|
end
|
340
324
|
|
325
|
+
write = Google::Cloud::Firestore::V1::Write.new(
|
326
|
+
update: Google::Cloud::Firestore::V1::Document.new(name: doc_path),
|
327
|
+
update_mask: Google::Cloud::Firestore::V1::DocumentMask.new,
|
328
|
+
current_document: Google::Cloud::Firestore::V1::Precondition.new(exists: true)
|
329
|
+
)
|
330
|
+
|
341
331
|
if data.any? || delete_paths.any?
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
current_document: Google::Cloud::Firestore::V1::Precondition.new(
|
349
|
-
exists: true)
|
350
|
-
)
|
332
|
+
htf = hash_to_fields data
|
333
|
+
htf.each_pair do |k, v|
|
334
|
+
write.update.fields[k] = v
|
335
|
+
end
|
336
|
+
write.update_mask.field_paths += field_paths.map(&:formatted_string).sort
|
337
|
+
|
351
338
|
if update_time
|
352
|
-
write.current_document =
|
353
|
-
|
354
|
-
|
339
|
+
write.current_document = Google::Cloud::Firestore::V1::Precondition.new(
|
340
|
+
update_time: time_to_timestamp(update_time)
|
341
|
+
)
|
355
342
|
end
|
356
|
-
writes << write
|
357
343
|
end
|
358
344
|
|
359
345
|
if field_paths_and_values.any?
|
360
|
-
|
361
|
-
if data.empty?
|
362
|
-
transform_write.current_document = \
|
363
|
-
Google::Cloud::Firestore::V1::Precondition.new(exists: true)
|
364
|
-
end
|
365
|
-
writes << transform_write
|
346
|
+
write.update_transforms += field_transforms field_paths_and_values
|
366
347
|
end
|
367
348
|
|
368
|
-
|
349
|
+
write
|
369
350
|
end
|
370
351
|
|
371
352
|
def write_for_delete doc_path, exists: nil, update_time: nil
|
@@ -387,13 +368,20 @@ module Google
|
|
387
368
|
write
|
388
369
|
end
|
389
370
|
|
390
|
-
def
|
371
|
+
def field_value_nested? obj, field_value_type = nil
|
391
372
|
return obj if obj.is_a?(FieldValue) && (field_value_type.nil? || obj.type == field_value_type)
|
392
373
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
374
|
+
case obj
|
375
|
+
when Array
|
376
|
+
obj.each do |o|
|
377
|
+
val = field_value_nested? o, field_value_type
|
378
|
+
return val if val
|
379
|
+
end
|
380
|
+
when Hash
|
381
|
+
obj.each do |_k, v|
|
382
|
+
val = field_value_nested? v, field_value_type
|
383
|
+
return val if val
|
384
|
+
end
|
397
385
|
end
|
398
386
|
nil
|
399
387
|
end
|
@@ -406,35 +394,33 @@ module Google
|
|
406
394
|
if value.is_a?(FieldValue) && (field_value_type.nil? || value.type == field_value_type)
|
407
395
|
paths << [FieldPath.new(*key), value]
|
408
396
|
nil # will be removed by calling compact
|
409
|
-
|
410
|
-
if value.
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
end
|
420
|
-
if nested_hash.empty?
|
421
|
-
nil # will be removed by calling compact
|
422
|
-
else
|
423
|
-
[String(key), nested_hash]
|
397
|
+
elsif value.is_a? Hash
|
398
|
+
if value.empty?
|
399
|
+
[String(key), value]
|
400
|
+
else
|
401
|
+
nested_hash, nested_paths = remove_field_value_from value, field_value_type
|
402
|
+
if nested_paths.any?
|
403
|
+
nested_paths.each do |nested_field_path, nested_field_value|
|
404
|
+
updated_field_paths = ([key] + nested_field_path.fields).flatten
|
405
|
+
updated_field_path = FieldPath.new(*updated_field_paths)
|
406
|
+
paths << [updated_field_path, nested_field_value]
|
424
407
|
end
|
408
|
+
end
|
409
|
+
if nested_hash.empty?
|
410
|
+
nil # will be removed by calling compact
|
425
411
|
else
|
426
|
-
[String(key),
|
412
|
+
[String(key), nested_hash]
|
427
413
|
end
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
414
|
+
end
|
415
|
+
else
|
416
|
+
if value.is_a? Array
|
417
|
+
nested_field_value = field_value_nested? value, field_value_type
|
418
|
+
if nested_field_value
|
419
|
+
raise ArgumentError, "cannot nest #{nested_field_value.type} under arrays"
|
434
420
|
end
|
435
|
-
|
436
|
-
[String(key), value]
|
437
421
|
end
|
422
|
+
|
423
|
+
[String(key), value]
|
438
424
|
end
|
439
425
|
end
|
440
426
|
|
@@ -449,14 +435,14 @@ module Google
|
|
449
435
|
if value.is_a? Hash
|
450
436
|
nested_paths = identify_leaf_nodes value
|
451
437
|
nested_paths.each do |nested_path|
|
452
|
-
paths << (
|
438
|
+
paths << ([key] + nested_path.fields).flatten
|
453
439
|
end
|
454
440
|
else
|
455
441
|
paths << [key]
|
456
442
|
end
|
457
443
|
end
|
458
444
|
|
459
|
-
paths.map { |path| FieldPath.new
|
445
|
+
paths.map { |path| FieldPath.new(*path) }
|
460
446
|
end
|
461
447
|
|
462
448
|
def identify_all_file_paths hash
|
@@ -465,15 +451,14 @@ module Google
|
|
465
451
|
hash.map do |key, value|
|
466
452
|
paths << [key]
|
467
453
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
end
|
454
|
+
next unless value.is_a? Hash
|
455
|
+
nested_paths = identify_all_file_paths value
|
456
|
+
nested_paths.each do |nested_path|
|
457
|
+
paths << ([key] + nested_path.fields).flatten
|
473
458
|
end
|
474
459
|
end
|
475
460
|
|
476
|
-
paths.map { |path| FieldPath.new
|
461
|
+
paths.map { |path| FieldPath.new(*path) }
|
477
462
|
end
|
478
463
|
|
479
464
|
def select_by_field_paths hash, field_paths
|
@@ -533,19 +518,19 @@ module Google
|
|
533
518
|
right_hash.each_pair do |key, right_value|
|
534
519
|
left_value = left_hash[key]
|
535
520
|
|
536
|
-
if left_value.is_a?(Hash) && right_value.is_a?(Hash)
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
521
|
+
left_hash[key] = if left_value.is_a?(Hash) && right_value.is_a?(Hash)
|
522
|
+
deep_merge_hashes left_value, right_value
|
523
|
+
else
|
524
|
+
right_value
|
525
|
+
end
|
541
526
|
end
|
542
527
|
|
543
528
|
left_hash
|
544
529
|
end
|
545
530
|
|
546
|
-
START_FIELD_PATH_CHARS = /\A[a-zA-Z_]
|
547
|
-
INVALID_FIELD_PATH_CHARS =
|
548
|
-
ESCAPED_FIELD_PATH = /\A
|
531
|
+
START_FIELD_PATH_CHARS = /\A[a-zA-Z_]/.freeze
|
532
|
+
INVALID_FIELD_PATH_CHARS = %r{[~*/\[\]]}.freeze
|
533
|
+
ESCAPED_FIELD_PATH = /\A`(.*)`\z/.freeze
|
549
534
|
|
550
535
|
def build_hash_from_field_paths_and_values pairs
|
551
536
|
pairs.each do |field_path, _value|
|
@@ -579,59 +564,52 @@ module Google
|
|
579
564
|
"`#{str}`"
|
580
565
|
end
|
581
566
|
|
582
|
-
def transform_write doc_path, paths
|
583
|
-
field_transforms = paths.map do |field_path, field_value|
|
584
|
-
to_field_transform field_path, field_value
|
585
|
-
end
|
586
|
-
|
587
|
-
Google::Cloud::Firestore::V1::Write.new(
|
588
|
-
transform: Google::Cloud::Firestore::V1::DocumentTransform.new(
|
589
|
-
document: doc_path,
|
590
|
-
field_transforms: field_transforms
|
591
|
-
)
|
592
|
-
)
|
593
|
-
end
|
594
|
-
|
595
567
|
def to_field_transform field_path, field_value
|
596
|
-
|
568
|
+
case field_value.type
|
569
|
+
when :server_time
|
597
570
|
Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
|
598
|
-
field_path:
|
571
|
+
field_path: field_path.formatted_string,
|
599
572
|
set_to_server_value: :REQUEST_TIME
|
600
573
|
)
|
601
|
-
|
574
|
+
when :array_union
|
602
575
|
Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
|
603
|
-
field_path:
|
576
|
+
field_path: field_path.formatted_string,
|
604
577
|
append_missing_elements: raw_to_value(Array(field_value.value)).array_value
|
605
578
|
)
|
606
|
-
|
579
|
+
when :array_delete
|
607
580
|
Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
|
608
|
-
field_path:
|
581
|
+
field_path: field_path.formatted_string,
|
609
582
|
remove_all_from_array: raw_to_value(Array(field_value.value)).array_value
|
610
583
|
)
|
611
|
-
|
584
|
+
when :increment
|
612
585
|
Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
|
613
586
|
field_path: field_path.formatted_string,
|
614
|
-
increment:
|
587
|
+
increment: raw_to_value(field_value.value)
|
615
588
|
)
|
616
|
-
|
589
|
+
when :maximum
|
617
590
|
Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
|
618
591
|
field_path: field_path.formatted_string,
|
619
|
-
maximum:
|
592
|
+
maximum: raw_to_value(field_value.value)
|
620
593
|
)
|
621
|
-
|
594
|
+
when :minimum
|
622
595
|
Google::Cloud::Firestore::V1::DocumentTransform::FieldTransform.new(
|
623
596
|
field_path: field_path.formatted_string,
|
624
|
-
minimum:
|
597
|
+
minimum: raw_to_value(field_value.value)
|
625
598
|
)
|
626
599
|
else
|
627
600
|
raise ArgumentError, "unknown field transform #{field_value.type}"
|
628
601
|
end
|
629
602
|
end
|
630
603
|
end
|
631
|
-
# rubocop:enable all
|
632
604
|
|
633
605
|
extend ClassMethods
|
634
606
|
end
|
607
|
+
# rubocop:enable Metrics/AbcSize
|
608
|
+
# rubocop:enable Metrics/BlockLength
|
609
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
610
|
+
# rubocop:enable Metrics/MethodLength
|
611
|
+
# rubocop:enable Metrics/ModuleLength
|
612
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
635
613
|
end
|
636
614
|
end
|
637
615
|
end
|