bloomfilter-rb-without-native 2.1.3
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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +0 -0
- data/Gemfile +3 -0
- data/README.md +65 -0
- data/Rakefile +9 -0
- data/benchmark/redis-bm.rb +34 -0
- data/bloomfilter-rb-without-native.gemspec +22 -0
- data/examples/counting-redis.rb +12 -0
- data/examples/pure-ruby-bf.rb +52 -0
- data/examples/simple-native.rb +25 -0
- data/examples/simple-redis.rb +31 -0
- data/lib/bloomfilter-rb-without-native.rb +7 -0
- data/lib/bloomfilter-rb.rb +7 -0
- data/lib/bloomfilter.rb +1 -0
- data/lib/bloomfilter/counting_redis.rb +64 -0
- data/lib/bloomfilter/filter.rb +13 -0
- data/lib/bloomfilter/redis.rb +76 -0
- data/lib/bloomfilter/version.rb +3 -0
- data/spec/counting_redis_spec.rb +61 -0
- data/spec/helper.rb +2 -0
- data/spec/redis_spec.rb +62 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea07d8c1212349fce6e2a2c7871ebac290026c76
|
4
|
+
data.tar.gz: c4b9d7ae7b93d0ea8f1d1ee6de5046869f69505b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c40a4cda2246babd8e84a3727117833282fac07c24f2339a6dff3da71b77f0d73962ec6a5fc785909fa4a692a6249a310147f530f7c82188cd66eba4222e8335
|
7
|
+
data.tar.gz: 97172d03be304c79160a1754320151130606b4dd848973ebd6a9d91d563d7fa72781e309696815211466ec5dffe1c09fec05ad11c1f3b0d1e33b2b0e20c850f1
|
data/.gitignore
ADDED
data/.rspec
ADDED
File without changes
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
THIS IS A FORK OF https://github.com/igrigorik/bloomfilter-rb
|
2
|
+
|
3
|
+
# BloomFilter(s) in Ruby
|
4
|
+
|
5
|
+
- Redis-backed getbit/setbit non-counting bloom filter
|
6
|
+
- Redis-backed set-based counting (+TTL) bloom filter
|
7
|
+
|
8
|
+
Bloom filter is a space-efficient probabilistic data structure that is used to test whether an element is a member of a set. False positives are possible, but false negatives are not. For more detail, check the [wikipedia article](http://en.wikipedia.org/wiki/Bloom_filter). Instead of using k different hash functions, this implementation seeds the CRC32 hash with k different initial values (0, 1, ..., k-1). This may or may not give you a good distribution, it all depends on the data.
|
9
|
+
|
10
|
+
Performance of the Bloom filter depends on a number of variables:
|
11
|
+
|
12
|
+
- size of the bit array
|
13
|
+
- size of the counter bucket
|
14
|
+
- number of hash functions
|
15
|
+
|
16
|
+
## Resources
|
17
|
+
|
18
|
+
- Determining parameters: [Scalable Datasets: Bloom Filters in Ruby](http://www.igvita.com/2008/12/27/scalable-datasets-bloom-filters-in-ruby/)
|
19
|
+
- Applications & reasons behind bloom filter: [Flow analysis: Time based bloom filter](http://www.igvita.com/2010/01/06/flow-analysis-time-based-bloom-filters/)
|
20
|
+
|
21
|
+
***
|
22
|
+
|
23
|
+
## Redis-backed setbit/getbit bloom filter
|
24
|
+
|
25
|
+
Uses [getbit](http://redis.io/commands/getbit)/[setbit](http://redis.io/commands/setbit) on Redis strings - efficient, fast, can be shared by multiple/concurrent processes.
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
bf = BloomFilter::Redis.new
|
29
|
+
|
30
|
+
bf.insert('test')
|
31
|
+
bf.include?('test') # => true
|
32
|
+
bf.include?('blah') # => false
|
33
|
+
|
34
|
+
bf.delete('test')
|
35
|
+
bf.include?('test') # => false
|
36
|
+
```
|
37
|
+
|
38
|
+
### Memory footprint
|
39
|
+
|
40
|
+
- 1.0% error rate for 1M items, 10 bits/item: *2.5 mb*
|
41
|
+
- 1.0% error rate for 150M items, 10 bits per item: *358.52 mb*
|
42
|
+
- 0.1% error rate for 150M items, 15 bits per item: *537.33 mb*
|
43
|
+
|
44
|
+
***
|
45
|
+
|
46
|
+
## Redis-backed counting bloom filter with TTL's
|
47
|
+
Uses regular Redis get/set counters to implement a counting filter with optional TTL expiry. Because each "bit" requires its own key in Redis, you do incur a much larger memory overhead.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
bf = BloomFilter::CountingRedis.new(:ttl => 2)
|
51
|
+
|
52
|
+
bf.insert('test')
|
53
|
+
bf.include?('test') # => true
|
54
|
+
|
55
|
+
sleep(2)
|
56
|
+
bf.include?('test') # => false
|
57
|
+
```
|
58
|
+
|
59
|
+
## Credits
|
60
|
+
|
61
|
+
Tatsuya Mori <valdzone@gmail.com> (Original C implementation: http://vald.x0.com/sb/)
|
62
|
+
|
63
|
+
## License
|
64
|
+
|
65
|
+
MIT License - Copyright (c) 2011 Ilya Grigorik
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
$:<< 'lib'
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require 'bloomfilter-rb'
|
5
|
+
|
6
|
+
n = 10000
|
7
|
+
|
8
|
+
Benchmark.bm do |x|
|
9
|
+
r = BloomFilter::Redis.new
|
10
|
+
|
11
|
+
x.report("insert") do
|
12
|
+
n.times do
|
13
|
+
r.insert("a")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
x.report("lookup present") do
|
18
|
+
n.times do
|
19
|
+
r.include?("a")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
x.report("lookup missing") do
|
24
|
+
n.times do
|
25
|
+
r.include?("b")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# user system total real
|
32
|
+
# insert 1.000000 0.380000 1.380000 ( 1.942181)
|
33
|
+
# lookup present 1.030000 0.470000 1.500000 ( 2.577577)
|
34
|
+
# lookup missing 0.370000 0.160000 0.530000 ( 1.060429)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "bloomfilter/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "bloomfilter-rb-without-native"
|
7
|
+
s.version = BloomFilter::VERSION
|
8
|
+
s.authors = ["Garrett Thornburg"]
|
9
|
+
s.email = ["film42@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/film42/bloomfilter-rb-without-native/"
|
11
|
+
s.summary = "Counting Bloom Filter implemented in Ruby (fork without native code)"
|
12
|
+
s.description = s.summary
|
13
|
+
|
14
|
+
s.add_dependency "redis"
|
15
|
+
s.add_development_dependency "rspec", ">= 3"
|
16
|
+
s.add_development_dependency "rake"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#
|
2
|
+
# Pure ruby implementation of a Bloom filter, just for kicks
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'bitset'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
class BloomFilter
|
9
|
+
|
10
|
+
def initialize(max_entries, num_hashes, seed)
|
11
|
+
@num_hashes = num_hashes
|
12
|
+
@size = max_entries.to_i
|
13
|
+
@bitmap = Bitset.new(@size)
|
14
|
+
@__mask = Bitset.new(@size)
|
15
|
+
@seed = seed
|
16
|
+
end
|
17
|
+
|
18
|
+
def insert(key)
|
19
|
+
mask = make_mask(key)
|
20
|
+
@bitmap |= mask
|
21
|
+
end
|
22
|
+
|
23
|
+
def new?(key)
|
24
|
+
mask = make_mask(key)
|
25
|
+
return ((@bitmap & mask) != mask);
|
26
|
+
end
|
27
|
+
|
28
|
+
def make_mask(key)
|
29
|
+
@__mask.clear
|
30
|
+
0.upto(@num_hashes.to_i - 1) do |i|
|
31
|
+
hash = Zlib.crc32(key, i + @seed)
|
32
|
+
@__mask.set(hash % @size, 1)
|
33
|
+
end
|
34
|
+
return @__mask
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def main
|
39
|
+
bf = BloomFilter.new(1000000, 4, 0)
|
40
|
+
num = 0
|
41
|
+
while line = ARGF.gets
|
42
|
+
data = line.chop
|
43
|
+
|
44
|
+
if bf.new?(data)
|
45
|
+
num += 1
|
46
|
+
bf.insert(data)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
print "#element = #{num}\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
main
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bloomfilter-rb'
|
3
|
+
|
4
|
+
WORDS = %w(duck penguin bear panda)
|
5
|
+
TEST = %w(penguin moose racooon)
|
6
|
+
|
7
|
+
bf = BloomFilter::Native.new(:size => 100, :hashes => 2, :seed => 1, :bucket => 3, :raise => false)
|
8
|
+
|
9
|
+
WORDS.each { |w| bf.insert(w) }
|
10
|
+
TEST.each do |w|
|
11
|
+
puts "#{w}: #{bf.include?(w)}"
|
12
|
+
end
|
13
|
+
|
14
|
+
bf.stats
|
15
|
+
|
16
|
+
# penguin: true
|
17
|
+
# moose: false
|
18
|
+
# racooon: false
|
19
|
+
#
|
20
|
+
# Number of filter buckets (m): 100
|
21
|
+
# Number of bits per buckets (b): 1
|
22
|
+
# Number of filter elements (n): 4
|
23
|
+
# Number of filter hashes (k) : 4
|
24
|
+
# Raise on overflow? (r) : false
|
25
|
+
# Predicted false positive rate = 0.05%
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'lib/bloomfilter-rb'
|
3
|
+
|
4
|
+
items = 1_000_00
|
5
|
+
bits = 1
|
6
|
+
|
7
|
+
# p BloomFilter::Redis.new(:size => items*bits, :hashes => 7) # 2.5 mb
|
8
|
+
# p BloomFilter::Redis.new(:size => items*bits*5, :hashes => 7) # 13 mb
|
9
|
+
# p BloomFilter::Redis.new(:size => items*bits*30, :hashes => 7) # 73 mb
|
10
|
+
|
11
|
+
# 1% error rate for 5M items/day, 10 bits per item, for 30 days of data: 358.52 mb
|
12
|
+
# 0.1% error rate for 5M items/day, 15 bits per item, for 30 days of data: 537.33 mb
|
13
|
+
|
14
|
+
bf = BloomFilter::Redis.new(:size => items*bits, :hashes => 7) # 2.5 mb
|
15
|
+
|
16
|
+
seen = Set.new
|
17
|
+
err = 0
|
18
|
+
num = 100000
|
19
|
+
|
20
|
+
num.times do
|
21
|
+
item = rand(items)
|
22
|
+
|
23
|
+
if bf.include?(item) != seen.include?(item)
|
24
|
+
err += 1
|
25
|
+
end
|
26
|
+
|
27
|
+
seen << item
|
28
|
+
bf.insert(item)
|
29
|
+
end
|
30
|
+
|
31
|
+
p [:error_rate, (err.to_f / num) * 100]
|
data/lib/bloomfilter.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bloomfilter-rb-without-native'
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module BloomFilter
|
2
|
+
class CountingRedis < Filter
|
3
|
+
|
4
|
+
def initialize(opts = {})
|
5
|
+
@opts = {
|
6
|
+
:identifier => 'rbloom',
|
7
|
+
:size => 100,
|
8
|
+
:hashes => 4,
|
9
|
+
:seed => Time.now.to_i,
|
10
|
+
:bucket => 3,
|
11
|
+
:ttl => false,
|
12
|
+
:server => {}
|
13
|
+
}.merge opts
|
14
|
+
@db = @opts.delete(:db) || ::Redis.new(@opts[:server])
|
15
|
+
end
|
16
|
+
|
17
|
+
def insert(key, ttl=nil)
|
18
|
+
ttl = @opts[:ttl] if ttl.nil?
|
19
|
+
|
20
|
+
indexes_for(key).each do |idx|
|
21
|
+
@db.incr idx
|
22
|
+
@db.expire(idx, ttl) if ttl
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias :[]= :insert
|
26
|
+
|
27
|
+
def delete(key)
|
28
|
+
indexes_for(key).each do |idx|
|
29
|
+
count = @db.decr(idx).to_i
|
30
|
+
if count <= 0
|
31
|
+
@db.del(idx)
|
32
|
+
@db.setbit(idx, 0) if count < 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def include?(*keys)
|
38
|
+
indexes = keys.collect { |key| indexes_for(key) }
|
39
|
+
not @db.mget(*indexes.flatten).include? nil
|
40
|
+
end
|
41
|
+
alias :key? :include?
|
42
|
+
|
43
|
+
def num_set
|
44
|
+
@db.eval("return #redis.call('keys', '#{@opts[:identifier]}:*')")
|
45
|
+
end
|
46
|
+
alias :size :num_set
|
47
|
+
|
48
|
+
def clear
|
49
|
+
@db.flushdb
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# compute index offsets for provided key
|
55
|
+
def indexes_for(key)
|
56
|
+
indexes = []
|
57
|
+
@opts[:hashes].times do |i|
|
58
|
+
indexes.push @opts[:identifier] + ":" + (Zlib.crc32("#{key}:#{i+@opts[:seed]}") % @opts[:size]).to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
indexes
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module BloomFilter
|
2
|
+
class Filter
|
3
|
+
def stats
|
4
|
+
fp = ((1.0 - Math.exp(-(@opts[:hashes] * size).to_f / @opts[:size])) ** @opts[:hashes]) * 100
|
5
|
+
printf "Number of filter buckets (m): %d\n" % @opts[:size]
|
6
|
+
printf "Number of bits per buckets (b): %d\n" % @opts[:bucket]
|
7
|
+
printf "Number of filter elements (n): %d\n" % size
|
8
|
+
printf "Number of filter hashes (k) : %d\n" % @opts[:hashes]
|
9
|
+
printf "Raise on overflow? (r) : %s\n" % @opts[:raise].to_s
|
10
|
+
printf "Predicted false positive rate = %.2f%\n" % fp
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module BloomFilter
|
2
|
+
class Redis < Filter
|
3
|
+
|
4
|
+
def initialize(opts = {})
|
5
|
+
@opts = {
|
6
|
+
:size => 100,
|
7
|
+
:hashes => 4,
|
8
|
+
:seed => Time.now.to_i,
|
9
|
+
:namespace => 'redis',
|
10
|
+
:eager => false,
|
11
|
+
:server => {}
|
12
|
+
}.merge opts
|
13
|
+
@db = @opts.delete(:db) || ::Redis.new(@opts[:server])
|
14
|
+
|
15
|
+
if @opts[:eager]
|
16
|
+
@db.setbit @opts[:namespace], @opts[:size]+1, 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def insert(key, ttl=nil)
|
21
|
+
@db.pipelined do
|
22
|
+
indexes_for(key) { |idx| @db.setbit @opts[:namespace], idx, 1 }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias :[]= :insert
|
26
|
+
|
27
|
+
def include?(*keys)
|
28
|
+
keys.each do |key|
|
29
|
+
indexes = []
|
30
|
+
indexes_for(key) { |idx| indexes << idx }
|
31
|
+
|
32
|
+
return false if @db.getbit(@opts[:namespace], indexes.shift) == 0
|
33
|
+
|
34
|
+
result = @db.pipelined do
|
35
|
+
indexes.each do |idx|
|
36
|
+
@db.getbit(@opts[:namespace], idx)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
return false if result.include?(0)
|
41
|
+
end
|
42
|
+
|
43
|
+
true
|
44
|
+
end
|
45
|
+
alias :key? :include?
|
46
|
+
|
47
|
+
def delete(key)
|
48
|
+
warn "Deletes are disabled on non-counting filter, see: https://github.com/igrigorik/bloomfilter-rb/issues/37. This method will be deprecated in a future release."
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear
|
52
|
+
@db.set @opts[:namespace], 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def num_set
|
56
|
+
@db.strlen @opts[:namespace]
|
57
|
+
end
|
58
|
+
alias :size :num_set
|
59
|
+
|
60
|
+
def stats
|
61
|
+
printf "Number of filter buckets (m): %d\n" % @opts[:size]
|
62
|
+
printf "Number of filter hashes (k) : %d\n" % @opts[:hashes]
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# compute index offsets for provided key
|
68
|
+
def indexes_for(key)
|
69
|
+
indexes = []
|
70
|
+
@opts[:hashes].times do |i|
|
71
|
+
yield Zlib.crc32("#{key}:#{i+@opts[:seed]}") % @opts[:size]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe BloomFilter::CountingRedis do
|
4
|
+
|
5
|
+
it "should connect to remote redis server" do
|
6
|
+
expect { BloomFilter::CountingRedis.new }.not_to raise_error
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should allow redis client instance to be passed in" do
|
10
|
+
redis_client = double("Redis")
|
11
|
+
bf = BloomFilter::CountingRedis.new(:db => redis_client)
|
12
|
+
expect(bf.instance_variable_get(:@db)).to be(redis_client)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "a default CountingRedis instance" do
|
16
|
+
before do
|
17
|
+
# clear all redis databases
|
18
|
+
subject.instance_variable_get(:@db).flushall
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should store data in Redis" do
|
22
|
+
subject.insert(:abcd)
|
23
|
+
subject.insert('test')
|
24
|
+
|
25
|
+
expect(subject.include?('test')).to be true
|
26
|
+
expect(subject.key?('test')).to be true
|
27
|
+
|
28
|
+
expect(subject.include?('test', 'test2')).to be false
|
29
|
+
expect(subject.include?('test', 'abcd')).to be true
|
30
|
+
expect(subject.include?('test', 'abcd', 'nada')).to be false
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should delete keys from Redis" do
|
34
|
+
subject.insert('test')
|
35
|
+
expect(subject.include?('test')).to be true
|
36
|
+
|
37
|
+
subject.delete('test')
|
38
|
+
expect(subject.include?('test')).to be false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should output current stats" do
|
42
|
+
subject.insert('test')
|
43
|
+
expect(subject.size).to eq(4)
|
44
|
+
expect { subject.stats }.not_to raise_error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "a TTL 1 instance" do
|
49
|
+
subject { BloomFilter::CountingRedis.new(:ttl => 1) }
|
50
|
+
|
51
|
+
it "should accept a TTL value for a key" do
|
52
|
+
subject.instance_variable_get(:@db).flushall
|
53
|
+
|
54
|
+
subject.insert('test')
|
55
|
+
expect(subject.include?('test')).to be true
|
56
|
+
|
57
|
+
sleep(2)
|
58
|
+
expect(subject.include?('test')).to be false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/spec/helper.rb
ADDED
data/spec/redis_spec.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe BloomFilter::Redis do
|
4
|
+
|
5
|
+
context "use Redis bitstring for storage" do
|
6
|
+
before do
|
7
|
+
# clear all redis databases
|
8
|
+
subject.instance_variable_get(:@db).flushall
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should store data in Redis" do
|
12
|
+
subject.insert(:abcd)
|
13
|
+
subject.insert('test')
|
14
|
+
expect(subject.include?('test')).to be true
|
15
|
+
expect(subject.key?('test')).to be true
|
16
|
+
|
17
|
+
expect(subject.include?('test', 'test2')).to be false
|
18
|
+
expect(subject.include?('test', 'abcd')).to be true
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should not delete keys from Redis" do
|
22
|
+
subject.insert('test')
|
23
|
+
expect(subject.include?('test')).to be true
|
24
|
+
|
25
|
+
subject.delete('test')
|
26
|
+
expect(subject.include?('test')).to be true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should clear Redis filter" do
|
30
|
+
subject.insert('test')
|
31
|
+
expect(subject.include?('test')).to be true
|
32
|
+
|
33
|
+
subject.clear
|
34
|
+
expect(subject.include?('test')).to be false
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should output current stats" do
|
38
|
+
subject.clear
|
39
|
+
subject.insert('test')
|
40
|
+
expect { subject.stats }.not_to raise_error
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should connect to remote redis server" do
|
44
|
+
expect { BloomFilter::Redis.new }.not_to raise_error
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should allow redis client instance to be passed in" do
|
48
|
+
redis_client = double("Redis")
|
49
|
+
bf = BloomFilter::Redis.new(:db => redis_client)
|
50
|
+
expect(bf.instance_variable_get(:@db)).to be redis_client
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should allow namespaced BloomFilters" do
|
54
|
+
bf1 = BloomFilter::Redis.new(:namespace => :a)
|
55
|
+
bf2 = BloomFilter::Redis.new(:namespace => :b)
|
56
|
+
|
57
|
+
bf1.insert('test')
|
58
|
+
expect(bf1.include?('test')).to be true
|
59
|
+
expect(bf2.include?('test')).to be false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bloomfilter-rb-without-native
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Garrett Thornburg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - '>='
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
name: redis
|
20
|
+
prerelease: false
|
21
|
+
type: :runtime
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3'
|
33
|
+
name: rspec
|
34
|
+
prerelease: false
|
35
|
+
type: :development
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
name: rake
|
48
|
+
prerelease: false
|
49
|
+
type: :development
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Counting Bloom Filter implemented in Ruby (fork without native code)
|
56
|
+
email:
|
57
|
+
- film42@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- .rspec
|
64
|
+
- Gemfile
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- benchmark/redis-bm.rb
|
68
|
+
- bloomfilter-rb-without-native.gemspec
|
69
|
+
- examples/counting-redis.rb
|
70
|
+
- examples/pure-ruby-bf.rb
|
71
|
+
- examples/simple-native.rb
|
72
|
+
- examples/simple-redis.rb
|
73
|
+
- lib/bloomfilter-rb-without-native.rb
|
74
|
+
- lib/bloomfilter-rb.rb
|
75
|
+
- lib/bloomfilter.rb
|
76
|
+
- lib/bloomfilter/counting_redis.rb
|
77
|
+
- lib/bloomfilter/filter.rb
|
78
|
+
- lib/bloomfilter/redis.rb
|
79
|
+
- lib/bloomfilter/version.rb
|
80
|
+
- spec/counting_redis_spec.rb
|
81
|
+
- spec/helper.rb
|
82
|
+
- spec/redis_spec.rb
|
83
|
+
homepage: https://github.com/film42/bloomfilter-rb-without-native/
|
84
|
+
licenses: []
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 2.2.2
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: Counting Bloom Filter implemented in Ruby (fork without native code)
|
106
|
+
test_files:
|
107
|
+
- spec/counting_redis_spec.rb
|
108
|
+
- spec/helper.rb
|
109
|
+
- spec/redis_spec.rb
|