redis 3.3.5 → 4.0.0.rc1
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/.travis.yml +32 -50
- data/CHANGELOG.md +7 -9
- data/Gemfile +0 -1
- data/README.md +31 -75
- data/benchmarking/logging.rb +1 -1
- data/lib/redis.rb +31 -35
- data/lib/redis/client.rb +13 -8
- data/lib/redis/connection.rb +2 -2
- data/lib/redis/connection/command_helper.rb +2 -8
- data/lib/redis/connection/hiredis.rb +2 -2
- data/lib/redis/connection/ruby.rb +7 -27
- data/lib/redis/connection/synchrony.rb +3 -3
- data/lib/redis/pipeline.rb +0 -6
- data/lib/redis/version.rb +1 -1
- data/makefile +42 -0
- data/redis.gemspec +4 -8
- data/test/bitpos_test.rb +13 -19
- data/test/blocking_commands_test.rb +147 -6
- data/test/client_test.rb +1 -1
- data/test/command_map_test.rb +3 -5
- data/test/commands_on_hashes_test.rb +158 -5
- data/test/commands_on_hyper_log_log_test.rb +55 -6
- data/test/commands_on_lists_test.rb +139 -5
- data/test/commands_on_sets_test.rb +136 -5
- data/test/commands_on_sorted_sets_test.rb +312 -5
- data/test/commands_on_strings_test.rb +243 -6
- data/test/commands_on_value_types_test.rb +120 -7
- data/test/connection_handling_test.rb +5 -7
- data/test/encoding_test.rb +4 -8
- data/test/error_replies_test.rb +2 -4
- data/test/fork_safety_test.rb +1 -6
- data/test/helper.rb +9 -62
- data/test/helper_test.rb +1 -3
- data/test/internals_test.rb +67 -49
- data/test/persistence_control_commands_test.rb +1 -3
- data/test/pipelining_commands_test.rb +4 -8
- data/test/publish_subscribe_test.rb +1 -3
- data/test/remote_server_control_commands_test.rb +61 -4
- data/test/scanning_test.rb +1 -7
- data/test/scripting_test.rb +1 -3
- data/test/sentinel_command_test.rb +1 -3
- data/test/sentinel_test.rb +1 -3
- data/test/sorting_test.rb +1 -3
- data/test/ssl_test.rb +45 -49
- data/test/support/connection/hiredis.rb +1 -1
- data/test/support/connection/ruby.rb +1 -1
- data/test/support/connection/synchrony.rb +1 -1
- data/test/synchrony_driver.rb +6 -9
- data/test/thread_safety_test.rb +1 -3
- data/test/transactions_test.rb +1 -3
- data/test/unknown_commands_test.rb +1 -3
- data/test/url_param_test.rb +44 -46
- metadata +31 -77
- data/Rakefile +0 -87
- data/examples/dist_redis.rb +0 -43
- data/lib/redis/distributed.rb +0 -873
- data/lib/redis/hash_ring.rb +0 -132
- data/test/connection_test.rb +0 -57
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -66
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
data/lib/redis/hash_ring.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
require 'zlib'
|
2
|
-
|
3
|
-
class Redis
|
4
|
-
class HashRing
|
5
|
-
|
6
|
-
POINTS_PER_SERVER = 160 # this is the default in libmemcached
|
7
|
-
|
8
|
-
attr_reader :ring, :sorted_keys, :replicas, :nodes
|
9
|
-
|
10
|
-
# nodes is a list of objects that have a proper to_s representation.
|
11
|
-
# replicas indicates how many virtual points should be used pr. node,
|
12
|
-
# replicas are required to improve the distribution.
|
13
|
-
def initialize(nodes=[], replicas=POINTS_PER_SERVER)
|
14
|
-
@replicas = replicas
|
15
|
-
@ring = {}
|
16
|
-
@nodes = []
|
17
|
-
@sorted_keys = []
|
18
|
-
nodes.each do |node|
|
19
|
-
add_node(node)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# Adds a `node` to the hash ring (including a number of replicas).
|
24
|
-
def add_node(node)
|
25
|
-
@nodes << node
|
26
|
-
@replicas.times do |i|
|
27
|
-
key = Zlib.crc32("#{node.id}:#{i}")
|
28
|
-
raise "Node ID collision" if @ring.has_key?(key)
|
29
|
-
@ring[key] = node
|
30
|
-
@sorted_keys << key
|
31
|
-
end
|
32
|
-
@sorted_keys.sort!
|
33
|
-
end
|
34
|
-
|
35
|
-
def remove_node(node)
|
36
|
-
@nodes.reject!{|n| n.id == node.id}
|
37
|
-
@replicas.times do |i|
|
38
|
-
key = Zlib.crc32("#{node.id}:#{i}")
|
39
|
-
@ring.delete(key)
|
40
|
-
@sorted_keys.reject! {|k| k == key}
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# get the node in the hash ring for this key
|
45
|
-
def get_node(key)
|
46
|
-
get_node_pos(key)[0]
|
47
|
-
end
|
48
|
-
|
49
|
-
def get_node_pos(key)
|
50
|
-
return [nil,nil] if @ring.size == 0
|
51
|
-
crc = Zlib.crc32(key)
|
52
|
-
idx = HashRing.binary_search(@sorted_keys, crc)
|
53
|
-
return [@ring[@sorted_keys[idx]], idx]
|
54
|
-
end
|
55
|
-
|
56
|
-
def iter_nodes(key)
|
57
|
-
return [nil,nil] if @ring.size == 0
|
58
|
-
_, pos = get_node_pos(key)
|
59
|
-
@ring.size.times do |n|
|
60
|
-
yield @ring[@sorted_keys[(pos+n) % @ring.size]]
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
class << self
|
65
|
-
|
66
|
-
# gem install RubyInline to use this code
|
67
|
-
# Native extension to perform the binary search within the hashring.
|
68
|
-
# There's a pure ruby version below so this is purely optional
|
69
|
-
# for performance. In testing 20k gets and sets, the native
|
70
|
-
# binary search shaved about 12% off the runtime (9sec -> 8sec).
|
71
|
-
begin
|
72
|
-
require 'inline'
|
73
|
-
inline do |builder|
|
74
|
-
builder.c <<-EOM
|
75
|
-
int binary_search(VALUE ary, unsigned int r) {
|
76
|
-
int upper = RARRAY_LEN(ary) - 1;
|
77
|
-
int lower = 0;
|
78
|
-
int idx = 0;
|
79
|
-
|
80
|
-
while (lower <= upper) {
|
81
|
-
idx = (lower + upper) / 2;
|
82
|
-
|
83
|
-
VALUE continuumValue = RARRAY_PTR(ary)[idx];
|
84
|
-
unsigned int l = NUM2UINT(continuumValue);
|
85
|
-
if (l == r) {
|
86
|
-
return idx;
|
87
|
-
}
|
88
|
-
else if (l > r) {
|
89
|
-
upper = idx - 1;
|
90
|
-
}
|
91
|
-
else {
|
92
|
-
lower = idx + 1;
|
93
|
-
}
|
94
|
-
}
|
95
|
-
if (upper < 0) {
|
96
|
-
upper = RARRAY_LEN(ary) - 1;
|
97
|
-
}
|
98
|
-
return upper;
|
99
|
-
}
|
100
|
-
EOM
|
101
|
-
end
|
102
|
-
rescue Exception
|
103
|
-
# Find the closest index in HashRing with value <= the given value
|
104
|
-
def binary_search(ary, value, &block)
|
105
|
-
upper = ary.size - 1
|
106
|
-
lower = 0
|
107
|
-
idx = 0
|
108
|
-
|
109
|
-
while(lower <= upper) do
|
110
|
-
idx = (lower + upper) / 2
|
111
|
-
comp = ary[idx] <=> value
|
112
|
-
|
113
|
-
if comp == 0
|
114
|
-
return idx
|
115
|
-
elsif comp > 0
|
116
|
-
upper = idx - 1
|
117
|
-
else
|
118
|
-
lower = idx + 1
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
if upper < 0
|
123
|
-
upper = ary.size - 1
|
124
|
-
end
|
125
|
-
return upper
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
end
|
132
|
-
end
|
data/test/connection_test.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require_relative "helper"
|
2
|
-
|
3
|
-
class TestConnection < Test::Unit::TestCase
|
4
|
-
|
5
|
-
include Helper::Client
|
6
|
-
|
7
|
-
def test_provides_a_meaningful_inspect
|
8
|
-
assert_equal "#<Redis client v#{Redis::VERSION} for redis://127.0.0.1:#{PORT}/15>", r.inspect
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_connection_information
|
12
|
-
assert_equal "127.0.0.1", r.connection.fetch(:host)
|
13
|
-
assert_equal 6381, r.connection.fetch(:port)
|
14
|
-
assert_equal 15, r.connection.fetch(:db)
|
15
|
-
assert_equal "127.0.0.1:6381", r.connection.fetch(:location)
|
16
|
-
assert_equal "redis://127.0.0.1:6381/15", r.connection.fetch(:id)
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_default_id_with_host_and_port
|
20
|
-
redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0))
|
21
|
-
assert_equal "redis://host:1234/0", redis.connection.fetch(:id)
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_default_id_with_host_and_port_and_explicit_scheme
|
25
|
-
redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0, :scheme => "foo"))
|
26
|
-
assert_equal "redis://host:1234/0", redis.connection.fetch(:id)
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_default_id_with_path
|
30
|
-
redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0))
|
31
|
-
assert_equal "redis:///tmp/redis.sock/0", redis.connection.fetch(:id)
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_default_id_with_path_and_explicit_scheme
|
35
|
-
redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0, :scheme => "foo"))
|
36
|
-
assert_equal "redis:///tmp/redis.sock/0", redis.connection.fetch(:id)
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_override_id
|
40
|
-
redis = Redis.new(OPTIONS.merge(:id => "test"))
|
41
|
-
assert_equal "test", redis.connection.fetch(:id)
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_id_inside_multi
|
45
|
-
redis = Redis.new(OPTIONS)
|
46
|
-
id = nil
|
47
|
-
connection_id = nil
|
48
|
-
|
49
|
-
redis.multi do
|
50
|
-
id = redis.id
|
51
|
-
connection_id = redis.connection.fetch(:id)
|
52
|
-
end
|
53
|
-
|
54
|
-
assert_equal "redis://127.0.0.1:6381/15", id
|
55
|
-
assert_equal "redis://127.0.0.1:6381/15", connection_id
|
56
|
-
end
|
57
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
require "lint/blocking_commands"
|
5
|
-
|
6
|
-
class TestDistributedBlockingCommands < Test::Unit::TestCase
|
7
|
-
|
8
|
-
include Helper::Distributed
|
9
|
-
include Lint::BlockingCommands
|
10
|
-
|
11
|
-
def test_blpop_raises
|
12
|
-
assert_raises(Redis::Distributed::CannotDistribute) do
|
13
|
-
r.blpop(["foo", "bar"])
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_blpop_raises_with_old_prototype
|
18
|
-
assert_raises(Redis::Distributed::CannotDistribute) do
|
19
|
-
r.blpop("foo", "bar", 0)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_brpop_raises
|
24
|
-
assert_raises(Redis::Distributed::CannotDistribute) do
|
25
|
-
r.brpop(["foo", "bar"])
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_brpop_raises_with_old_prototype
|
30
|
-
assert_raises(Redis::Distributed::CannotDistribute) do
|
31
|
-
r.brpop("foo", "bar", 0)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_brpoplpush_raises
|
36
|
-
assert_raises(Redis::Distributed::CannotDistribute) do
|
37
|
-
r.brpoplpush("foo", "bar")
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_brpoplpush_raises_with_old_prototype
|
42
|
-
assert_raises(Redis::Distributed::CannotDistribute) do
|
43
|
-
r.brpoplpush("foo", "bar", 0)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
require "lint/hyper_log_log"
|
5
|
-
|
6
|
-
class TestDistributedCommandsOnHyperLogLog < Test::Unit::TestCase
|
7
|
-
|
8
|
-
include Helper::Distributed
|
9
|
-
include Lint::HyperLogLog
|
10
|
-
|
11
|
-
def test_pfmerge
|
12
|
-
target_version "2.8.9" do
|
13
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
14
|
-
r.pfadd "foo", "s1"
|
15
|
-
r.pfadd "bar", "s2"
|
16
|
-
|
17
|
-
assert r.pfmerge("res", "foo", "bar")
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_pfcount_multiple_keys_diff_nodes
|
23
|
-
target_version "2.8.9" do
|
24
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
25
|
-
r.pfadd "foo", "s1"
|
26
|
-
r.pfadd "bar", "s2"
|
27
|
-
|
28
|
-
assert r.pfcount("res", "foo", "bar")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
require "lint/lists"
|
5
|
-
|
6
|
-
class TestDistributedCommandsOnLists < Test::Unit::TestCase
|
7
|
-
|
8
|
-
include Helper::Distributed
|
9
|
-
include Lint::Lists
|
10
|
-
|
11
|
-
def test_rpoplpush
|
12
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
13
|
-
r.rpoplpush("foo", "bar")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_brpoplpush
|
18
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
19
|
-
r.brpoplpush("foo", "bar", :timeout => 1)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
require "lint/sets"
|
5
|
-
|
6
|
-
class TestDistributedCommandsOnSets < Test::Unit::TestCase
|
7
|
-
|
8
|
-
include Helper::Distributed
|
9
|
-
include Lint::Sets
|
10
|
-
|
11
|
-
def test_smove
|
12
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
13
|
-
r.sadd "foo", "s1"
|
14
|
-
r.sadd "bar", "s2"
|
15
|
-
|
16
|
-
r.smove("foo", "bar", "s1")
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_sinter
|
21
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
22
|
-
r.sadd "foo", "s1"
|
23
|
-
r.sadd "foo", "s2"
|
24
|
-
r.sadd "bar", "s2"
|
25
|
-
|
26
|
-
r.sinter("foo", "bar")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_sinterstore
|
31
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
32
|
-
r.sadd "foo", "s1"
|
33
|
-
r.sadd "foo", "s2"
|
34
|
-
r.sadd "bar", "s2"
|
35
|
-
|
36
|
-
r.sinterstore("baz", "foo", "bar")
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_sunion
|
41
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
42
|
-
r.sadd "foo", "s1"
|
43
|
-
r.sadd "foo", "s2"
|
44
|
-
r.sadd "bar", "s2"
|
45
|
-
r.sadd "bar", "s3"
|
46
|
-
|
47
|
-
r.sunion("foo", "bar")
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_sunionstore
|
52
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
53
|
-
r.sadd "foo", "s1"
|
54
|
-
r.sadd "foo", "s2"
|
55
|
-
r.sadd "bar", "s2"
|
56
|
-
r.sadd "bar", "s3"
|
57
|
-
|
58
|
-
r.sunionstore("baz", "foo", "bar")
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def test_sdiff
|
63
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
64
|
-
r.sadd "foo", "s1"
|
65
|
-
r.sadd "foo", "s2"
|
66
|
-
r.sadd "bar", "s2"
|
67
|
-
r.sadd "bar", "s3"
|
68
|
-
|
69
|
-
r.sdiff("foo", "bar")
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def test_sdiffstore
|
74
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
75
|
-
r.sadd "foo", "s1"
|
76
|
-
r.sadd "foo", "s2"
|
77
|
-
r.sadd "bar", "s2"
|
78
|
-
r.sadd "bar", "s3"
|
79
|
-
|
80
|
-
r.sdiffstore("baz", "foo", "bar")
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
require "lint/sorted_sets"
|
5
|
-
|
6
|
-
class TestDistributedCommandsOnSortedSets < Test::Unit::TestCase
|
7
|
-
|
8
|
-
include Helper::Distributed
|
9
|
-
include Lint::SortedSets
|
10
|
-
|
11
|
-
def test_zcount
|
12
|
-
r.zadd "foo", 1, "s1"
|
13
|
-
r.zadd "foo", 2, "s2"
|
14
|
-
r.zadd "foo", 3, "s3"
|
15
|
-
|
16
|
-
assert_equal 2, r.zcount("foo", 2, 3)
|
17
|
-
end
|
18
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
4
|
-
require "lint/strings"
|
5
|
-
|
6
|
-
class TestDistributedCommandsOnStrings < Test::Unit::TestCase
|
7
|
-
|
8
|
-
include Helper::Distributed
|
9
|
-
include Lint::Strings
|
10
|
-
|
11
|
-
def test_mget
|
12
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
13
|
-
r.mget("foo", "bar")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_mget_mapped
|
18
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
19
|
-
r.mapped_mget("foo", "bar")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_mset
|
24
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
25
|
-
r.mset(:foo, "s1", :bar, "s2")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_mset_mapped
|
30
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
31
|
-
r.mapped_mset(:foo => "s1", :bar => "s2")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_msetnx
|
36
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
37
|
-
r.set("foo", "s1")
|
38
|
-
r.msetnx(:foo, "s2", :bar, "s3")
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_msetnx_mapped
|
43
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
44
|
-
r.set("foo", "s1")
|
45
|
-
r.mapped_msetnx(:foo => "s2", :bar => "s3")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_bitop
|
50
|
-
target_version "2.5.10" do
|
51
|
-
assert_raise Redis::Distributed::CannotDistribute do
|
52
|
-
r.set("foo", "a")
|
53
|
-
r.set("bar", "b")
|
54
|
-
|
55
|
-
r.bitop(:and, "foo&bar", "foo", "bar")
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|