mongo 1.9.1 → 1.9.2
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.tar.gz.sig +0 -0
- data/VERSION +1 -1
- data/lib/mongo/collection.rb +23 -13
- data/lib/mongo/cursor.rb +23 -3
- data/lib/mongo/mongo_client.rb +5 -10
- data/lib/mongo/networking.rb +1 -1
- data/lib/mongo/util/node.rb +8 -1
- data/lib/mongo/util/uri_parser.rb +14 -7
- data/test/functional/collection_test.rb +30 -1
- data/test/functional/connection_test.rb +13 -1
- data/test/functional/cursor_test.rb +28 -0
- data/test/functional/uri_test.rb +12 -0
- metadata +5 -5
- metadata.gz.sig +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0373b5b1e6c7a4e81fa022f379ed2ee17cffde80
|
4
|
+
data.tar.gz: 681a86c1b120260262b2f77f6133c7a7497a0f34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e192fa80088c0151f75b40fbb0649b356c3ac781be003985d753b9fa2e6af08371bcda2061c2a1a3e088fcfdd4896987845edcdca9a5af59ce01ef604311c749
|
7
|
+
data.tar.gz: 67cff259cc85729e4c16ae706ae4cef3e3e5a747895deae699ea0cdace0eb416b5c522e3dc41fd8e92aab4b39b3cb4c30c8b3e5c6c99be4b25b965922aabf95f
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.9.
|
1
|
+
1.9.2
|
data/lib/mongo/collection.rb
CHANGED
@@ -616,25 +616,36 @@ module Mongo
|
|
616
616
|
|
617
617
|
# Atomically update and return a document using MongoDB's findAndModify command. (MongoDB > 1.3.0)
|
618
618
|
#
|
619
|
-
# @option opts [Hash] :query ({}) a query selector document for matching
|
620
|
-
#
|
621
|
-
# @option opts [
|
622
|
-
#
|
623
|
-
#
|
624
|
-
#
|
625
|
-
#
|
626
|
-
#
|
619
|
+
# @option opts [Hash] :query ({}) a query selector document for matching
|
620
|
+
# the desired document.
|
621
|
+
# @option opts [Hash] :update (nil) the update operation to perform on the
|
622
|
+
# matched document.
|
623
|
+
# @option opts [Array, String, OrderedHash] :sort ({}) specify a sort
|
624
|
+
# option for the query using any
|
625
|
+
# of the sort options available for Cursor#sort. Sort order is important
|
626
|
+
# if the query will be matching multiple documents since only the first
|
627
|
+
# matching document will be updated and returned.
|
628
|
+
# @option opts [Boolean] :remove (false) If true, removes the the returned
|
629
|
+
# document from the collection.
|
630
|
+
# @option opts [Boolean] :new (false) If true, returns the updated
|
631
|
+
# document; otherwise, returns the document prior to update.
|
632
|
+
# @option opts [Boolean] :full_response (false) If true, returns the entire
|
633
|
+
# response object from the server including 'ok' and 'lastErrorObject'.
|
627
634
|
#
|
628
635
|
# @return [Hash] the matched document.
|
629
636
|
#
|
630
637
|
# @core findandmodify find_and_modify-instance_method
|
631
638
|
def find_and_modify(opts={})
|
639
|
+
full_response = opts.delete(:full_response)
|
640
|
+
|
632
641
|
cmd = BSON::OrderedHash.new
|
633
642
|
cmd[:findandmodify] = @name
|
634
643
|
cmd.merge!(opts)
|
635
|
-
cmd[:sort] = Mongo::Support.format_order_clause(opts[:sort]) if opts[:sort]
|
636
644
|
|
637
|
-
|
645
|
+
cmd[:sort] =
|
646
|
+
Mongo::Support.format_order_clause(opts[:sort]) if opts[:sort]
|
647
|
+
|
648
|
+
full_response ? @db.command(cmd) : @db.command(cmd)['value']
|
638
649
|
end
|
639
650
|
|
640
651
|
# Perform an aggregation using the aggregation framework on the current collection.
|
@@ -1098,8 +1109,7 @@ module Mongo
|
|
1098
1109
|
def insert_batch(message, documents, write_concern, continue_on_error, errors, collection_name=@name)
|
1099
1110
|
begin
|
1100
1111
|
send_insert_message(message, documents, collection_name, write_concern)
|
1101
|
-
rescue
|
1102
|
-
NoMemoryError, SystemCallError => ex
|
1112
|
+
rescue OperationFailure => ex
|
1103
1113
|
raise ex unless continue_on_error
|
1104
1114
|
errors << ex
|
1105
1115
|
end
|
@@ -1159,7 +1169,7 @@ module Mongo
|
|
1159
1169
|
insert_batch(message, docs_to_insert, write_concern, continue_on_error, errors, collection_name)
|
1160
1170
|
# insert_batch collects errors if w > 0 and continue_on_error is true,
|
1161
1171
|
# so raise the error here, as this is the last or only msg sent
|
1162
|
-
raise errors.
|
1172
|
+
raise errors.last unless errors.empty?
|
1163
1173
|
end
|
1164
1174
|
|
1165
1175
|
collect_on_error ? [inserted_ids, error_docs] : inserted_ids
|
data/lib/mongo/cursor.rb
CHANGED
@@ -117,7 +117,7 @@ module Mongo
|
|
117
117
|
# @return [Hash, Nil] the next document or Nil if no documents remain.
|
118
118
|
def next
|
119
119
|
if @cache.length == 0
|
120
|
-
if @query_run &&
|
120
|
+
if @query_run && exhaust?
|
121
121
|
close
|
122
122
|
return nil
|
123
123
|
else
|
@@ -224,6 +224,11 @@ module Mongo
|
|
224
224
|
def limit(number_to_return=nil)
|
225
225
|
return @limit unless number_to_return
|
226
226
|
check_modifiable
|
227
|
+
|
228
|
+
if (number_to_return != 0) && exhaust?
|
229
|
+
raise MongoArgumentError, "Limit is incompatible with exhaust option."
|
230
|
+
end
|
231
|
+
|
227
232
|
@limit = number_to_return
|
228
233
|
self
|
229
234
|
end
|
@@ -381,6 +386,14 @@ module Mongo
|
|
381
386
|
def add_option(opt)
|
382
387
|
check_modifiable
|
383
388
|
|
389
|
+
if exhaust?(opt)
|
390
|
+
if @limit != 0
|
391
|
+
raise MongoArgumentError, "Exhaust is incompatible with limit."
|
392
|
+
elsif @connection.mongos?
|
393
|
+
raise MongoArgumentError, "Exhaust is incompatible with mongos."
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
384
397
|
@options |= opt
|
385
398
|
@options
|
386
399
|
end
|
@@ -447,7 +460,7 @@ module Mongo
|
|
447
460
|
# Return the number of documents remaining for this cursor.
|
448
461
|
def num_remaining
|
449
462
|
if @cache.length == 0
|
450
|
-
if @query_run &&
|
463
|
+
if @query_run && exhaust?
|
451
464
|
close
|
452
465
|
return 0
|
453
466
|
else
|
@@ -483,7 +496,7 @@ module Mongo
|
|
483
496
|
socket = @socket || checkout_socket_from_connection
|
484
497
|
results, @n_received, @cursor_id = @connection.receive_message(
|
485
498
|
Mongo::Constants::OP_QUERY, message, nil, socket, @command,
|
486
|
-
nil,
|
499
|
+
nil, exhaust?)
|
487
500
|
rescue ConnectionFailure => ex
|
488
501
|
socket.close if socket
|
489
502
|
@pool = nil
|
@@ -621,6 +634,13 @@ module Mongo
|
|
621
634
|
end
|
622
635
|
end
|
623
636
|
|
637
|
+
# Check whether the exhaust option is set
|
638
|
+
#
|
639
|
+
# @return [true, false] The state of the exhaust flag.
|
640
|
+
def exhaust?(opts = options)
|
641
|
+
!(opts & OP_QUERY_EXHAUST).zero?
|
642
|
+
end
|
643
|
+
|
624
644
|
def check_modifiable
|
625
645
|
if @query_run || @closed
|
626
646
|
raise InvalidOperation, "Cannot modify the query once it has been run or closed."
|
data/lib/mongo/mongo_client.rb
CHANGED
@@ -30,7 +30,7 @@ module Mongo
|
|
30
30
|
DEFAULT_HOST = 'localhost'
|
31
31
|
DEFAULT_PORT = 27017
|
32
32
|
DEFAULT_DB_NAME = 'test'
|
33
|
-
GENERIC_OPTS = [:auths, :logger, :connect]
|
33
|
+
GENERIC_OPTS = [:auths, :logger, :connect, :default_db]
|
34
34
|
TIMEOUT_OPTS = [:timeout, :op_timeout, :connect_timeout]
|
35
35
|
SSL_OPTS = [:ssl, :ssl_key, :ssl_cert, :ssl_verify, :ssl_ca_cert]
|
36
36
|
POOL_OPTS = [:pool_size, :pool_timeout]
|
@@ -199,7 +199,7 @@ module Mongo
|
|
199
199
|
#
|
200
200
|
# @return [Mongo::MongoClient, Mongo::MongoReplicaSetClient]
|
201
201
|
def self.from_uri(uri = ENV['MONGODB_URI'], extra_opts={})
|
202
|
-
parser = URIParser.new
|
202
|
+
parser = URIParser.new(uri)
|
203
203
|
parser.connection(extra_opts)
|
204
204
|
end
|
205
205
|
|
@@ -210,7 +210,7 @@ module Mongo
|
|
210
210
|
raise MongoArgumentError,
|
211
211
|
"ENV['MONGODB_URI'] implies a replica set."
|
212
212
|
end
|
213
|
-
opts.merge!
|
213
|
+
opts.merge!(parser.connection_options)
|
214
214
|
[parser.host, parser.port]
|
215
215
|
else
|
216
216
|
[host || DEFAULT_HOST, port || DEFAULT_PORT]
|
@@ -358,13 +358,7 @@ module Mongo
|
|
358
358
|
# @return [Mongo::DB]
|
359
359
|
#
|
360
360
|
# @core databases db-instance_method
|
361
|
-
def db(db_name=
|
362
|
-
if !db_name && uri = ENV['MONGODB_URI']
|
363
|
-
db_name = uri[%r{/([^/\?]+)(\?|$)}, 1]
|
364
|
-
end
|
365
|
-
|
366
|
-
db_name ||= DEFAULT_DB_NAME
|
367
|
-
|
361
|
+
def db(db_name = @default_db, opts = {})
|
368
362
|
DB.new(db_name, self, opts)
|
369
363
|
end
|
370
364
|
|
@@ -689,6 +683,7 @@ module Mongo
|
|
689
683
|
end
|
690
684
|
Mongo::ReadPreference::validate(@read)
|
691
685
|
|
686
|
+
@default_db = opts.delete(:default_db) || DEFAULT_DB_NAME
|
692
687
|
@tag_sets = opts.delete(:tag_sets) || []
|
693
688
|
@acceptable_latency = opts.delete(:secondary_acceptable_latency_ms) || 15
|
694
689
|
|
data/lib/mongo/networking.rb
CHANGED
@@ -208,7 +208,7 @@ module Mongo
|
|
208
208
|
raise Mongo::OperationFailure, "Query response returned CURSOR_NOT_FOUND. " +
|
209
209
|
"Either an invalid cursor was specified, or the cursor may have timed out on the server."
|
210
210
|
elsif flags & Mongo::Constants::REPLY_QUERY_FAILURE != 0
|
211
|
-
#
|
211
|
+
# Mongo query reply failures are handled in Cursor#next.
|
212
212
|
end
|
213
213
|
end
|
214
214
|
|
data/lib/mongo/util/node.rb
CHANGED
@@ -105,7 +105,14 @@ module Mongo
|
|
105
105
|
@last_state = @config['ismaster'] ? :primary : :other
|
106
106
|
end
|
107
107
|
|
108
|
-
|
108
|
+
if @client.connect_timeout
|
109
|
+
Timeout::timeout(@client.connect_timeout, OperationTimeout) do
|
110
|
+
@config = @client['admin'].command({:ismaster => 1}, :socket => @socket)
|
111
|
+
end
|
112
|
+
else
|
113
|
+
@config = @client['admin'].command({:ismaster => 1}, :socket => @socket)
|
114
|
+
end
|
115
|
+
|
109
116
|
update_max_sizes
|
110
117
|
|
111
118
|
if @config['msg']
|
@@ -13,11 +13,12 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
require 'cgi'
|
16
|
+
require 'uri'
|
16
17
|
|
17
18
|
module Mongo
|
18
19
|
class URIParser
|
19
20
|
|
20
|
-
USER_REGEX = /(
|
21
|
+
USER_REGEX = /(.+)/
|
21
22
|
PASS_REGEX = /([^@,]+)/
|
22
23
|
AUTH_REGEX = /(#{USER_REGEX}:#{PASS_REGEX}@)?/
|
23
24
|
|
@@ -108,7 +109,7 @@ module Mongo
|
|
108
109
|
:w => lambda { |arg| Mongo::Support.is_i?(arg) ? arg.to_i : arg.to_sym },
|
109
110
|
:wtimeout => lambda { |arg| arg.to_i },
|
110
111
|
:wtimeoutms => lambda { |arg| arg.to_i }
|
111
|
-
|
112
|
+
}
|
112
113
|
|
113
114
|
attr_reader :auths,
|
114
115
|
:connect,
|
@@ -253,6 +254,7 @@ module Mongo
|
|
253
254
|
opts[:name] = replicaset
|
254
255
|
end
|
255
256
|
|
257
|
+
opts[:default_db] = @db
|
256
258
|
opts[:connect] = connect?
|
257
259
|
|
258
260
|
opts
|
@@ -277,7 +279,7 @@ module Mongo
|
|
277
279
|
uname = matches[2]
|
278
280
|
pwd = matches[3]
|
279
281
|
hosturis = matches[4].split(',')
|
280
|
-
db
|
282
|
+
@db = matches[8]
|
281
283
|
|
282
284
|
hosturis.each do |hosturi|
|
283
285
|
# If port is present, use it, otherwise use default port
|
@@ -295,11 +297,16 @@ module Mongo
|
|
295
297
|
raise MongoArgumentError, "No nodes specified. Please ensure that you've provided at least one node."
|
296
298
|
end
|
297
299
|
|
298
|
-
if uname && pwd && db
|
299
|
-
auths << {
|
300
|
+
if uname && pwd && @db
|
301
|
+
auths << {
|
302
|
+
:db_name => @db,
|
303
|
+
:username => URI.unescape(uname),
|
304
|
+
:password => URI.unescape(pwd)
|
305
|
+
}
|
300
306
|
elsif uname || pwd
|
301
|
-
raise MongoArgumentError,
|
302
|
-
|
307
|
+
raise MongoArgumentError, 'MongoDB URI must include username, ' +
|
308
|
+
'password, and db if username and ' +
|
309
|
+
'password are specified.'
|
303
310
|
end
|
304
311
|
end
|
305
312
|
|
@@ -292,6 +292,18 @@ class TestCollection < Test::Unit::TestCase
|
|
292
292
|
return conn.db(MONGO_TEST_DB)["test"]
|
293
293
|
end
|
294
294
|
|
295
|
+
def test_non_operation_failure_halts_insertion_with_continue_on_error
|
296
|
+
coll = limited_collection
|
297
|
+
coll.stubs(:send_insert_message).raises(OperationTimeout).times(1)
|
298
|
+
docs = []
|
299
|
+
10.times do
|
300
|
+
docs << {'foo' => 'a' * 950}
|
301
|
+
end
|
302
|
+
assert_raise OperationTimeout do
|
303
|
+
coll.insert(docs, :continue_on_error => true)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
295
307
|
def test_chunking_batch_insert
|
296
308
|
docs = []
|
297
309
|
10.times do
|
@@ -955,7 +967,9 @@ class TestCollection < Test::Unit::TestCase
|
|
955
967
|
@@test << { :a => 2, :processed => false }
|
956
968
|
@@test << { :a => 3, :processed => false }
|
957
969
|
|
958
|
-
@@test.find_and_modify(:query => {},
|
970
|
+
@@test.find_and_modify(:query => {},
|
971
|
+
:sort => [['a', -1]],
|
972
|
+
:update => {"$set" => {:processed => true}})
|
959
973
|
|
960
974
|
assert @@test.find_one({:a => 3})['processed']
|
961
975
|
end
|
@@ -969,6 +983,21 @@ class TestCollection < Test::Unit::TestCase
|
|
969
983
|
@@test.find_and_modify(:blimey => {})
|
970
984
|
end
|
971
985
|
end
|
986
|
+
|
987
|
+
def test_find_and_modify_with_full_response
|
988
|
+
@@test << { :a => 1, :processed => false }
|
989
|
+
@@test << { :a => 2, :processed => false }
|
990
|
+
@@test << { :a => 3, :processed => false }
|
991
|
+
|
992
|
+
doc = @@test.find_and_modify(:query => {},
|
993
|
+
:sort => [['a', -1]],
|
994
|
+
:update => {"$set" => {:processed => true}},
|
995
|
+
:full_response => true,
|
996
|
+
:new => true)
|
997
|
+
|
998
|
+
assert doc['value']['processed']
|
999
|
+
assert ['ok', 'value', 'lastErrorObject'].all? { |key| doc.key?(key) }
|
1000
|
+
end
|
972
1001
|
end
|
973
1002
|
|
974
1003
|
if @@version >= "1.3.5"
|
@@ -122,12 +122,24 @@ class TestConnection < Test::Unit::TestCase
|
|
122
122
|
ENV['MONGODB_URI'] = "mongodb://#{host_port}/"
|
123
123
|
con = MongoClient.from_uri
|
124
124
|
db = con.db
|
125
|
-
assert_equal db.name,
|
125
|
+
assert_equal db.name, MongoClient::DEFAULT_DB_NAME
|
126
126
|
ensure
|
127
127
|
ENV['MONGODB_URI'] = old_mongodb_uri
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
+
def test_db_from_uri_from_string_param
|
132
|
+
db_name = "_database"
|
133
|
+
db = MongoClient.from_uri("mongodb://#{host_port}/#{db_name}").db
|
134
|
+
assert_equal db.name, db_name
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_db_from_uri_from_string_param_no_db_name
|
138
|
+
db = MongoClient.from_uri("mongodb://#{host_port}").db
|
139
|
+
assert_equal db.name, MongoClient::DEFAULT_DB_NAME
|
140
|
+
end
|
141
|
+
|
142
|
+
|
131
143
|
def test_server_version
|
132
144
|
assert_match(/\d\.\d+(\.\d+)?/, @client.server_version.to_s)
|
133
145
|
end
|
@@ -91,6 +91,34 @@ class CursorTest < Test::Unit::TestCase
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
+
def test_exhaust_after_limit_error
|
95
|
+
c = Cursor.new(@@coll, :limit => 17)
|
96
|
+
assert_raise MongoArgumentError do
|
97
|
+
c.add_option(OP_QUERY_EXHAUST)
|
98
|
+
end
|
99
|
+
|
100
|
+
assert_raise MongoArgumentError do
|
101
|
+
c.add_option(OP_QUERY_EXHAUST + OP_QUERY_SLAVE_OK)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_limit_after_exhaust_error
|
106
|
+
c = Cursor.new(@@coll)
|
107
|
+
c.add_option(OP_QUERY_EXHAUST)
|
108
|
+
assert_raise MongoArgumentError do
|
109
|
+
c.limit(17)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_exhaust_with_mongos
|
114
|
+
@@connection.expects(:mongos?).returns(:true)
|
115
|
+
c = Cursor.new(@@coll)
|
116
|
+
|
117
|
+
assert_raise MongoArgumentError do
|
118
|
+
c.add_option(OP_QUERY_EXHAUST)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
94
122
|
def test_inspect
|
95
123
|
selector = {:a => 1}
|
96
124
|
cursor = @@coll.find(selector)
|
data/test/functional/uri_test.rb
CHANGED
@@ -53,6 +53,18 @@ class URITest < Test::Unit::TestCase
|
|
53
53
|
assert_equal "b:ob", parser.auths[0][:username]
|
54
54
|
end
|
55
55
|
|
56
|
+
def test_username_with_encoded_symbol
|
57
|
+
parser = Mongo::URIParser.new('mongodb://f%40o:bar@localhost/admin')
|
58
|
+
username = parser.auths.first[:username]
|
59
|
+
assert_equal 'f@o', username
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_password_with_encoded_symbol
|
63
|
+
parser = Mongo::URIParser.new('mongodb://foo:b%40r@localhost/admin')
|
64
|
+
password = parser.auths.first[:password]
|
65
|
+
assert_equal 'b@r', password
|
66
|
+
end
|
67
|
+
|
56
68
|
def test_passwords_contain_no_commas
|
57
69
|
assert_raise MongoArgumentError do
|
58
70
|
Mongo::URIParser.new('mongodb://bob:a,b@a.example.com:27018/test')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.9.
|
4
|
+
version: 1.9.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tyler Brock
|
@@ -33,7 +33,7 @@ cert_chain:
|
|
33
33
|
8v7zLF2XliYbfurYIwkcXs8yPn8ggApBIy9bX6VJxRs/l2+UvqzaHIFaFy/F8/GP
|
34
34
|
RNTuXsVG5NDACo7Q
|
35
35
|
-----END CERTIFICATE-----
|
36
|
-
date: 2013-
|
36
|
+
date: 2013-08-21 00:00:00.000000000 Z
|
37
37
|
dependencies:
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: bson
|
@@ -41,14 +41,14 @@ dependencies:
|
|
41
41
|
requirements:
|
42
42
|
- - ~>
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: 1.9.
|
44
|
+
version: 1.9.2
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
47
|
version_requirements: !ruby/object:Gem::Requirement
|
48
48
|
requirements:
|
49
49
|
- - ~>
|
50
50
|
- !ruby/object:Gem::Version
|
51
|
-
version: 1.9.
|
51
|
+
version: 1.9.2
|
52
52
|
description: A Ruby driver for MongoDB. For more information about Mongo, see http://www.mongodb.org.
|
53
53
|
email: mongodb-dev@googlegroups.com
|
54
54
|
executables:
|
@@ -171,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
171
|
version: '0'
|
172
172
|
requirements: []
|
173
173
|
rubyforge_project: mongo
|
174
|
-
rubygems_version: 2.0.
|
174
|
+
rubygems_version: 2.0.7
|
175
175
|
signing_key:
|
176
176
|
specification_version: 4
|
177
177
|
summary: Ruby driver for MongoDB
|
metadata.gz.sig
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
����d��
|
2
|
+
;5����ʼn_�]d�e��41E�.HR�g��v�鸁\
|