mock_redis 0.4.1 → 0.5.0
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.
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -1
- data/lib/mock_redis/assertions.rb +2 -2
- data/lib/mock_redis/database.rb +16 -8
- data/lib/mock_redis/expire_wrapper.rb +2 -2
- data/lib/mock_redis/hash_methods.rb +12 -6
- data/lib/mock_redis/list_methods.rb +10 -8
- data/lib/mock_redis/multi_db_wrapper.rb +3 -3
- data/lib/mock_redis/pipelined_wrapper.rb +45 -0
- data/lib/mock_redis/set_methods.rb +14 -5
- data/lib/mock_redis/string_methods.rb +6 -6
- data/lib/mock_redis/transaction_wrapper.rb +13 -8
- data/lib/mock_redis/version.rb +1 -1
- data/lib/mock_redis/zset_methods.rb +52 -22
- data/lib/mock_redis.rb +92 -5
- data/mock_redis.gemspec +1 -1
- data/spec/cloning_spec.rb +1 -1
- data/spec/commands/blpop_spec.rb +3 -3
- data/spec/commands/brpop_spec.rb +3 -3
- data/spec/commands/brpoplpush_spec.rb +3 -3
- data/spec/commands/hdel_spec.rb +5 -0
- data/spec/commands/lpush_spec.rb +7 -0
- data/spec/commands/mapped_hmset_spec.rb +7 -1
- data/spec/commands/move_spec.rb +2 -2
- data/spec/commands/msetnx_spec.rb +2 -2
- data/spec/commands/pipelined_spec.rb +12 -0
- data/spec/commands/rename_spec.rb +7 -0
- data/spec/commands/renamenx_spec.rb +7 -0
- data/spec/commands/rpush_spec.rb +7 -0
- data/spec/commands/sadd_spec.rb +1 -1
- data/spec/commands/sdiff_spec.rb +1 -1
- data/spec/commands/sdiffstore_spec.rb +3 -3
- data/spec/commands/sinterstore_spec.rb +2 -2
- data/spec/commands/smembers_spec.rb +4 -4
- data/spec/commands/srem_spec.rb +5 -0
- data/spec/commands/sunion_spec.rb +2 -2
- data/spec/commands/sunionstore_spec.rb +3 -3
- data/spec/commands/watch_spec.rb +2 -2
- data/spec/commands/zadd_spec.rb +6 -1
- data/spec/commands/zcount_spec.rb +8 -0
- data/spec/commands/zincrby_spec.rb +4 -4
- data/spec/commands/zinterstore_spec.rb +7 -7
- data/spec/commands/zrange_spec.rb +6 -2
- data/spec/commands/zrangebyscore_spec.rb +2 -2
- data/spec/commands/zrem_spec.rb +5 -0
- data/spec/commands/zrevrange_spec.rb +2 -2
- data/spec/commands/zrevrangebyscore_spec.rb +2 -2
- data/spec/commands/zscore_spec.rb +2 -2
- data/spec/commands/zunionstore_spec.rb +9 -9
- data/spec/mock_redis_spec.rb +21 -0
- data/spec/support/redis_multiplexer.rb +23 -19
- data/spec/transactions_spec.rb +17 -0
- metadata +67 -84
- data/lib/mock_redis/distributed.rb +0 -6
@@ -19,12 +19,12 @@ describe "#zrevrange(key, start, stop [, :with_scores => true])" do
|
|
19
19
|
|
20
20
|
it "returns the scores when :with_scores is specified" do
|
21
21
|
@redises.zrevrange(@key, 2, 3, :with_scores => true).
|
22
|
-
should == ["Adams",
|
22
|
+
should == [["Adams", 2.0], ["Washington", 1.0]]
|
23
23
|
end
|
24
24
|
|
25
25
|
it "returns the scores when :withscores is specified" do
|
26
26
|
@redises.zrevrange(@key, 2, 3, :withscores => true).
|
27
|
-
should == ["Adams",
|
27
|
+
should == [["Adams", 2.0], ["Washington", 1.0]]
|
28
28
|
end
|
29
29
|
|
30
30
|
it_should_behave_like "a zset-only command"
|
@@ -15,12 +15,12 @@ describe "#zrevrangebyscore(key, start, stop [:with_scores => true] [:limit => [
|
|
15
15
|
|
16
16
|
it "returns the scores when :with_scores is specified" do
|
17
17
|
@redises.zrevrangebyscore(@key, 4, 3, :with_scores => true).
|
18
|
-
should ==
|
18
|
+
should == [["Madison", 4.0], ["Jefferson", 3.0]]
|
19
19
|
end
|
20
20
|
|
21
21
|
it "returns the scores when :withscores is specified" do
|
22
22
|
@redises.zrevrangebyscore(@key, 4, 3, :withscores => true).
|
23
|
-
should ==
|
23
|
+
should == [["Madison", 4.0], ["Jefferson", 3.0]]
|
24
24
|
end
|
25
25
|
|
26
26
|
it "treats +inf as positive infinity" do
|
@@ -5,13 +5,13 @@ describe "#zscore(key, member)" do
|
|
5
5
|
|
6
6
|
it "returns the score as a string" do
|
7
7
|
@redises.zadd(@key, 0.25, 'foo').should be_true
|
8
|
-
@redises.zscore(@key, 'foo').should ==
|
8
|
+
@redises.zscore(@key, 'foo').should == 0.25
|
9
9
|
end
|
10
10
|
|
11
11
|
it "handles integer members correctly" do
|
12
12
|
member = 11
|
13
13
|
@redises.zadd(@key, 0.25, member).should be_true
|
14
|
-
@redises.zscore(@key, member).should ==
|
14
|
+
@redises.zscore(@key, member).should == 0.25
|
15
15
|
end
|
16
16
|
|
17
17
|
it "returns nil if member is not present in the set" do
|
@@ -24,7 +24,7 @@ describe "#zunionstore(destination, keys, [:weights => [w,w,], [:aggregate => :s
|
|
24
24
|
it "sums the members' scores by default" do
|
25
25
|
@redises.zunionstore(@dest, [@set1, @set2, @set3])
|
26
26
|
@redises.zrange(@dest, 0, -1, :with_scores => true).should ==
|
27
|
-
|
27
|
+
[["one", 3.0], ["three", 3.0], ["two", 4.0]]
|
28
28
|
end
|
29
29
|
|
30
30
|
it "removes existing elements in destination" do
|
@@ -32,7 +32,7 @@ describe "#zunionstore(destination, keys, [:weights => [w,w,], [:aggregate => :s
|
|
32
32
|
|
33
33
|
@redises.zunionstore(@dest, [@set1])
|
34
34
|
@redises.zrange(@dest, 0, -1, :with_scores => true).should ==
|
35
|
-
|
35
|
+
[["one", 1.0]]
|
36
36
|
end
|
37
37
|
|
38
38
|
it "raises an error if keys is empty" do
|
@@ -45,7 +45,7 @@ describe "#zunionstore(destination, keys, [:weights => [w,w,], [:aggregate => :s
|
|
45
45
|
it "multiplies the scores by the weights while aggregating" do
|
46
46
|
@redises.zunionstore(@dest, [@set1, @set2, @set3], :weights => [2, 3, 5])
|
47
47
|
@redises.zrange(@dest, 0, -1, :with_scores => true).should ==
|
48
|
-
|
48
|
+
[["one", 10.0], ["three", 15.0], ["two", 16.0]]
|
49
49
|
end
|
50
50
|
|
51
51
|
it "raises an error if the number of weights != the number of keys" do
|
@@ -69,30 +69,30 @@ describe "#zunionstore(destination, keys, [:weights => [w,w,], [:aggregate => :s
|
|
69
69
|
it "aggregates scores with min when :aggregate => :min is specified" do
|
70
70
|
@redises.zunionstore(@dest, [@bigs, @smalls], :aggregate => :min)
|
71
71
|
@redises.zrange(@dest, 0, -1, :with_scores => true).should ==
|
72
|
-
|
72
|
+
[["bert", 1.0], ["ernie", 2.0]]
|
73
73
|
end
|
74
74
|
|
75
75
|
it "aggregates scores with max when :aggregate => :max is specified" do
|
76
76
|
@redises.zunionstore(@dest, [@bigs, @smalls], :aggregate => :max)
|
77
77
|
@redises.zrange(@dest, 0, -1, :with_scores => true).should ==
|
78
|
-
|
78
|
+
[["bert", 100.0], ["ernie", 200.0]]
|
79
79
|
end
|
80
80
|
|
81
81
|
it "ignores scores for missing members" do
|
82
82
|
@redises.zadd(@smalls, 3, 'grover')
|
83
83
|
@redises.zunionstore(@dest, [@bigs, @smalls], :aggregate => :min)
|
84
|
-
@redises.zscore(@dest, 'grover').should ==
|
84
|
+
@redises.zscore(@dest, 'grover').should == 3.0
|
85
85
|
|
86
86
|
@redises.zunionstore(@dest, [@bigs, @smalls], :aggregate => :max)
|
87
|
-
@redises.zscore(@dest, 'grover').should ==
|
87
|
+
@redises.zscore(@dest, 'grover').should == 3.0
|
88
88
|
end
|
89
89
|
|
90
90
|
it "allows 'min', 'MIN', etc. as aliases for :min" do
|
91
91
|
@redises.zunionstore(@dest, [@bigs, @smalls], :aggregate => 'min')
|
92
|
-
@redises.zscore(@dest, 'bert').should ==
|
92
|
+
@redises.zscore(@dest, 'bert').should == 1.0
|
93
93
|
|
94
94
|
@redises.zunionstore(@dest, [@bigs, @smalls], :aggregate => 'MIN')
|
95
|
-
@redises.zscore(@dest, 'bert').should ==
|
95
|
+
@redises.zscore(@dest, 'bert').should == 1.0
|
96
96
|
end
|
97
97
|
|
98
98
|
it "raises an error for unknown aggregation function" do
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MockRedis do
|
4
|
+
|
5
|
+
context "with passed options" do
|
6
|
+
before do
|
7
|
+
@mock = MockRedis.new(:url => "redis://127.0.0.1:6379/1")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should correctly parse options" do
|
11
|
+
@mock.host.should == "127.0.0.1"
|
12
|
+
@mock.port.should == 6379
|
13
|
+
@mock.db.should == 1
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have an id equal to redis url" do
|
17
|
+
@mock.id.should == "redis://127.0.0.1:6379/1"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -11,13 +11,30 @@ class RedisMultiplexer < BlankSlate
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def method_missing(method, *args, &blk)
|
14
|
-
|
15
|
-
|
14
|
+
# if we're in a Redis command that accepts a block, and we execute more redis commands, ONLY execute them
|
15
|
+
# on the Redis implementation that the block came from.
|
16
|
+
# e.g. if a pipelined command is started on a MockRedis object, DON'T send commands inside the pipelined block
|
17
|
+
# to the real Redis object, as that one WON'T be inside a pipelined command, and we'll see weird behaviour
|
18
|
+
if blk
|
19
|
+
@in_mock_block = true
|
20
|
+
@in_redis_block = false
|
21
|
+
end
|
22
|
+
mock_retval, mock_error = catch_errors { @in_redis_block ? :no_op : @mock_redis.send(method, *args, &blk) }
|
23
|
+
|
24
|
+
if blk
|
25
|
+
@in_mock_block = false
|
26
|
+
@in_redis_block = true
|
27
|
+
end
|
28
|
+
real_retval, real_error = catch_errors { @in_mock_block ? :no_op : @real_redis.send(method, *args, &blk) }
|
16
29
|
|
17
|
-
|
18
|
-
|
30
|
+
if blk
|
31
|
+
@in_mock_block = false
|
32
|
+
@in_redis_block = false
|
33
|
+
end
|
19
34
|
|
20
|
-
if (
|
35
|
+
if (mock_retval == :no_op || real_retval == :no_op)
|
36
|
+
# ignore, we were inside a block (like pipelined)
|
37
|
+
elsif (!equalish?(mock_retval, real_retval) && !mock_error && !real_error)
|
21
38
|
# no exceptions, just different behavior
|
22
39
|
raise MismatchedResponse,
|
23
40
|
"Mock failure: responses not equal.\n" +
|
@@ -49,7 +66,7 @@ class RedisMultiplexer < BlankSlate
|
|
49
66
|
if a == b
|
50
67
|
true
|
51
68
|
elsif a.is_a?(Array) && b.is_a?(Array)
|
52
|
-
a.
|
69
|
+
a.collect(&:to_s).sort == b.collect(&:to_s).sort
|
53
70
|
elsif a.is_a?(Exception) && b.is_a?(Exception)
|
54
71
|
a.class == b.class && a.message == b.message
|
55
72
|
else
|
@@ -60,19 +77,6 @@ class RedisMultiplexer < BlankSlate
|
|
60
77
|
def mock() @mock_redis end
|
61
78
|
def real() @real_redis end
|
62
79
|
|
63
|
-
# Some commands require special handling due to nondeterminism in
|
64
|
-
# the returned values.
|
65
|
-
def handle_special_cases(method, value)
|
66
|
-
case method.to_s
|
67
|
-
when 'keys', 'hkeys', 'sdiff', 'sinter', 'smembers', 'sunion'
|
68
|
-
# The order is irrelevant, but [a,b] != [b,a] in Ruby, so we
|
69
|
-
# sort the returned values so we can ignore the order.
|
70
|
-
value.sort if value
|
71
|
-
else
|
72
|
-
value
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
80
|
# Used in cleanup before() blocks.
|
77
81
|
def send_without_checking(method, *args)
|
78
82
|
@mock_redis.send(method, *args)
|
data/spec/transactions_spec.rb
CHANGED
@@ -16,6 +16,23 @@ describe 'transactions (multi/exec/discard)' do
|
|
16
16
|
@redises.multi
|
17
17
|
end.should raise_error(RuntimeError)
|
18
18
|
end
|
19
|
+
|
20
|
+
it "cleanup the state of tansaction wrapper if an exception occurs while a transaction is being processed" do
|
21
|
+
lambda do
|
22
|
+
@redises.mock.multi do |r|
|
23
|
+
raise "i'm a command that fails"
|
24
|
+
end
|
25
|
+
end.should raise_error(RuntimeError)
|
26
|
+
|
27
|
+
# before the fix this used to raised a #<RuntimeError: ERR MULTI calls can not be nested>
|
28
|
+
lambda do
|
29
|
+
@redises.mock.multi do |r|
|
30
|
+
# do stuff that succeed
|
31
|
+
s.set(nil, 'string')
|
32
|
+
end
|
33
|
+
end.should_not raise_error(RuntimeError)
|
34
|
+
|
35
|
+
end
|
19
36
|
end
|
20
37
|
|
21
38
|
context "#blocks" do
|
metadata
CHANGED
@@ -1,86 +1,73 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: mock_redis
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 4
|
9
|
-
- 1
|
10
|
-
segments_generated: true
|
11
|
-
version: 0.4.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
prerelease:
|
12
6
|
platform: ruby
|
13
|
-
authors:
|
7
|
+
authors:
|
14
8
|
- Causes Engineering
|
15
9
|
- Samuel Merritt
|
16
10
|
autorequire:
|
17
11
|
bindir: bin
|
18
12
|
cert_chain: []
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
version_requirements: &id001 !ruby/object:Gem::Requirement
|
13
|
+
date: 2012-08-27 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
25
18
|
none: false
|
26
|
-
requirements:
|
19
|
+
requirements:
|
27
20
|
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 63
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
- 9
|
33
|
-
- 2
|
34
|
-
segments_generated: true
|
21
|
+
- !ruby/object:Gem::Version
|
35
22
|
version: 0.9.2
|
36
|
-
requirement: *id001
|
37
|
-
name: rake
|
38
|
-
prerelease: false
|
39
23
|
type: :development
|
40
|
-
|
41
|
-
version_requirements:
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
26
|
none: false
|
43
|
-
requirements:
|
27
|
+
requirements:
|
44
28
|
- - ~>
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
|
47
|
-
|
48
|
-
- 2
|
49
|
-
- 2
|
50
|
-
- 1
|
51
|
-
segments_generated: true
|
52
|
-
version: 2.2.1
|
53
|
-
requirement: *id002
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 0.9.2
|
31
|
+
- !ruby/object:Gem::Dependency
|
54
32
|
name: redis
|
55
|
-
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 3.0.0
|
56
39
|
type: :development
|
57
|
-
|
58
|
-
version_requirements:
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
42
|
none: false
|
60
|
-
requirements:
|
43
|
+
requirements:
|
61
44
|
- - ~>
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
|
64
|
-
|
65
|
-
- 2
|
66
|
-
- 6
|
67
|
-
- 0
|
68
|
-
segments_generated: true
|
69
|
-
version: 2.6.0
|
70
|
-
requirement: *id003
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.0.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
71
48
|
name: rspec
|
72
|
-
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.6.0
|
73
55
|
type: :development
|
74
|
-
|
75
|
-
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.6.0
|
63
|
+
description: Instantiate one with `redis = MockRedis.new` and treat it like you would
|
64
|
+
a normal Redis object. It supports all the usual Redis operations.
|
65
|
+
email:
|
76
66
|
- eng@causes.com
|
77
67
|
executables: []
|
78
|
-
|
79
68
|
extensions: []
|
80
|
-
|
81
69
|
extra_rdoc_files: []
|
82
|
-
|
83
|
-
files:
|
70
|
+
files:
|
84
71
|
- .gitignore
|
85
72
|
- .mailmap
|
86
73
|
- .rspec
|
@@ -92,12 +79,12 @@ files:
|
|
92
79
|
- lib/mock_redis.rb
|
93
80
|
- lib/mock_redis/assertions.rb
|
94
81
|
- lib/mock_redis/database.rb
|
95
|
-
- lib/mock_redis/distributed.rb
|
96
82
|
- lib/mock_redis/exceptions.rb
|
97
83
|
- lib/mock_redis/expire_wrapper.rb
|
98
84
|
- lib/mock_redis/hash_methods.rb
|
99
85
|
- lib/mock_redis/list_methods.rb
|
100
86
|
- lib/mock_redis/multi_db_wrapper.rb
|
87
|
+
- lib/mock_redis/pipelined_wrapper.rb
|
101
88
|
- lib/mock_redis/set_methods.rb
|
102
89
|
- lib/mock_redis/string_methods.rb
|
103
90
|
- lib/mock_redis/transaction_wrapper.rb
|
@@ -216,6 +203,7 @@ files:
|
|
216
203
|
- spec/commands/zrevrank_spec.rb
|
217
204
|
- spec/commands/zscore_spec.rb
|
218
205
|
- spec/commands/zunionstore_spec.rb
|
206
|
+
- spec/mock_redis_spec.rb
|
219
207
|
- spec/spec_helper.rb
|
220
208
|
- spec/support/redis_multiplexer.rb
|
221
209
|
- spec/support/shared_examples/only_operates_on_hashes.rb
|
@@ -224,43 +212,37 @@ files:
|
|
224
212
|
- spec/support/shared_examples/only_operates_on_strings.rb
|
225
213
|
- spec/support/shared_examples/only_operates_on_zsets.rb
|
226
214
|
- spec/transactions_spec.rb
|
227
|
-
has_rdoc: true
|
228
215
|
homepage: https://github.com/causes/mock_redis
|
229
216
|
licenses: []
|
230
|
-
|
231
217
|
post_install_message:
|
232
218
|
rdoc_options: []
|
233
|
-
|
234
|
-
require_paths:
|
219
|
+
require_paths:
|
235
220
|
- lib
|
236
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
221
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
237
222
|
none: false
|
238
|
-
requirements:
|
239
|
-
- -
|
240
|
-
- !ruby/object:Gem::Version
|
241
|
-
|
242
|
-
segments:
|
223
|
+
requirements:
|
224
|
+
- - ! '>='
|
225
|
+
- !ruby/object:Gem::Version
|
226
|
+
version: '0'
|
227
|
+
segments:
|
243
228
|
- 0
|
244
|
-
|
245
|
-
|
246
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
|
+
hash: 2382509530287634625
|
230
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
247
231
|
none: false
|
248
|
-
requirements:
|
249
|
-
- -
|
250
|
-
- !ruby/object:Gem::Version
|
251
|
-
|
252
|
-
segments:
|
232
|
+
requirements:
|
233
|
+
- - ! '>='
|
234
|
+
- !ruby/object:Gem::Version
|
235
|
+
version: '0'
|
236
|
+
segments:
|
253
237
|
- 0
|
254
|
-
|
255
|
-
version: "0"
|
238
|
+
hash: 2382509530287634625
|
256
239
|
requirements: []
|
257
|
-
|
258
240
|
rubyforge_project:
|
259
|
-
rubygems_version: 1.
|
241
|
+
rubygems_version: 1.8.23
|
260
242
|
signing_key:
|
261
243
|
specification_version: 3
|
262
244
|
summary: Redis mock that just lives in memory; useful for testing.
|
263
|
-
test_files:
|
245
|
+
test_files:
|
264
246
|
- spec/cloning_spec.rb
|
265
247
|
- spec/commands/append_spec.rb
|
266
248
|
- spec/commands/auth_spec.rb
|
@@ -370,6 +352,7 @@ test_files:
|
|
370
352
|
- spec/commands/zrevrank_spec.rb
|
371
353
|
- spec/commands/zscore_spec.rb
|
372
354
|
- spec/commands/zunionstore_spec.rb
|
355
|
+
- spec/mock_redis_spec.rb
|
373
356
|
- spec/spec_helper.rb
|
374
357
|
- spec/support/redis_multiplexer.rb
|
375
358
|
- spec/support/shared_examples/only_operates_on_hashes.rb
|