bloom_filter 0.6.4 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/bloom_filter.gemspec +4 -2
- data/lib/bloom_filter/client.rb +52 -36
- data/test/bloom_filter_client_test.rb +18 -8
- data/test/bloom_filter_integration_test.rb +42 -0
- metadata +4 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.5
|
data/bloom_filter.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bloom_filter}
|
8
|
-
s.version = "0.6.
|
8
|
+
s.version = "0.6.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Arya Asemanfar"]
|
12
|
-
s.date = %q{2010-02-
|
12
|
+
s.date = %q{2010-02-04}
|
13
13
|
s.default_executable = %q{bloom_filter_server}
|
14
14
|
s.description = %q{}
|
15
15
|
s.email = %q{misterfunnyarsal@gmail.com}
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/bloom_filter/server.rb",
|
35
35
|
"test/bloom_filter_bit_vector_test.rb",
|
36
36
|
"test/bloom_filter_client_test.rb",
|
37
|
+
"test/bloom_filter_integration_test.rb",
|
37
38
|
"test/bloom_filter_server_test.rb",
|
38
39
|
"test/bloom_filter_test.rb",
|
39
40
|
"test/test_helper.rb"
|
@@ -46,6 +47,7 @@ Gem::Specification.new do |s|
|
|
46
47
|
s.test_files = [
|
47
48
|
"test/bloom_filter_bit_vector_test.rb",
|
48
49
|
"test/bloom_filter_client_test.rb",
|
50
|
+
"test/bloom_filter_integration_test.rb",
|
49
51
|
"test/bloom_filter_server_test.rb",
|
50
52
|
"test/bloom_filter_test.rb",
|
51
53
|
"test/test_helper.rb"
|
data/lib/bloom_filter/client.rb
CHANGED
@@ -4,6 +4,7 @@ require 'bloom_filter/bit_vector'
|
|
4
4
|
|
5
5
|
class BloomFilter
|
6
6
|
class Timeout < StandardError; end
|
7
|
+
class ConnectionError < StandardError; end
|
7
8
|
|
8
9
|
class Client
|
9
10
|
PACK_N = "N"
|
@@ -12,61 +13,59 @@ class BloomFilter
|
|
12
13
|
@host, @port = host, port
|
13
14
|
@timeout = options[:timeout]
|
14
15
|
@raise_on_timeout = options[:raise_on_timeout] || false
|
16
|
+
@raise_on_error = options[:raise_on_error] || false
|
15
17
|
self.connect
|
16
18
|
end
|
17
19
|
|
18
20
|
def add(el)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@socket.read(@socket.read(4).unpack(PACK_N).first) == Protocol::TRUE
|
21
|
+
with_socket_or_default(false) do
|
22
|
+
el = el.to_s
|
23
|
+
@socket.write("#{[el.size + 1].pack(PACK_N)}#{Protocol::ADD}#{el}")
|
24
|
+
read_response == Protocol::TRUE
|
24
25
|
end
|
25
26
|
end
|
26
27
|
alias_method :<<, :add
|
27
28
|
|
28
29
|
def include?(el)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@socket.read(@socket.read(4).unpack(PACK_N).first) == Protocol::TRUE
|
30
|
+
with_socket_or_default(true) do
|
31
|
+
el = el.to_s
|
32
|
+
@socket.write("#{[el.size + 1].pack(PACK_N)}#{Protocol::INCLUDE}#{el}")
|
33
|
+
read_response == Protocol::TRUE
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
def &(els)
|
38
|
-
self.connect
|
39
38
|
if els.size == 1
|
40
39
|
el = els.first
|
41
40
|
self.include?(el) ? [el] : []
|
42
41
|
else
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
42
|
+
with_socket_or_default(els) do
|
43
|
+
elements = els.collect { |el| el.to_s }.join(Protocol::DEFAULT_SEPARATOR)
|
44
|
+
@socket.write("#{[elements.size + 1].pack(PACK_N)}#{Protocol::INCLUDE_MANY}#{elements}")
|
45
|
+
|
46
|
+
read_response do |response|
|
47
|
+
response = BitVector.new(els.size, response)
|
48
|
+
result = []
|
49
|
+
els.size.times do |i|
|
50
|
+
result << els[i] if response[i] == 1
|
51
|
+
end
|
52
|
+
result
|
53
|
+
end # read_response
|
54
|
+
end # with_socket_or_default
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
57
58
|
def dump(path, timeout = nil)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@socket.read(@socket.read(4).unpack(PACK_N).first) == Protocol::TRUE
|
59
|
+
with_socket_or_default(false) do
|
60
|
+
@socket.write("#{[path.size + 1].pack(PACK_N)}#{Protocol::DUMP}#{path}")
|
61
|
+
read_response(timeout) == Protocol::TRUE
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
65
|
def load(path, timeout = nil)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
@socket.read(@socket.read(4).unpack(PACK_N).first) == Protocol::TRUE
|
66
|
+
with_socket_or_default(false) do
|
67
|
+
@socket.write("#{[path.size + 1].pack(PACK_N)}#{Protocol::LOAD}#{path}")
|
68
|
+
read_response(timeout) == Protocol::TRUE
|
70
69
|
end
|
71
70
|
end
|
72
71
|
|
@@ -74,7 +73,7 @@ class BloomFilter
|
|
74
73
|
unless self.connected?
|
75
74
|
@socket = begin
|
76
75
|
TCPSocket.new(@host, @port)
|
77
|
-
rescue
|
76
|
+
rescue Errno::ECONNREFUSED
|
78
77
|
nil
|
79
78
|
end
|
80
79
|
end
|
@@ -94,15 +93,32 @@ class BloomFilter
|
|
94
93
|
end
|
95
94
|
|
96
95
|
private
|
97
|
-
def
|
96
|
+
def read_response(timeout = nil, &block)
|
98
97
|
ready = IO.select([@socket], nil, nil, timeout || @timeout)
|
99
98
|
if ready
|
100
|
-
|
101
|
-
|
102
|
-
|
99
|
+
size = @socket.read(4)
|
100
|
+
raise BloomFilter::ConnectionError if size.nil? || size.length < 4
|
101
|
+
size = size.unpack(PACK_N).first
|
102
|
+
response = @socket.read(size)
|
103
|
+
raise BloomFilter::ConnectionError if response.nil? || response.length < size
|
104
|
+
block ? block.call(response) : response
|
105
|
+
else
|
103
106
|
raise BloomFilter::Timeout
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def with_socket_or_default(default, &block)
|
111
|
+
self.connect
|
112
|
+
if self.connected?
|
113
|
+
begin
|
114
|
+
yield
|
115
|
+
rescue BloomFilter::Timeout
|
116
|
+
self.disconnect
|
117
|
+
@raise_on_timeout ? raise : default
|
118
|
+
rescue BloomFilter::ConnectionError, Errno::EPIPE
|
119
|
+
@raise_on_error ? raise : default
|
120
|
+
end
|
104
121
|
else
|
105
|
-
self.disconnect
|
106
122
|
default
|
107
123
|
end
|
108
124
|
end
|
@@ -110,17 +110,27 @@ class BloomFilterClientTest < Test::Unit::TestCase
|
|
110
110
|
should "reconnect if not connected before making a call" do
|
111
111
|
socket = mock()
|
112
112
|
|
113
|
-
socket.
|
113
|
+
socket.stubs(:closed?).returns(true, true, false)
|
114
114
|
TCPSocket.expects(:new).with("localhost", 4111).returns(socket).twice
|
115
115
|
IO.stubs(:select).returns(socket)
|
116
|
+
socket.stubs(:write, :read)
|
117
|
+
|
116
118
|
client = BloomFilter::Client.new("localhost", 4111, :timeout => 1, :raise_on_timeout => true)
|
117
|
-
|
118
|
-
socket.expects(:write).with("#{[6].pack("N")}#{BloomFilter::Protocol::ADD}hello")
|
119
|
-
socket.expects(:read).with(4).returns([1].pack("N"))
|
120
|
-
socket.expects(:read).with(1).returns(BloomFilter::Protocol::TRUE)
|
121
|
-
|
122
|
-
|
123
|
-
IO.expects(:select).with([socket], nil, nil, 1).returns(socket)
|
124
119
|
client.add("hello")
|
125
120
|
end
|
121
|
+
|
122
|
+
should "raise exception if set to on conn failure" do
|
123
|
+
socket = mock()
|
124
|
+
|
125
|
+
socket.stubs(:closed?).returns(false)
|
126
|
+
TCPSocket.stubs(:new).with("localhost", 4111).returns(socket)
|
127
|
+
IO.stubs(:select).returns(socket)
|
128
|
+
socket.stubs(:write)
|
129
|
+
socket.expects(:read).returns(nil)
|
130
|
+
|
131
|
+
client = BloomFilter::Client.new("localhost", 4111, :timeout => 1, :raise_on_error => true)
|
132
|
+
assert_raises(BloomFilter::ConnectionError) do
|
133
|
+
client.add("hello")
|
134
|
+
end
|
135
|
+
end
|
126
136
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'bloom_filter/server'
|
3
|
+
require 'bloom_filter/client'
|
4
|
+
|
5
|
+
class BloomFilterIntegrationTest < Test::Unit::TestCase
|
6
|
+
context "with a server listening on port 4111" do
|
7
|
+
setup do
|
8
|
+
Thread.new do
|
9
|
+
EM.run do
|
10
|
+
EM.start_server("localhost", 4111, BloomFilter.new_server(1000, 0.05))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@client = BloomFilter::Client.new("localhost", 4111)
|
14
|
+
end
|
15
|
+
|
16
|
+
should "add an element and return true" do
|
17
|
+
assert @client.add("foo")
|
18
|
+
end
|
19
|
+
|
20
|
+
should "add an element and return true after checking for inclusion" do
|
21
|
+
@client.add("foo")
|
22
|
+
assert @client.include?("foo")
|
23
|
+
end
|
24
|
+
|
25
|
+
should "return false if we haven't added anything" do
|
26
|
+
assert !@client.include?("foo")
|
27
|
+
end
|
28
|
+
|
29
|
+
should "intersect with an array and return only the ones that have been added" do
|
30
|
+
@client.add 1
|
31
|
+
@client.add 2
|
32
|
+
@client.add 3
|
33
|
+
assert_equal [1,2,3], @client & [1,2,3,4,5]
|
34
|
+
end
|
35
|
+
|
36
|
+
teardown do
|
37
|
+
EM.schedule do
|
38
|
+
EM.stop
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bloom_filter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arya Asemanfar
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-04 00:00:00 -08:00
|
13
13
|
default_executable: bloom_filter_server
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- lib/bloom_filter/server.rb
|
68
68
|
- test/bloom_filter_bit_vector_test.rb
|
69
69
|
- test/bloom_filter_client_test.rb
|
70
|
+
- test/bloom_filter_integration_test.rb
|
70
71
|
- test/bloom_filter_server_test.rb
|
71
72
|
- test/bloom_filter_test.rb
|
72
73
|
- test/test_helper.rb
|
@@ -101,6 +102,7 @@ summary: A simple BloomFilter implementation, usable in-process or as an EventMa
|
|
101
102
|
test_files:
|
102
103
|
- test/bloom_filter_bit_vector_test.rb
|
103
104
|
- test/bloom_filter_client_test.rb
|
105
|
+
- test/bloom_filter_integration_test.rb
|
104
106
|
- test/bloom_filter_server_test.rb
|
105
107
|
- test/bloom_filter_test.rb
|
106
108
|
- test/test_helper.rb
|