mongoid 7.3.2 → 7.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/config/locales/en.yml +13 -0
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +3 -7
- data/lib/mongoid/association/relatable.rb +2 -0
- data/lib/mongoid/atomic.rb +26 -2
- data/lib/mongoid/config/environment.rb +9 -1
- data/lib/mongoid/contextual/atomic.rb +7 -2
- data/lib/mongoid/contextual/none.rb +3 -0
- data/lib/mongoid/criteria/queryable/selectable.rb +2 -2
- data/lib/mongoid/criteria/queryable/storable.rb +4 -4
- data/lib/mongoid/document.rb +3 -2
- data/lib/mongoid/errors/empty_config_file.rb +26 -0
- data/lib/mongoid/errors/invalid_config_file.rb +26 -0
- data/lib/mongoid/errors.rb +2 -0
- data/lib/mongoid/persistence_context.rb +3 -1
- data/lib/mongoid/query_cache.rb +11 -1
- data/lib/mongoid/tasks/database.rb +1 -1
- data/lib/mongoid/touchable.rb +10 -0
- data/lib/mongoid/version.rb +1 -1
- data/spec/integration/contextual/empty_spec.rb +142 -0
- data/spec/integration/stringified_symbol_field_spec.rb +2 -2
- data/spec/mongoid/association/referenced/belongs_to_query_spec.rb +20 -0
- data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +244 -92
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +6 -6
- data/spec/mongoid/association/referenced/has_many_models.rb +17 -0
- data/spec/mongoid/clients/factory_spec.rb +9 -3
- data/spec/mongoid/clients/options_spec.rb +11 -5
- data/spec/mongoid/config/environment_spec.rb +86 -8
- data/spec/mongoid/config_spec.rb +89 -16
- data/spec/mongoid/contextual/atomic_spec.rb +64 -25
- data/spec/mongoid/contextual/geo_near_spec.rb +1 -1
- data/spec/mongoid/document_spec.rb +21 -1
- data/spec/mongoid/errors/invalid_config_file_spec.rb +32 -0
- data/spec/mongoid/persistable/updatable_spec.rb +2 -0
- data/spec/mongoid/query_cache_spec.rb +24 -0
- data/spec/mongoid/touchable_spec.rb +18 -0
- data/spec/mongoid/touchable_spec_models.rb +2 -0
- data/spec/shared/lib/mrss/constraints.rb +21 -4
- data/spec/shared/lib/mrss/event_subscriber.rb +200 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +17 -12
- data/spec/shared/share/Dockerfile.erb +5 -4
- data/spec/shared/shlib/server.sh +71 -21
- data.tar.gz.sig +0 -0
- metadata +581 -573
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bf7cd241503d485cd393a4d0885629b2916aa5542083a281ff8883111938044e
|
4
|
+
data.tar.gz: f10cbf0946bd1b4a6f93deebe42fc2f9a728704d0565e1ea392df3007e5d6cc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e2a3edf8051a2ad27202288f73dc7d284effe7c766ab5d7a7274c0901e793eb124fd178d5b366ffb3b3c196e9e69cded899b66dab8f15044a9981e4b12f5c55
|
7
|
+
data.tar.gz: 3c990c42772c0b2f61eccf88bdb8ad5f26779fe62c8ad119f5717d9da48c558a85deaf56aa8ff088f97c2aee2e41c2a29cfec67018142a59c79e3ea13f8e415c
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/config/locales/en.yml
CHANGED
@@ -80,6 +80,12 @@ en:
|
|
80
80
|
different collections so a simple id lookup is not sufficient enough."
|
81
81
|
resolution: "Don't attempt to perform this action and have patience,
|
82
82
|
maybe this will be supported in the future."
|
83
|
+
empty_config_file:
|
84
|
+
message: "Empty configuration file: %{path}."
|
85
|
+
summary: "Your mongoid.yml configuration file appears to be empty."
|
86
|
+
resolution: "Ensure your configuration file contains the correct contents.
|
87
|
+
Please consult the following page with respect to Mongoid's configuration:
|
88
|
+
https://docs.mongodb.com/mongoid/current/reference/configuration/"
|
83
89
|
invalid_collection:
|
84
90
|
message: "Access to the collection for %{klass} is not allowed."
|
85
91
|
summary: "%{klass}.collection was called, and %{klass} is an embedded
|
@@ -94,6 +100,13 @@ en:
|
|
94
100
|
A collation option is only supported if the query is executed on a MongoDB server
|
95
101
|
with version >= 3.4."
|
96
102
|
resolution: "Remove the collation option from the query."
|
103
|
+
invalid_config_file:
|
104
|
+
message: "Invalid configuration file: %{path}."
|
105
|
+
summary: "Your mongoid.yml configuration file does not contain the
|
106
|
+
correct file structure."
|
107
|
+
resolution: "Ensure your configuration file contains the correct contents.
|
108
|
+
Please consult the following page with respect to Mongoid's configuration:
|
109
|
+
https://docs.mongodb.com/mongoid/current/reference/configuration/"
|
97
110
|
invalid_config_option:
|
98
111
|
message: "Invalid configuration option: %{name}."
|
99
112
|
summary: "A invalid configuration option was provided in your
|
@@ -209,9 +209,9 @@ module Mongoid
|
|
209
209
|
# @since 2.1.0
|
210
210
|
def empty?
|
211
211
|
if _loaded?
|
212
|
-
in_memory.
|
212
|
+
in_memory.empty?
|
213
213
|
else
|
214
|
-
|
214
|
+
_added.empty? && !_unloaded.exists?
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
@@ -242,11 +242,7 @@ module Mongoid
|
|
242
242
|
def any?(*args)
|
243
243
|
return super if args.any? || block_given?
|
244
244
|
|
245
|
-
|
246
|
-
in_memory.length > 0
|
247
|
-
else
|
248
|
-
_unloaded.exists? || _added.length > 0
|
249
|
-
end
|
245
|
+
!empty?
|
250
246
|
end
|
251
247
|
|
252
248
|
# Get the first document in the enumerable. Will check the persisted
|
data/lib/mongoid/atomic.rb
CHANGED
@@ -385,11 +385,35 @@ module Mongoid
|
|
385
385
|
updates = atomic_updates
|
386
386
|
return {} unless atomic_updates.key?("$set")
|
387
387
|
touches = {}
|
388
|
+
wanted_keys = %w(updated_at u_at)
|
389
|
+
# TODO this permits field to be passed as an empty string in which case
|
390
|
+
# it is ignored, get rid of this behavior.
|
391
|
+
if field.present?
|
392
|
+
wanted_keys << field.to_s
|
393
|
+
end
|
388
394
|
updates["$set"].each_pair do |key, value|
|
389
|
-
|
390
|
-
|
395
|
+
if wanted_keys.include?(key.split('.').last)
|
396
|
+
touches.update(key => value)
|
397
|
+
end
|
391
398
|
end
|
392
399
|
{ "$set" => touches }
|
393
400
|
end
|
401
|
+
|
402
|
+
# Returns the $set atomic updates affecting the specified field.
|
403
|
+
#
|
404
|
+
# @param [ String ] field The field name.
|
405
|
+
#
|
406
|
+
# @api private
|
407
|
+
def set_field_atomic_updates(field)
|
408
|
+
updates = atomic_updates
|
409
|
+
return {} unless atomic_updates.key?("$set")
|
410
|
+
sets = {}
|
411
|
+
updates["$set"].each_pair do |key, value|
|
412
|
+
if key.split('.').last == field
|
413
|
+
sets.update(key => value)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
{ "$set" => sets }
|
417
|
+
end
|
394
418
|
end
|
395
419
|
end
|
@@ -52,7 +52,15 @@ module Mongoid
|
|
52
52
|
# @api private
|
53
53
|
def load_yaml(path, environment = nil)
|
54
54
|
env = environment ? environment.to_s : env_name
|
55
|
-
|
55
|
+
contents = File.new(path).read
|
56
|
+
if contents.empty?
|
57
|
+
raise Mongoid::Errors::EmptyConfigFile.new(path)
|
58
|
+
end
|
59
|
+
data = YAML.load(ERB.new(contents).result)
|
60
|
+
unless data.is_a?(Hash)
|
61
|
+
raise Mongoid::Errors::InvalidConfigFile.new(path)
|
62
|
+
end
|
63
|
+
data[env]
|
56
64
|
end
|
57
65
|
end
|
58
66
|
end
|
@@ -173,13 +173,18 @@ module Mongoid
|
|
173
173
|
# @example Unset the field on the matches.
|
174
174
|
# context.unset(:name)
|
175
175
|
#
|
176
|
-
# @param [ String
|
176
|
+
# @param [ String | Symbol | Array<String|Symbol> | Hash ] args
|
177
|
+
# The name(s) of the field(s) to unset.
|
178
|
+
# If a Hash is specified, its keys will be used irrespective of what
|
179
|
+
# each key's value is, even if the value is nil or false.
|
177
180
|
#
|
178
181
|
# @return [ nil ] Nil.
|
179
182
|
#
|
180
183
|
# @since 3.0.0
|
181
184
|
def unset(*args)
|
182
|
-
fields = args.
|
185
|
+
fields = args.map { |a| a.is_a?(Hash) ? a.keys : a }
|
186
|
+
.__find_args__
|
187
|
+
.map { |f| [database_field_name(f), true] }
|
183
188
|
view.update_many("$unset" => Hash[fields])
|
184
189
|
end
|
185
190
|
|
@@ -597,7 +597,7 @@ module Mongoid
|
|
597
597
|
end
|
598
598
|
_mongoid_expand_keys(new_s).each do |k, v|
|
599
599
|
k = k.to_s
|
600
|
-
if c.selector[k] || k
|
600
|
+
if c.selector[k] || k.start_with?('$')
|
601
601
|
c = c.send(:__multi__, [{'$nor' => [{k => v}]}], '$and')
|
602
602
|
else
|
603
603
|
if v.is_a?(Hash)
|
@@ -890,7 +890,7 @@ module Mongoid
|
|
890
890
|
clone.tap do |query|
|
891
891
|
normalized.each do |field, value|
|
892
892
|
field_s = field.to_s
|
893
|
-
if field_s
|
893
|
+
if field_s.start_with?('$')
|
894
894
|
# Query expression-level operator, like $and or $where
|
895
895
|
query.add_operator_expression(field_s, value)
|
896
896
|
else
|
@@ -38,7 +38,7 @@ module Mongoid
|
|
38
38
|
raise ArgumentError, "Field must be a string: #{field}"
|
39
39
|
end
|
40
40
|
|
41
|
-
if field
|
41
|
+
if field.start_with?('$')
|
42
42
|
raise ArgumentError, "Field cannot be an operator (i.e. begin with $): #{field}"
|
43
43
|
end
|
44
44
|
|
@@ -48,7 +48,7 @@ module Mongoid
|
|
48
48
|
if value.is_a?(Hash) && selector[field].is_a?(Hash) &&
|
49
49
|
value.keys.all? { |key|
|
50
50
|
key_s = key.to_s
|
51
|
-
key_s
|
51
|
+
key_s.start_with?('$') && !selector[field].key?(key_s)
|
52
52
|
}
|
53
53
|
then
|
54
54
|
# Multiple operators can be combined on the same field by
|
@@ -185,7 +185,7 @@ module Mongoid
|
|
185
185
|
raise ArgumentError, "Operator must be a string: #{operator}"
|
186
186
|
end
|
187
187
|
|
188
|
-
unless operator
|
188
|
+
unless operator.start_with?('$')
|
189
189
|
raise ArgumentError, "Operator must begin with $: #{operator}"
|
190
190
|
end
|
191
191
|
|
@@ -220,7 +220,7 @@ module Mongoid
|
|
220
220
|
raise ArgumentError, "Field must be a string: #{field}"
|
221
221
|
end
|
222
222
|
|
223
|
-
if field
|
223
|
+
if field.start_with?('$')
|
224
224
|
add_operator_expression(field, value)
|
225
225
|
else
|
226
226
|
add_field_expression(field, value)
|
data/lib/mongoid/document.rb
CHANGED
@@ -194,8 +194,8 @@ module Mongoid
|
|
194
194
|
#
|
195
195
|
# @param [ Hash ] options The options.
|
196
196
|
#
|
197
|
-
# @option options [ true, false ] :compact Whether to include fields
|
198
|
-
# nil values in the json document.
|
197
|
+
# @option options [ true, false ] :compact (Deprecated) Whether to include fields
|
198
|
+
# with nil values in the json document.
|
199
199
|
#
|
200
200
|
# @return [ Hash ] The document as json.
|
201
201
|
#
|
@@ -203,6 +203,7 @@ module Mongoid
|
|
203
203
|
def as_json(options = nil)
|
204
204
|
rv = super
|
205
205
|
if options && options[:compact]
|
206
|
+
Mongoid.logger.warn('#as_json :compact option is deprecated. Please call #compact on the returned Hash object instead.')
|
206
207
|
rv = rv.compact
|
207
208
|
end
|
208
209
|
rv
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
module Mongoid
|
5
|
+
module Errors
|
6
|
+
|
7
|
+
# This error is raised when an empty configuration file is attempted to be
|
8
|
+
# loaded.
|
9
|
+
class EmptyConfigFile < MongoidError
|
10
|
+
|
11
|
+
# Create the new error.
|
12
|
+
#
|
13
|
+
# @param [ String ] path The path of the config file used.
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
def initialize(path)
|
17
|
+
super(
|
18
|
+
compose_message(
|
19
|
+
"empty_config_file",
|
20
|
+
{ path: path }
|
21
|
+
)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
module Mongoid
|
5
|
+
module Errors
|
6
|
+
|
7
|
+
# This error is raised when a bad configuration file is attempted to be
|
8
|
+
# loaded.
|
9
|
+
class InvalidConfigFile < MongoidError
|
10
|
+
|
11
|
+
# Create the new error.
|
12
|
+
#
|
13
|
+
# @param [ String ] path The path of the config file used.
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
def initialize(path)
|
17
|
+
super(
|
18
|
+
compose_message(
|
19
|
+
"invalid_config_file",
|
20
|
+
{ path: path }
|
21
|
+
)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/mongoid/errors.rb
CHANGED
@@ -8,8 +8,10 @@ require "mongoid/errors/criteria_argument_required"
|
|
8
8
|
require "mongoid/errors/document_not_destroyed"
|
9
9
|
require "mongoid/errors/document_not_found"
|
10
10
|
require "mongoid/errors/eager_load"
|
11
|
+
require "mongoid/errors/empty_config_file"
|
11
12
|
require "mongoid/errors/in_memory_collation_not_supported"
|
12
13
|
require "mongoid/errors/invalid_collection"
|
14
|
+
require "mongoid/errors/invalid_config_file"
|
13
15
|
require "mongoid/errors/invalid_config_option"
|
14
16
|
require "mongoid/errors/invalid_dependent_strategy"
|
15
17
|
require "mongoid/errors/invalid_field"
|
@@ -237,7 +237,9 @@ module Mongoid
|
|
237
237
|
# @since 6.0.0
|
238
238
|
def clear(object, cluster = nil, original_context = nil)
|
239
239
|
if context = get(object)
|
240
|
-
|
240
|
+
unless cluster.nil? || context.cluster.equal?(cluster)
|
241
|
+
context.client.close
|
242
|
+
end
|
241
243
|
end
|
242
244
|
ensure
|
243
245
|
Thread.current["[mongoid][#{object.object_id}]:context"] = original_context
|
data/lib/mongoid/query_cache.rb
CHANGED
@@ -7,8 +7,14 @@ module Mongoid
|
|
7
7
|
#
|
8
8
|
# @since 4.0.0
|
9
9
|
module QueryCache
|
10
|
-
|
10
|
+
# @api private
|
11
|
+
LEGACY_WARNING = <<~DOC
|
12
|
+
You are using the legacy Mongoid query cache which has known issues.
|
13
|
+
Please upgrade the `mongo' gem to at least 2.14.0 to use the improved driver query cache.
|
14
|
+
Refer to: https://docs.mongodb.com/mongoid/current/tutorials/mongoid-queries/#the-improved-driver-query-cache
|
15
|
+
DOC
|
11
16
|
|
17
|
+
class << self
|
12
18
|
# Get the cached queries.
|
13
19
|
#
|
14
20
|
# @example Get the cached queries from the current thread.
|
@@ -86,6 +92,10 @@ module Mongoid
|
|
86
92
|
if defined?(Mongo::QueryCache)
|
87
93
|
Mongo::QueryCache.cache(&block)
|
88
94
|
else
|
95
|
+
@legacy_query_cache_warned ||= begin
|
96
|
+
Mongoid.logger.warn(LEGACY_WARNING)
|
97
|
+
true
|
98
|
+
end
|
89
99
|
enabled = QueryCache.enabled?
|
90
100
|
QueryCache.enabled = true
|
91
101
|
begin
|
@@ -123,7 +123,7 @@ module Mongoid
|
|
123
123
|
next if model.shard_config.nil?
|
124
124
|
|
125
125
|
if model.embedded? && !model.cyclic?
|
126
|
-
logger.warn("MONGOID: #{model} has shard config but is
|
126
|
+
logger.warn("MONGOID: #{model} has shard config but is embedded")
|
127
127
|
next
|
128
128
|
end
|
129
129
|
|
data/lib/mongoid/touchable.rb
CHANGED
@@ -41,6 +41,16 @@ module Mongoid
|
|
41
41
|
# _association.inverse_association.options but inverse_association
|
42
42
|
# seems to not always/ever be set here. See MONGOID-5014.
|
43
43
|
_parent.touch
|
44
|
+
|
45
|
+
if field
|
46
|
+
# If we are told to also touch a field, perform a separate write
|
47
|
+
# for that field. See MONGOID-5136.
|
48
|
+
# In theory we should combine the writes, which would require
|
49
|
+
# passing the fields to be updated to the parents - MONGOID-5142.
|
50
|
+
sets = set_field_atomic_updates(field)
|
51
|
+
selector = atomic_selector
|
52
|
+
_root.collection.find(selector).update_one(positionally(selector, sets), session: _session)
|
53
|
+
end
|
44
54
|
else
|
45
55
|
# If the current document is not embedded, it is composition root
|
46
56
|
# and we need to persist the write here.
|
data/lib/mongoid/version.rb
CHANGED
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
describe 'Contextual classes when dealing with empty result set' do
|
7
|
+
shared_examples 'behave as expected' do
|
8
|
+
context '#exists?' do
|
9
|
+
it 'is false' do
|
10
|
+
context.exists?.should be false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context '#count' do
|
15
|
+
it 'is 0' do
|
16
|
+
context.count.should == 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context '#length' do
|
21
|
+
it 'is 0' do
|
22
|
+
context.length.should == 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# #estimated_count only exists for Mongo
|
27
|
+
|
28
|
+
context '#distinct' do
|
29
|
+
it 'is empty array' do
|
30
|
+
context.distinct(:foo).should == []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#each' do
|
35
|
+
context 'with block' do
|
36
|
+
it 'does not invoke the block' do
|
37
|
+
called = false
|
38
|
+
context.each do
|
39
|
+
called = true
|
40
|
+
end
|
41
|
+
called.should be false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'without block' do
|
46
|
+
it 'returns Enumerable' do
|
47
|
+
context.each.should be_a(Enumerable)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns empty Enumerable' do
|
51
|
+
context.each.to_a.should == []
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context '#map' do
|
57
|
+
context 'with block' do
|
58
|
+
it 'does not invoke the block' do
|
59
|
+
called = false
|
60
|
+
context.map do
|
61
|
+
called = true
|
62
|
+
end
|
63
|
+
called.should be false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'without block' do
|
68
|
+
it 'returns empty array' do
|
69
|
+
skip 'MONGOID-5148'
|
70
|
+
|
71
|
+
context.map(:field).should == []
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context '#first' do
|
77
|
+
it 'is nil' do
|
78
|
+
context.first.should be nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context '#find_first' do
|
83
|
+
it 'is nil' do
|
84
|
+
context.find_first.should be nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context '#one' do
|
89
|
+
it 'is nil' do
|
90
|
+
context.one.should be nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context '#last' do
|
95
|
+
it 'is nil' do
|
96
|
+
context.last.should be nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
let(:context) do
|
102
|
+
context_cls.new(criteria)
|
103
|
+
end
|
104
|
+
|
105
|
+
before do
|
106
|
+
# Create an object of the same class used in the Criteria instance
|
107
|
+
# to verify we are using the Contextual classes.
|
108
|
+
Mop.create!
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'Mongo' do
|
112
|
+
let(:context_cls) { Mongoid::Contextual::Mongo }
|
113
|
+
|
114
|
+
let(:criteria) do
|
115
|
+
Mop.and(Mop.where(a: 1), Mop.where(a: 2))
|
116
|
+
end
|
117
|
+
|
118
|
+
include_examples 'behave as expected'
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'Memory' do
|
122
|
+
let(:context_cls) { Mongoid::Contextual::Memory }
|
123
|
+
|
124
|
+
let(:criteria) do
|
125
|
+
Mop.all.tap do |criteria|
|
126
|
+
criteria.documents = []
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
include_examples 'behave as expected'
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'None' do
|
134
|
+
let(:context_cls) { Mongoid::Contextual::None }
|
135
|
+
|
136
|
+
let(:criteria) do
|
137
|
+
Mop.none
|
138
|
+
end
|
139
|
+
|
140
|
+
include_examples 'behave as expected'
|
141
|
+
end
|
142
|
+
end
|