redis 3.0.5 → 3.0.6
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 +8 -8
- data/.gitignore +9 -8
- data/.travis.yml +10 -8
- data/CHANGELOG.md +4 -0
- data/Rakefile +21 -10
- data/lib/redis.rb +229 -72
- data/lib/redis/version.rb +1 -1
- data/test/blocking_commands_test.rb +1 -1
- data/test/command_map_test.rb +1 -1
- data/test/commands_on_hashes_test.rb +1 -1
- data/test/commands_on_lists_test.rb +1 -1
- data/test/commands_on_sets_test.rb +1 -1
- data/test/commands_on_sorted_sets_test.rb +1 -1
- data/test/commands_on_strings_test.rb +14 -14
- data/test/commands_on_value_types_test.rb +1 -1
- data/test/connection_handling_test.rb +1 -1
- data/test/distributed_blocking_commands_test.rb +1 -1
- data/test/distributed_commands_on_hashes_test.rb +1 -1
- data/test/distributed_commands_on_lists_test.rb +1 -1
- data/test/distributed_commands_on_sets_test.rb +1 -1
- data/test/distributed_commands_on_sorted_sets_test.rb +1 -1
- data/test/distributed_commands_on_strings_test.rb +7 -7
- data/test/distributed_commands_on_value_types_test.rb +1 -1
- data/test/distributed_commands_requiring_clustering_test.rb +14 -14
- data/test/distributed_connection_handling_test.rb +1 -1
- data/test/distributed_internals_test.rb +1 -1
- data/test/distributed_key_tags_test.rb +1 -1
- data/test/distributed_persistence_control_commands_test.rb +1 -1
- data/test/distributed_publish_subscribe_test.rb +1 -1
- data/test/distributed_remote_server_control_commands_test.rb +15 -15
- data/test/distributed_scripting_test.rb +57 -57
- data/test/distributed_sorting_test.rb +1 -1
- data/test/distributed_test.rb +1 -1
- data/test/distributed_transactions_test.rb +1 -1
- data/test/encoding_test.rb +1 -1
- data/test/error_replies_test.rb +1 -1
- data/test/helper.rb +10 -1
- data/test/helper_test.rb +1 -1
- data/test/internals_test.rb +20 -9
- data/test/lint/hashes.rb +17 -17
- data/test/lint/lists.rb +10 -10
- data/test/lint/sets.rb +14 -14
- data/test/lint/sorted_sets.rb +30 -30
- data/test/lint/strings.rb +45 -45
- data/test/lint/value_types.rb +32 -32
- data/test/persistence_control_commands_test.rb +1 -1
- data/test/pipelining_commands_test.rb +1 -1
- data/test/publish_subscribe_test.rb +1 -1
- data/test/remote_server_control_commands_test.rb +7 -7
- data/test/scanning_test.rb +413 -0
- data/test/scripting_test.rb +45 -45
- data/test/sorting_test.rb +1 -1
- data/test/thread_safety_test.rb +1 -1
- data/test/transactions_test.rb +1 -1
- data/test/unknown_commands_test.rb +1 -1
- data/test/url_param_test.rb +1 -1
- metadata +6 -4
- data/test/db/.gitignore +0 -1
data/test/lint/value_types.rb
CHANGED
@@ -33,11 +33,11 @@ module Lint
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_pexpire
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
target_version "2.5.4" do
|
37
|
+
r.set("foo", "s1")
|
38
|
+
assert r.pexpire("foo", 2000)
|
39
|
+
assert_in_range 0..2, r.ttl("foo")
|
40
|
+
end
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_expireat
|
@@ -47,11 +47,11 @@ module Lint
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_pexpireat
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
target_version "2.5.4" do
|
51
|
+
r.set("foo", "s1")
|
52
|
+
assert r.pexpireat("foo", (Time.now + 2).to_i * 1_000)
|
53
|
+
assert_in_range 0..2, r.ttl("foo")
|
54
|
+
end
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_persist
|
@@ -69,31 +69,31 @@ module Lint
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def test_pttl
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
target_version "2.5.4" do
|
73
|
+
r.set("foo", "s1")
|
74
|
+
r.expire("foo", 2)
|
75
|
+
assert_in_range 1..2000, r.pttl("foo")
|
76
|
+
end
|
77
77
|
end
|
78
78
|
|
79
79
|
def test_dump_and_restore
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
80
|
+
target_version "2.5.7" do
|
81
|
+
r.set("foo", "a")
|
82
|
+
v = r.dump("foo")
|
83
|
+
r.del("foo")
|
84
|
+
|
85
|
+
assert r.restore("foo", 1000, v)
|
86
|
+
assert_equal "a", r.get("foo")
|
87
|
+
assert [0, 1].include? r.ttl("foo")
|
88
|
+
|
89
|
+
r.rpush("bar", ["b", "c", "d"])
|
90
|
+
w = r.dump("bar")
|
91
|
+
r.del("bar")
|
92
|
+
|
93
|
+
assert r.restore("bar", 1000, w)
|
94
|
+
assert_equal ["b", "c", "d"], r.lrange("bar", 0, -1)
|
95
|
+
assert [0, 1].include? r.ttl("bar")
|
96
|
+
end
|
97
97
|
end
|
98
98
|
|
99
99
|
def test_move
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
require "helper"
|
3
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
4
4
|
|
5
5
|
class TestRemoteServerControlCommands < Test::Unit::TestCase
|
6
6
|
|
@@ -26,13 +26,13 @@ class TestRemoteServerControlCommands < Test::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_info_commandstats
|
29
|
-
|
29
|
+
target_version "2.5.7" do
|
30
|
+
r.config(:resetstat)
|
31
|
+
r.ping
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
result = r.info(:commandstats)
|
35
|
-
assert_equal "1", result["ping"]["calls"]
|
33
|
+
result = r.info(:commandstats)
|
34
|
+
assert_equal "1", result["ping"]["calls"]
|
35
|
+
end
|
36
36
|
end
|
37
37
|
|
38
38
|
def test_monitor_redis_lt_2_5_0
|
@@ -0,0 +1,413 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
unless defined?(Enumerator)
|
6
|
+
Enumerator = Enumerable::Enumerator
|
7
|
+
end
|
8
|
+
|
9
|
+
class TestScanning < Test::Unit::TestCase
|
10
|
+
|
11
|
+
include Helper::Client
|
12
|
+
|
13
|
+
def test_scan_basic
|
14
|
+
target_version "2.7.105" do
|
15
|
+
r.debug :populate, 1000
|
16
|
+
|
17
|
+
cursor = 0
|
18
|
+
all_keys = []
|
19
|
+
loop {
|
20
|
+
cursor, keys = r.scan cursor
|
21
|
+
all_keys += keys
|
22
|
+
break if cursor == "0"
|
23
|
+
}
|
24
|
+
|
25
|
+
assert_equal 1000, all_keys.uniq.size
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_scan_count
|
30
|
+
target_version "2.7.105" do
|
31
|
+
r.debug :populate, 1000
|
32
|
+
|
33
|
+
cursor = 0
|
34
|
+
all_keys = []
|
35
|
+
loop {
|
36
|
+
cursor, keys = r.scan cursor, :count => 5
|
37
|
+
all_keys += keys
|
38
|
+
break if cursor == "0"
|
39
|
+
}
|
40
|
+
|
41
|
+
assert_equal 1000, all_keys.uniq.size
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_scan_match
|
46
|
+
target_version "2.7.105" do
|
47
|
+
r.debug :populate, 1000
|
48
|
+
|
49
|
+
cursor = 0
|
50
|
+
all_keys = []
|
51
|
+
loop {
|
52
|
+
cursor, keys = r.scan cursor, :match => "key:1??"
|
53
|
+
all_keys += keys
|
54
|
+
break if cursor == "0"
|
55
|
+
}
|
56
|
+
|
57
|
+
assert_equal 100, all_keys.uniq.size
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_scan_each_enumerator
|
62
|
+
target_version "2.7.105" do
|
63
|
+
|
64
|
+
r.debug :populate, 1000
|
65
|
+
|
66
|
+
scan_enumerator = r.scan_each
|
67
|
+
assert_equal true, scan_enumerator.is_a?(::Enumerator)
|
68
|
+
|
69
|
+
keys_from_scan = scan_enumerator.to_a.uniq
|
70
|
+
all_keys = r.keys "*"
|
71
|
+
|
72
|
+
assert all_keys.sort == keys_from_scan.sort
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_scan_each_enumerator_match
|
77
|
+
target_version "2.7.105" do
|
78
|
+
|
79
|
+
r.debug :populate, 1000
|
80
|
+
|
81
|
+
keys_from_scan = r.scan_each(:match => "key:1??").to_a.uniq
|
82
|
+
all_keys = r.keys "key:1??"
|
83
|
+
|
84
|
+
assert all_keys.sort == keys_from_scan.sort
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_scan_each_block
|
89
|
+
target_version "2.7.105" do
|
90
|
+
|
91
|
+
r.debug :populate, 100
|
92
|
+
|
93
|
+
keys_from_scan = []
|
94
|
+
r.scan_each {|key|
|
95
|
+
keys_from_scan << key
|
96
|
+
}
|
97
|
+
|
98
|
+
all_keys = r.keys "*"
|
99
|
+
|
100
|
+
assert all_keys.sort == keys_from_scan.uniq.sort
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_scan_each_block_match
|
105
|
+
target_version "2.7.105" do
|
106
|
+
|
107
|
+
r.debug :populate, 100
|
108
|
+
|
109
|
+
keys_from_scan = []
|
110
|
+
r.scan_each(:match => "key:1?") {|key|
|
111
|
+
keys_from_scan << key
|
112
|
+
}
|
113
|
+
|
114
|
+
all_keys = r.keys "key:1?"
|
115
|
+
|
116
|
+
assert all_keys.sort == keys_from_scan.uniq.sort
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_sscan_with_encoding
|
121
|
+
target_version "2.7.105" do
|
122
|
+
[:intset, :hashtable].each do |enc|
|
123
|
+
r.del "set"
|
124
|
+
|
125
|
+
prefix = ""
|
126
|
+
prefix = "ele:" if enc == :hashtable
|
127
|
+
|
128
|
+
elements = []
|
129
|
+
100.times { |j| elements << "#{prefix}#{j}" }
|
130
|
+
|
131
|
+
r.sadd "set", elements
|
132
|
+
|
133
|
+
assert_equal enc.to_s, r.object("encoding", "set")
|
134
|
+
|
135
|
+
cursor = 0
|
136
|
+
all_keys = []
|
137
|
+
loop {
|
138
|
+
cursor, keys = r.sscan "set", cursor
|
139
|
+
all_keys += keys
|
140
|
+
break if cursor == "0"
|
141
|
+
}
|
142
|
+
|
143
|
+
assert_equal 100, all_keys.uniq.size
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_sscan_each_enumerator
|
149
|
+
target_version "2.7.105" do
|
150
|
+
elements = []
|
151
|
+
100.times { |j| elements << "ele:#{j}" }
|
152
|
+
r.sadd "set", elements
|
153
|
+
|
154
|
+
scan_enumerator = r.sscan_each("set")
|
155
|
+
assert_equal true, scan_enumerator.is_a?(::Enumerator)
|
156
|
+
|
157
|
+
keys_from_scan = scan_enumerator.to_a.uniq
|
158
|
+
all_keys = r.smembers("set")
|
159
|
+
|
160
|
+
assert all_keys.sort == keys_from_scan.sort
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_sscan_each_enumerator_match
|
165
|
+
target_version "2.7.105" do
|
166
|
+
elements = []
|
167
|
+
100.times { |j| elements << "ele:#{j}" }
|
168
|
+
r.sadd "set", elements
|
169
|
+
|
170
|
+
keys_from_scan = r.sscan_each("set", :match => "ele:1?").to_a.uniq
|
171
|
+
|
172
|
+
all_keys = r.smembers("set").grep(/^ele:1.$/)
|
173
|
+
|
174
|
+
assert all_keys.sort == keys_from_scan.sort
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_sscan_each_enumerator_block
|
179
|
+
target_version "2.7.105" do
|
180
|
+
elements = []
|
181
|
+
100.times { |j| elements << "ele:#{j}" }
|
182
|
+
r.sadd "set", elements
|
183
|
+
|
184
|
+
keys_from_scan = []
|
185
|
+
r.sscan_each("set") do |key|
|
186
|
+
keys_from_scan << key
|
187
|
+
end
|
188
|
+
|
189
|
+
all_keys = r.smembers("set")
|
190
|
+
|
191
|
+
assert all_keys.sort == keys_from_scan.uniq.sort
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_sscan_each_enumerator_block_match
|
196
|
+
target_version "2.7.105" do
|
197
|
+
elements = []
|
198
|
+
100.times { |j| elements << "ele:#{j}" }
|
199
|
+
r.sadd "set", elements
|
200
|
+
|
201
|
+
keys_from_scan = []
|
202
|
+
r.sscan_each("set", :match => "ele:1?") do |key|
|
203
|
+
keys_from_scan << key
|
204
|
+
end
|
205
|
+
|
206
|
+
all_keys = r.smembers("set").grep(/^ele:1.$/)
|
207
|
+
|
208
|
+
assert all_keys.sort == keys_from_scan.uniq.sort
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_hscan_with_encoding
|
213
|
+
target_version "2.7.105" do
|
214
|
+
[:ziplist, :hashtable].each do |enc|
|
215
|
+
r.del "set"
|
216
|
+
|
217
|
+
count = 1000
|
218
|
+
count = 30 if enc == :ziplist
|
219
|
+
|
220
|
+
elements = []
|
221
|
+
count.times { |j| elements << "key:#{j}" << j.to_s }
|
222
|
+
|
223
|
+
r.hmset "hash", *elements
|
224
|
+
|
225
|
+
assert_equal enc.to_s, r.object("encoding", "hash")
|
226
|
+
|
227
|
+
cursor = 0
|
228
|
+
all_key_values = []
|
229
|
+
loop {
|
230
|
+
cursor, key_values = r.hscan "hash", cursor
|
231
|
+
all_key_values.concat key_values
|
232
|
+
break if cursor == "0"
|
233
|
+
}
|
234
|
+
|
235
|
+
keys2 = []
|
236
|
+
all_key_values.each do |k, v|
|
237
|
+
assert_equal "key:#{v}", k
|
238
|
+
keys2 << k
|
239
|
+
end
|
240
|
+
|
241
|
+
assert_equal count, keys2.uniq.size
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_hscan_each_enumerator
|
247
|
+
target_version "2.7.105" do
|
248
|
+
count = 1000
|
249
|
+
elements = []
|
250
|
+
count.times { |j| elements << "key:#{j}" << j.to_s }
|
251
|
+
r.hmset "hash", *elements
|
252
|
+
|
253
|
+
scan_enumerator = r.hscan_each("hash")
|
254
|
+
assert_equal true, scan_enumerator.is_a?(::Enumerator)
|
255
|
+
|
256
|
+
keys_from_scan = scan_enumerator.to_a.uniq
|
257
|
+
all_keys = r.hgetall("hash").to_a
|
258
|
+
|
259
|
+
assert all_keys.sort == keys_from_scan.sort
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_hscan_each_enumerator_match
|
264
|
+
target_version "2.7.105" do
|
265
|
+
count = 100
|
266
|
+
elements = []
|
267
|
+
count.times { |j| elements << "key:#{j}" << j.to_s }
|
268
|
+
r.hmset "hash", *elements
|
269
|
+
|
270
|
+
keys_from_scan = r.hscan_each("hash", :match => "key:1?").to_a.uniq
|
271
|
+
all_keys = r.hgetall("hash").to_a.select{|k,v| k =~ /^key:1.$/}
|
272
|
+
|
273
|
+
assert all_keys.sort == keys_from_scan.sort
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_hscan_each_block
|
278
|
+
target_version "2.7.105" do
|
279
|
+
count = 1000
|
280
|
+
elements = []
|
281
|
+
count.times { |j| elements << "key:#{j}" << j.to_s }
|
282
|
+
r.hmset "hash", *elements
|
283
|
+
|
284
|
+
keys_from_scan = []
|
285
|
+
r.hscan_each("hash") do |field, value|
|
286
|
+
keys_from_scan << [field, value]
|
287
|
+
end
|
288
|
+
all_keys = r.hgetall("hash").to_a
|
289
|
+
|
290
|
+
assert all_keys.sort == keys_from_scan.uniq.sort
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def test_hscan_each_block_match
|
295
|
+
target_version "2.7.105" do
|
296
|
+
count = 1000
|
297
|
+
elements = []
|
298
|
+
count.times { |j| elements << "key:#{j}" << j.to_s }
|
299
|
+
r.hmset "hash", *elements
|
300
|
+
|
301
|
+
keys_from_scan = []
|
302
|
+
r.hscan_each("hash", :match => "key:1?") do |field, value|
|
303
|
+
keys_from_scan << [field, value]
|
304
|
+
end
|
305
|
+
all_keys = r.hgetall("hash").to_a.select{|k,v| k =~ /^key:1.$/}
|
306
|
+
|
307
|
+
assert all_keys.sort == keys_from_scan.uniq.sort
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def test_zscan_with_encoding
|
312
|
+
target_version "2.7.105" do
|
313
|
+
[:ziplist, :skiplist].each do |enc|
|
314
|
+
r.del "zset"
|
315
|
+
|
316
|
+
count = 1000
|
317
|
+
count = 30 if enc == :ziplist
|
318
|
+
|
319
|
+
elements = []
|
320
|
+
count.times { |j| elements << j << "key:#{j}" }
|
321
|
+
|
322
|
+
r.zadd "zset", elements
|
323
|
+
|
324
|
+
assert_equal enc.to_s, r.object("encoding", "zset")
|
325
|
+
|
326
|
+
cursor = 0
|
327
|
+
all_key_scores = []
|
328
|
+
loop {
|
329
|
+
cursor, key_scores = r.zscan "zset", cursor
|
330
|
+
all_key_scores.concat key_scores
|
331
|
+
break if cursor == "0"
|
332
|
+
}
|
333
|
+
|
334
|
+
keys2 = []
|
335
|
+
all_key_scores.each do |k, v|
|
336
|
+
assert_equal true, v.is_a?(Float)
|
337
|
+
assert_equal "key:#{Integer(v)}", k
|
338
|
+
keys2 << k
|
339
|
+
end
|
340
|
+
|
341
|
+
assert_equal count, keys2.uniq.size
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def test_zscan_each_enumerator
|
347
|
+
target_version "2.7.105" do
|
348
|
+
count = 1000
|
349
|
+
elements = []
|
350
|
+
count.times { |j| elements << j << "key:#{j}" }
|
351
|
+
r.zadd "zset", elements
|
352
|
+
|
353
|
+
scan_enumerator = r.zscan_each "zset"
|
354
|
+
assert_equal true, scan_enumerator.is_a?(::Enumerator)
|
355
|
+
|
356
|
+
scores_from_scan = scan_enumerator.to_a.uniq
|
357
|
+
member_scores = r.zrange("zset", 0, -1, :with_scores => true)
|
358
|
+
|
359
|
+
assert member_scores.sort == scores_from_scan.sort
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_zscan_each_enumerator_match
|
364
|
+
target_version "2.7.105" do
|
365
|
+
count = 1000
|
366
|
+
elements = []
|
367
|
+
count.times { |j| elements << j << "key:#{j}" }
|
368
|
+
r.zadd "zset", elements
|
369
|
+
|
370
|
+
scores_from_scan = r.zscan_each("zset", :match => "key:1??").to_a.uniq
|
371
|
+
member_scores = r.zrange("zset", 0, -1, :with_scores => true)
|
372
|
+
filtered_members = member_scores.select{|k,s| k =~ /^key:1..$/}
|
373
|
+
|
374
|
+
assert filtered_members.sort == scores_from_scan.sort
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def test_zscan_each_block
|
379
|
+
target_version "2.7.105" do
|
380
|
+
count = 1000
|
381
|
+
elements = []
|
382
|
+
count.times { |j| elements << j << "key:#{j}" }
|
383
|
+
r.zadd "zset", elements
|
384
|
+
|
385
|
+
scores_from_scan = []
|
386
|
+
r.zscan_each("zset") do |member, score|
|
387
|
+
scores_from_scan << [member, score]
|
388
|
+
end
|
389
|
+
member_scores = r.zrange("zset", 0, -1, :with_scores => true)
|
390
|
+
|
391
|
+
assert member_scores.sort == scores_from_scan.sort
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_zscan_each_block_match
|
396
|
+
target_version "2.7.105" do
|
397
|
+
count = 1000
|
398
|
+
elements = []
|
399
|
+
count.times { |j| elements << j << "key:#{j}" }
|
400
|
+
r.zadd "zset", elements
|
401
|
+
|
402
|
+
scores_from_scan = []
|
403
|
+
r.zscan_each("zset", :match => "key:1??") do |member, score|
|
404
|
+
scores_from_scan << [member, score]
|
405
|
+
end
|
406
|
+
member_scores = r.zrange("zset", 0, -1, :with_scores => true)
|
407
|
+
filtered_members = member_scores.select{|k,s| k =~ /^key:1..$/}
|
408
|
+
|
409
|
+
assert filtered_members.sort == scores_from_scan.sort
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|