redis-bloomfilter 0.0.3 → 1.0.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.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YWU5ZWE2Mjg1OTVmMGVlZWQ0ZTZmNzkwNTE3MDJiNjI1OTI0MzMzYQ==
5
- data.tar.gz: !binary |-
6
- Zjg5NDkwNDdmOWZiOThlMWYxZTkyOWI1OGE2ODcxZmU1ZjZkMTZiYQ==
2
+ SHA256:
3
+ metadata.gz: 03a3466f74223e60731f3054278118e7ce0d3745826469b3f752e0b916e664e1
4
+ data.tar.gz: 840df14ff183236d0c1dd78e954b43635c39469e82c845787b7741526edb4890
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OTQ2YmM1Zjk2NTg4NjE4N2MwMjVlZDEyMmE3ZGE2MTlmMmE2NWM3YzdmYjJh
10
- YjdjODBkM2QzN2NiNTgzNmUyMmY4OWNiNTBiNjhiYTU5OGRkNTI1NDNlOWNh
11
- ZDJhYWNmMmZjOGQ5M2QxNjYyYzdmMmI4YmEzZjM4NzU0YjIwMzA=
12
- data.tar.gz: !binary |-
13
- YzkzMjg5ZTRhZDNlNGI1YTBlMzMwMjY4NDQ0N2RhMTM4MmE2ZmZmMTY1ZjQ3
14
- NTg0MzM3NmM2MGE0N2JkYWMzMDI0NDdjZTFlMzMyMDMzNmU2ZmYzYTZkMjhl
15
- ZjMxMTllZTVmZGY4YmMxZGUzYzExZGYyNWNmZmNlZTRiZTlmZGQ=
6
+ metadata.gz: 82d34182fd1599a6c88f87b6d04cfc0d1e01fe001920b8e7e6371e7bfb824549c21697853516b8f34b90c238616ed88a4b3d1e9c5e9e109430d87b20e0400f5e
7
+ data.tar.gz: 6595a1a50381052cd7bfdc4d4c73cf2710deab362168fd08aff1d1c14abb58a0a7ccfa219a04067b7b5f4175e351180d33f0d69f3477d591fad66a2cc168facf
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.1
3
+ - 2.6
4
+ - 2.5
5
+ - 2.4
6
+ - 2.3
6
7
 
7
8
  services:
8
9
  - redis
data/Gemfile CHANGED
@@ -1,4 +1,6 @@
1
- source "http://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'http://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in redis-bloomfilter.gemspec
4
6
  gemspec
data/Rakefile CHANGED
@@ -1,9 +1,11 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec) do |spec|
5
7
  spec.pattern = 'spec/*_spec.rb'
6
8
  end
7
9
 
8
- task :default => :spec
9
- task :test => :spec
10
+ task default: :spec
11
+ task test: :spec
@@ -1,23 +1,25 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
- require "redis-bloomfilter"
3
- require "benchmark"
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'redis-bloomfilter'
5
+ require 'benchmark'
4
6
  items = 100_000
5
7
  error_rate = 0.01
6
- %w(lua ruby).each do |driver|
8
+ %w[lua ruby].each do |driver|
7
9
  bf = Redis::Bloomfilter.new(
8
10
  {
9
- :size => items,
10
- :error_rate => error_rate,
11
- :key_name => "bloom-filter-bench-flat-#{driver}",
12
- :driver => driver
11
+ size: items,
12
+ error_rate: error_rate,
13
+ key_name: "bloom-filter-bench-flat-#{driver}",
14
+ driver: driver
13
15
  }
14
16
  )
15
17
  bf.clear
16
- puts "---------------------------------------------"
18
+ puts '---------------------------------------------'
17
19
  puts "Benchmarking #{driver} driver with #{items} items"
18
20
  Benchmark.bm(7) do |x|
19
- x.report("insert: ") {items.times { |i| bf.insert(rand(items)) }}
20
- x.report("include?:") {items.times { |i| bf.include?(rand(items)) }}
21
+ x.report('insert: ') { items.times { |_i| bf.insert(rand(items)) } }
22
+ x.report('include?:') { items.times { |_i| bf.include?(rand(items)) } }
21
23
  end
22
24
  puts
23
- end
25
+ end
@@ -1,11 +1,13 @@
1
- $:.push File.expand_path("../lib", __FILE__)
2
- require "redis-bloomfilter"
3
- require "benchmark"
4
- require "set"
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'redis-bloomfilter'
5
+ require 'benchmark'
6
+ require 'set'
5
7
 
6
8
  def rand_word(length = 8)
7
9
  @charset ||= ('a'..'z').to_a
8
- @charset.shuffle.first(length).join
10
+ @charset.sample(length).join
9
11
  end
10
12
 
11
13
  items = ARGV[0].nil? ? 10_000 : ARGV[0].to_i
@@ -16,10 +18,10 @@ error_rate = 0.01
16
18
 
17
19
  bf = Redis::Bloomfilter.new(
18
20
  {
19
- :size => items,
20
- :error_rate => error_rate,
21
- :key_name => "bloom-filter-bench-#{driver}",
22
- :driver => driver
21
+ size: items,
22
+ error_rate: error_rate,
23
+ key_name: "bloom-filter-bench-#{driver}",
24
+ driver: driver
23
25
  }
24
26
  )
25
27
  bf.clear
@@ -27,7 +29,7 @@ error_rate = 0.01
27
29
  first_error_at = 0
28
30
  visited = Set.new
29
31
 
30
- Benchmark.bm(7) do |x|
32
+ Benchmark.bm(7) do |x|
31
33
  x.report do
32
34
  items.times do |i|
33
35
  item = rand_word
@@ -38,7 +40,7 @@ error_rate = 0.01
38
40
  end
39
41
  visited << item
40
42
  bf.insert item
41
- #print ".(#{"%.1f" % ((i.to_f/items.to_f) * 100)}%) " if i % 1000 == 0
43
+ # print ".(#{"%.1f" % ((i.to_f/items.to_f) * 100)}%) " if i % 1000 == 0
42
44
  end
43
45
  end
44
46
  end
@@ -50,4 +52,4 @@ error_rate = 0.01
50
52
  puts "Error found: #{error}"
51
53
  puts "Error rate: #{(error.to_f / items)}"
52
54
  puts
53
- end
55
+ end
@@ -1,4 +1,6 @@
1
- require "redis-bloomfilter"
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis-bloomfilter'
2
4
 
3
5
  # It creates a Bloom Filter using the default ruby driver
4
6
  # Number of elements expected : 10000
@@ -6,16 +8,16 @@ require "redis-bloomfilter"
6
8
  # Key name on Redis: my-bloom-filter
7
9
  # Redis: 127.0.0.1:6379 or an already existing connection
8
10
  @bf = Redis::Bloomfilter.new(
9
- :size => 10_000,
10
- :error_rate => 0.01,
11
- :key_name => 'my-bloom-filter'
11
+ size: 10_000,
12
+ error_rate: 0.01,
13
+ key_name: 'my-bloom-filter'
12
14
  )
13
15
 
14
16
  # Insert an element
15
- @bf.insert "foo"
17
+ @bf.insert 'foo'
16
18
  # Check if an element exists
17
- puts @bf.include?("foo") # => true
18
- puts @bf.include?("bar") # => false
19
+ puts @bf.include?('foo') # => true
20
+ puts @bf.include?('bar') # => false
19
21
 
20
22
  # Empty the BF and delete the key stored on redis
21
23
  @bf.clear
@@ -23,16 +25,16 @@ puts @bf.include?("bar") # => false
23
25
  # Using Lua's driver: only available on Redis >= 2.6.0
24
26
  # This driver should be prefered because is faster
25
27
  @bf = Redis::Bloomfilter.new(
26
- :size => 10_000,
27
- :error_rate => 0.01,
28
- :key_name => 'my-bloom-filter-lua',
29
- :driver => 'lua'
28
+ size: 10_000,
29
+ error_rate: 0.01,
30
+ key_name: 'my-bloom-filter-lua',
31
+ driver: 'lua'
30
32
  )
31
33
 
32
34
  # Specify a redis connection:
33
35
  # @bf = Redis::Bloomfilter.new(
34
- # :size => 10_000,
35
- # :error_rate => 0.01,
36
+ # :size => 10_000,
37
+ # :error_rate => 0.01,
36
38
  # :key_name => 'my-bloom-filter-lua',
37
39
  # :driver => 'lua',
38
40
  # :redis => Redis.new(:host => "10.0.1.1", :port => 6380)
@@ -1,7 +1,8 @@
1
- require "digest/sha1"
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
2
4
  class Redis
3
5
  module BloomfilterDriver
4
-
5
6
  # It loads lua script into redis.
6
7
  # BF implementation is done by lua scripting
7
8
  # The alghoritm is executed directly on redis
@@ -20,97 +21,94 @@ class Redis
20
21
  set data, 1
21
22
  end
22
23
 
23
- def remove(data)
24
- set data, 0
25
- end
26
-
27
24
  def include?(key)
28
- r = @redis.evalsha(@check_fnc_sha, :keys => [@options[:key_name]], :argv => [@options[:size], @options[:error_rate], key])
29
- r == 1 ? true : false
25
+ r = @redis.evalsha(@check_fnc_sha, keys: [@options[:key_name]], argv: [@options[:size], @options[:error_rate], key])
26
+ r == 1
30
27
  end
31
28
 
32
29
  def clear
33
- @redis.keys("#{@options[:key_name]}:*").each {|k|@redis.del k}
30
+ @redis.keys("#{@options[:key_name]}:*").each { |k| @redis.del k }
34
31
  end
35
32
 
36
33
  protected
37
- # It loads the script inside Redis
38
- # Taken from https://github.com/ErikDubbelboer/redis-lua-scaling-bloom-filter
39
- # This is a scalable implementation of BF. It means the initial size can vary
40
- def lua_load
41
- add_fnc = %q(
42
- local entries = ARGV[1]
43
- local precision = ARGV[2]
44
- local set_value = ARGV[4]
45
- local index = math.ceil(redis.call('INCR', KEYS[1] .. ':count') / entries)
46
- local key = KEYS[1] .. ':' .. index
47
- local bits = math.floor(-(entries * math.log(precision * math.pow(0.5, index))) / 0.480453013)
34
+
35
+ # It loads the script inside Redis
36
+ # Taken from https://github.com/ErikDubbelboer/redis-lua-scaling-bloom-filter
37
+ # This is a scalable implementation of BF. It means the initial size can vary
38
+ def lua_load
39
+ add_fnc = "
40
+ local entries = ARGV[1]
41
+ local precision = ARGV[2]
42
+ local set_value = ARGV[4]
43
+ local index = math.ceil(redis.call('INCR', KEYS[1] .. ':count') / entries)
44
+ local key = KEYS[1] .. ':' .. index
45
+ local bits = math.floor(-(entries * math.log(precision * math.pow(0.5, index))) / 0.480453013)
46
+ local k = math.floor(0.693147180 * bits / entries)
47
+ local hash = redis.sha1hex(ARGV[3])
48
+ local h = { }
49
+ h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
50
+ h[1] = tonumber(string.sub(hash, 8 , 16), 16)
51
+ h[2] = tonumber(string.sub(hash, 16, 24), 16)
52
+ h[3] = tonumber(string.sub(hash, 24, 32), 16)
53
+ for i=1, k do
54
+ redis.call('SETBIT', key, (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % bits, set_value)
55
+ end
56
+ "
57
+
58
+ check_fnc = "
59
+
60
+ local entries = ARGV[1]
61
+ local precision = ARGV[2]
62
+ local index = redis.call('GET', KEYS[1] .. ':count')
63
+ if not index then
64
+ return 0
65
+ end
66
+ index = math.ceil(redis.call('GET', KEYS[1] .. ':count') / entries)
67
+ local hash = redis.sha1hex(ARGV[3])
68
+ local h = { }
69
+ h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
70
+ h[1] = tonumber(string.sub(hash, 8 , 16), 16)
71
+ h[2] = tonumber(string.sub(hash, 16, 24), 16)
72
+ h[3] = tonumber(string.sub(hash, 24, 32), 16)
73
+ local maxk = math.floor(0.693147180 * math.floor((entries * math.log(precision * math.pow(0.5, index))) / -0.480453013) / entries)
74
+ local b = { }
75
+ for i=1, maxk do
76
+ table.insert(b, h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)])
77
+ end
78
+ for n=1, index do
79
+ local key = KEYS[1] .. ':' .. n
80
+ local found = true
81
+ local bits = math.floor((entries * math.log(precision * math.pow(0.5, n))) / -0.480453013)
48
82
  local k = math.floor(0.693147180 * bits / entries)
49
- local hash = redis.sha1hex(ARGV[3])
50
- local h = { }
51
- h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
52
- h[1] = tonumber(string.sub(hash, 8 , 16), 16)
53
- h[2] = tonumber(string.sub(hash, 16, 24), 16)
54
- h[3] = tonumber(string.sub(hash, 24, 32), 16)
83
+
55
84
  for i=1, k do
56
- redis.call('SETBIT', key, (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % bits, set_value)
85
+ if redis.call('GETBIT', key, b[i] % bits) == 0 then
86
+ found = false
87
+ break
88
+ end
57
89
  end
58
- )
59
-
60
- check_fnc = %q(
61
90
 
62
- local entries = ARGV[1]
63
- local precision = ARGV[2]
64
- local index = redis.call('GET', KEYS[1] .. ':count')
65
- if not index then
66
- return 0
67
- end
68
- index = math.ceil(redis.call('GET', KEYS[1] .. ':count') / entries)
69
- local hash = redis.sha1hex(ARGV[3])
70
- local h = { }
71
- h[0] = tonumber(string.sub(hash, 0 , 8 ), 16)
72
- h[1] = tonumber(string.sub(hash, 8 , 16), 16)
73
- h[2] = tonumber(string.sub(hash, 16, 24), 16)
74
- h[3] = tonumber(string.sub(hash, 24, 32), 16)
75
- local maxk = math.floor(0.693147180 * math.floor((entries * math.log(precision * math.pow(0.5, index))) / -0.480453013) / entries)
76
- local b = { }
77
- for i=1, maxk do
78
- table.insert(b, h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)])
91
+ if found then
92
+ return 1
79
93
  end
80
- for n=1, index do
81
- local key = KEYS[1] .. ':' .. n
82
- local found = true
83
- local bits = math.floor((entries * math.log(precision * math.pow(0.5, n))) / -0.480453013)
84
- local k = math.floor(0.693147180 * bits / entries)
85
-
86
- for i=1, k do
87
- if redis.call('GETBIT', key, b[i] % bits) == 0 then
88
- found = false
89
- break
90
- end
91
- end
94
+ end
92
95
 
93
- if found then
94
- return 1
95
- end
96
- end
96
+ return 0
97
+ "
97
98
 
98
- return 0
99
- )
100
-
101
- @add_fnc_sha = Digest::SHA1.hexdigest(add_fnc)
102
- @check_fnc_sha = Digest::SHA1.hexdigest(check_fnc)
99
+ @add_fnc_sha = Digest::SHA1.hexdigest(add_fnc)
100
+ @check_fnc_sha = Digest::SHA1.hexdigest(check_fnc)
103
101
 
104
- loaded = @redis.script(:exists, [@add_fnc_sha, @check_fnc_sha]).uniq
105
- if loaded.count != 1 || loaded.first != true
106
- @add_fnc_sha = @redis.script(:load, add_fnc)
107
- @check_fnc_sha = @redis.script(:load, check_fnc)
108
- end
102
+ loaded = @redis.script(:exists, [@add_fnc_sha, @check_fnc_sha]).uniq
103
+ if loaded.count != 1 || loaded.first != true
104
+ @add_fnc_sha = @redis.script(:load, add_fnc)
105
+ @check_fnc_sha = @redis.script(:load, check_fnc)
109
106
  end
107
+ end
110
108
 
111
- def set(data, val)
112
- @redis.evalsha(@add_fnc_sha, :keys => [@options[:key_name]], :argv => [@options[:size], @options[:error_rate], data, val])
113
- end
109
+ def set(data, val)
110
+ @redis.evalsha(@add_fnc_sha, keys: [@options[:key_name]], argv: [@options[:size], @options[:error_rate], data, val])
111
+ end
114
112
  end
115
113
  end
116
- end
114
+ end
@@ -1,8 +1,9 @@
1
- require "digest/sha1"
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
2
4
  class Redis
3
5
  module BloomfilterDriver
4
6
  class Ruby
5
-
6
7
  # Faster Ruby version.
7
8
  # This driver should be used if Redis version < 2.6
8
9
  attr_accessor :redis
@@ -11,58 +12,53 @@ class Redis
11
12
  end
12
13
 
13
14
  # Insert a new element
14
- def insert(data)
15
+ def insert(data)
15
16
  set data, 1
16
17
  end
17
18
 
18
19
  # It checks if a key is part of the set
19
20
  def include?(key)
20
-
21
21
  indexes = []
22
22
  indexes_for(key).each { |idx| indexes << idx }
23
- return false if @redis.getbit(@options[:key_name], indexes.shift) == 0
23
+ return false if @redis.getbit(@options[:key_name], indexes.shift).zero?
24
24
 
25
25
  result = @redis.pipelined do
26
- indexes.each {|idx| @redis.getbit(@options[:key_name], idx)}
26
+ indexes.each { |idx| @redis.getbit(@options[:key_name], idx) }
27
27
  end
28
28
 
29
29
  !result.include?(0)
30
30
  end
31
31
 
32
- # It removes an element from the filter
33
- def remove(data)
34
- set data, 0
35
- end
36
-
37
32
  # It deletes a bloomfilter
38
33
  def clear
39
34
  @redis.del @options[:key_name]
40
35
  end
41
36
 
42
37
  protected
43
- # Hashing strategy:
44
- # http://www.eecs.harvard.edu/~kirsch/pubs/bbbf/esa06.pdf
45
- def indexes_for data
46
- sha = Digest::SHA1.hexdigest(data.to_s)
47
- h = []
48
- h[0] = sha[0...8].to_i(16)
49
- h[1] = sha[8...16].to_i(16)
50
- h[2] = sha[16...24].to_i(16)
51
- h[3] = sha[24...32].to_i(16)
52
- idxs = []
53
38
 
54
- (@options[:hashes]).times {|i|
55
- v = (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % @options[:bits]
56
- idxs << v
57
- }
58
- idxs
39
+ # Hashing strategy:
40
+ # https://www.eecs.harvard.edu/~michaelm/postscripts/tr-02-05.pdf
41
+ def indexes_for(data)
42
+ sha = Digest::SHA1.hexdigest(data.to_s)
43
+ h = []
44
+ h[0] = sha[0...8].to_i(16)
45
+ h[1] = sha[8...16].to_i(16)
46
+ h[2] = sha[16...24].to_i(16)
47
+ h[3] = sha[24...32].to_i(16)
48
+ idxs = []
49
+
50
+ (@options[:hashes]).times do |i|
51
+ v = (h[i % 2] + i * h[2 + (((i + (i % 2)) % 4) / 2)]) % @options[:bits]
52
+ idxs << v
59
53
  end
54
+ idxs
55
+ end
60
56
 
61
- def set(key, val)
62
- @redis.pipelined do
63
- indexes_for(key).each {|i| @redis.setbit @options[:key_name], i, val}
64
- end
57
+ def set(key, val)
58
+ @redis.pipelined do
59
+ indexes_for(key).each { |i| @redis.setbit @options[:key_name], i, val }
65
60
  end
61
+ end
66
62
  end
67
63
  end
68
- end
64
+ end
@@ -1,6 +1,8 @@
1
- require "digest/md5"
2
- require "digest/sha1"
3
- require "zlib"
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/md5'
4
+ require 'digest/sha1'
5
+ require 'zlib'
4
6
  class Redis
5
7
  module BloomfilterDriver
6
8
  # It uses different hash strategy
@@ -13,24 +15,19 @@ class Redis
13
15
  end
14
16
 
15
17
  # Insert a new element
16
- def insert(data)
18
+ def insert(data)
17
19
  set data, 1
18
20
  end
19
21
 
20
- # Insert a new element
21
- def remove(data)
22
- set data, 0
23
- end
24
-
25
22
  # It checks if a key is part of the set
26
23
  def include?(key)
27
24
  indexes = []
28
25
  indexes_for(key) { |idx| indexes << idx }
29
26
 
30
- return false if @redis.getbit(@options[:key_name], indexes.shift) == 0
27
+ return false if @redis.getbit(@options[:key_name], indexes.shift).zero?
31
28
 
32
29
  result = @redis.pipelined do
33
- indexes.each {|idx| @redis.getbit(@options[:key_name], idx)}
30
+ indexes.each { |idx| @redis.getbit(@options[:key_name], idx) }
34
31
  end
35
32
 
36
33
  !result.include?(0)
@@ -42,31 +39,32 @@ class Redis
42
39
  end
43
40
 
44
41
  protected
45
- def indexes_for(key, engine = nil)
46
- engine ||= @options[:hash_engine]
47
- @options[:hashes].times do |i|
48
- yield self.send("engine_#{engine}", key.to_s, i)
49
- end
50
- end
51
42
 
52
- # A set of different hash functions
53
- def engine_crc32(data, i)
54
- Zlib.crc32("#{i}-#{data}").to_i(16) % @options[:bits]
43
+ def indexes_for(key, engine = nil)
44
+ engine ||= @options[:hash_engine]
45
+ @options[:hashes].times do |i|
46
+ yield send("engine_#{engine}", key.to_s, i)
55
47
  end
48
+ end
56
49
 
57
- def engine_md5(data, i)
58
- Digest::MD5.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
59
- end
50
+ # A set of different hash functions
51
+ def engine_crc32(data, i)
52
+ Zlib.crc32("#{i}-#{data}").to_i(16) % @options[:bits]
53
+ end
60
54
 
61
- def engine_sha1(data, i)
62
- Digest::SHA1.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
63
- end
55
+ def engine_md5(data, i)
56
+ Digest::MD5.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
57
+ end
64
58
 
65
- def set(data, val)
66
- @redis.pipelined do
67
- indexes_for(data) { |i| @redis.setbit @options[:key_name], i, val }
68
- end
59
+ def engine_sha1(data, i)
60
+ Digest::SHA1.hexdigest("#{i}-#{data}").to_i(16) % @options[:bits]
61
+ end
62
+
63
+ def set(data, val)
64
+ @redis.pipelined do
65
+ indexes_for(data) { |i| @redis.setbit @options[:key_name], i, val }
69
66
  end
67
+ end
70
68
  end
71
69
  end
72
- end
70
+ end
@@ -1,7 +1,9 @@
1
- require "redis"
2
- require "redis/connection/hiredis"
3
- require "redis/bloomfilter"
4
- require "redis/bloomfilter/version"
5
- require "bloomfilter_driver/ruby"
6
- require "bloomfilter_driver/lua"
7
- require "bloomfilter_driver/ruby_test"
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis'
4
+ require 'redis/connection/hiredis'
5
+ require 'redis/bloomfilter'
6
+ require 'redis/bloomfilter/version'
7
+ require 'bloomfilter_driver/ruby'
8
+ require 'bloomfilter_driver/lua'
9
+ require 'bloomfilter_driver/ruby_test'
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  class Bloomfilter
3
-
4
5
  attr_reader :options
5
6
  attr_reader :driver
6
7
 
@@ -8,17 +9,17 @@ class Redis
8
9
  # It creates a bloomfilter with a capacity of 1000 items and an error rate of 1%
9
10
  def initialize(options = {})
10
11
  @options = {
11
- :size => 1000,
12
- :error_rate => 0.01,
13
- :key_name => 'redis-bloomfilter',
14
- :hash_engine => 'md5',
15
- :redis => Redis.current,
16
- :driver => nil
12
+ size: 1000,
13
+ error_rate: 0.01,
14
+ key_name: 'redis-bloomfilter',
15
+ hash_engine: 'md5',
16
+ redis: Redis.current,
17
+ driver: nil
17
18
  }.merge options
18
19
 
19
- raise ArgumentError, "options[:size] && options[:error_rate] cannot be nil" if options[:error_rate].nil? || options[:size].nil?
20
+ raise ArgumentError, 'options[:size] && options[:error_rate] cannot be nil' if options[:error_rate].nil? || options[:size].nil?
20
21
 
21
- #Size provided, compute hashes and bits
22
+ # Size provided, compute hashes and bits
22
23
 
23
24
  @options[:size] = options[:size]
24
25
  @options[:error_rate] = options[:error_rate] ? options[:error_rate] : @options[:error_rate]
@@ -31,27 +32,27 @@ class Redis
31
32
  if @options[:driver].nil?
32
33
  ver = @redis.info['redis_version']
33
34
 
34
- if Gem::Version.new(ver) >= Gem::Version.new('2.6.0')
35
- @options[:driver] = 'lua'
36
- else
37
- @options[:driver] = 'ruby'
38
- end
35
+ @options[:driver] = if Gem::Version.new(ver) >= Gem::Version.new('2.6.0')
36
+ 'lua'
37
+ else
38
+ 'ruby'
39
+ end
39
40
  end
40
41
 
41
42
  driver_class = Redis::BloomfilterDriver.const_get(driver_name)
42
43
  @driver = driver_class.new @options
43
- @driver.redis = @redis
44
+ @driver.redis = @redis
44
45
  end
45
46
 
46
47
  # Methods used to calculate M and K
47
48
  # Taken from http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives
48
- def self.optimal_m num_of_elements, false_positive_rate = 0.01
49
- (-1 * (num_of_elements) * Math.log(false_positive_rate) / (Math.log(2) ** 2)).round
49
+ def self.optimal_m(num_of_elements, false_positive_rate = 0.01)
50
+ (-1 * num_of_elements * Math.log(false_positive_rate) / (Math.log(2)**2)).round
50
51
  end
51
52
 
52
- def self.optimal_k num_of_elements, bf_size
53
+ def self.optimal_k(num_of_elements, bf_size)
53
54
  h = (Math.log(2) * (bf_size / num_of_elements)).round
54
- h+=1 if h == 0
55
+ h += 1 if h.zero?
55
56
  h
56
57
  end
57
58
 
@@ -65,19 +66,15 @@ class Redis
65
66
  @driver.include?(key)
66
67
  end
67
68
 
68
- def remove(key)
69
- @driver.remove key if @driver.respond_to? :remove
70
- end
71
-
72
69
  # It deletes a bloomfilter
73
70
  def clear
74
71
  @driver.clear
75
72
  end
76
73
 
77
74
  protected
78
- def driver_name
79
- @options[:driver].downcase.split('-').collect{|t| t.gsub(/(\w+)/){|s|s.capitalize}}.join
80
- end
81
75
 
76
+ def driver_name
77
+ @options[:driver].downcase.split('-').collect { |t| t.gsub(/(\w+)/, &:capitalize) }.join
78
+ end
82
79
  end
83
- end
80
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  class Bloomfilter
3
- VERSION = "0.0.3"
5
+ VERSION = '1.0.0'
4
6
  def self.version
5
7
  "redis-bloomfilter version #{VERSION}"
6
8
  end
7
9
  end
8
- end
10
+ end
@@ -1,31 +1,32 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "redis/bloomfilter/version"
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
5
+ require 'redis/bloomfilter/version'
4
6
 
5
7
  Gem::Specification.new do |s|
6
- s.name = "redis-bloomfilter"
8
+ s.name = 'redis-bloomfilter'
7
9
  s.version = Redis::Bloomfilter::VERSION
8
- s.authors = ["Francesco Laurita"]
9
- s.email = ["francesco.laurita@gmail.com"]
10
- s.homepage = "https://github.com/taganaka/redis-bloomfilter"
11
- s.summary = %q{Distributed Bloom Filter implementation on Redis}
12
- s.description = %q{
10
+ s.authors = ['Francesco Laurita']
11
+ s.email = ['francesco.laurita@gmail.com']
12
+ s.homepage = 'https://github.com/taganaka/redis-bloomfilter'
13
+ s.summary = 'Distributed Bloom Filter implementation on Redis'
14
+ s.description = '
13
15
  Adds Redis::Bloomfilter class which can be used as ditributed bloom filter implementation on Redis.
14
16
  A Bloom filter is a space-efficient probabilistic data structure that is used to test whether an element is a member of a set.
15
- }
17
+ '
16
18
  s.licenses = ['MIT']
17
- s.rubyforge_project = "redis-bloomfilter"
19
+ s.rubyforge_project = 'redis-bloomfilter'
18
20
 
19
21
  s.files = `git ls-files`.split("\n")
20
22
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
- s.require_paths = ["lib"]
23
-
24
- s.add_runtime_dependency 'hiredis', '~> 0.5', '>= 0.5.2'
25
- s.add_runtime_dependency 'redis', '~> 3.0', '>= 3.0.4'
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
24
+ s.require_paths = ['lib']
26
25
 
27
- s.add_development_dependency "rspec"
28
- s.add_development_dependency "flexmock"
29
- s.add_development_dependency "rake"
26
+ s.add_runtime_dependency 'hiredis', '~> 0.6.1'
27
+ s.add_runtime_dependency 'redis', '~> 4.0', '>= 4.0.1'
30
28
 
29
+ s.add_development_dependency 'flexmock'
30
+ s.add_development_dependency 'rake'
31
+ s.add_development_dependency 'rspec'
31
32
  end
@@ -1,11 +1,12 @@
1
- require "spec_helper"
2
- require "set"
1
+ # frozen_string_literal: true
3
2
 
3
+ require 'spec_helper'
4
+ require 'set'
4
5
 
5
- def test_error_rate(bf,elems)
6
+ def test_error_rate(bf, elems)
6
7
  visited = Set.new
7
8
  error = 0
8
- elems.times do |i|
9
+ elems.times do |_i|
9
10
  a = rand(elems)
10
11
  error += 1 if bf.include?(a) != visited.include?(a)
11
12
  visited << a
@@ -14,86 +15,78 @@ def test_error_rate(bf,elems)
14
15
  error.to_f / elems
15
16
  end
16
17
 
17
- def factory options, driver
18
+ def factory(options, driver)
18
19
  options[:driver] = driver
19
20
  Redis::Bloomfilter.new options
20
21
  end
21
22
 
22
23
  describe Redis::Bloomfilter do
23
-
24
24
  it 'should return the right version' do
25
- Redis::Bloomfilter.version.should eq "redis-bloomfilter version #{Redis::Bloomfilter::VERSION}"
25
+ expect(Redis::Bloomfilter.version).to eq "redis-bloomfilter version #{Redis::Bloomfilter::VERSION}"
26
26
  end
27
27
 
28
28
  it 'should check for the initialize options' do
29
29
  expect { Redis::Bloomfilter.new }.to raise_error(ArgumentError)
30
- expect { Redis::Bloomfilter.new :size => 123 }.to raise_error(ArgumentError)
31
- expect { Redis::Bloomfilter.new :error_rate => 0.01 }.to raise_error(ArgumentError)
32
- expect { Redis::Bloomfilter.new :size => 123,:error_rate => 0.01, :driver => 'bibu' }.to raise_error(NameError)
30
+ expect { Redis::Bloomfilter.new size: 123 }.to raise_error(ArgumentError)
31
+ expect { Redis::Bloomfilter.new error_rate: 0.01 }.to raise_error(ArgumentError)
32
+ expect { Redis::Bloomfilter.new size: 123, error_rate: 0.01, driver: 'bibu' }.to raise_error(NameError)
33
33
  end
34
34
 
35
35
  it 'should choose the right driver based on the Redis version' do
36
-
37
- redis_mock = flexmock("redis")
38
- redis_mock.should_receive(:info).and_return({'redis_version' => '2.6.0'})
36
+ redis_mock = flexmock('redis')
37
+ redis_mock.should_receive(:info).and_return({ 'redis_version' => '2.6.0' })
39
38
  redis_mock.should_receive(:script).and_return([true, true])
40
- redis_mock_2_5 = flexmock("redis_2_5")
41
- redis_mock_2_5.should_receive(:info).and_return({'redis_version' => '2.5.0'})
39
+ redis_mock_2_5 = flexmock('redis_2_5')
40
+ redis_mock_2_5.should_receive(:info).and_return({ 'redis_version' => '2.5.0' })
42
41
 
43
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => 'ossom', :redis => redis_mock}, nil)
44
- bf.driver.should be_kind_of(Redis::BloomfilterDriver::Lua)
42
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: 'ossom', redis: redis_mock }, nil)
43
+ expect(bf.driver).to be_kind_of(Redis::BloomfilterDriver::Lua)
45
44
 
46
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => 'ossom', :redis => redis_mock_2_5}, nil)
47
- bf.driver.should be_kind_of(Redis::BloomfilterDriver::Ruby)
45
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: 'ossom', redis: redis_mock_2_5 }, nil)
46
+ expect(bf.driver).to be_kind_of(Redis::BloomfilterDriver::Ruby)
48
47
  end
49
48
 
50
49
  it 'should create a Redis::Bloomfilter object' do
51
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => 'ossom'}, 'ruby')
52
- bf.should be
53
- bf.options[:size].should eq 1000
54
- bf.options[:bits].should eq 9585
55
- bf.options[:hashes].should eq 6
56
- bf.options[:key_name].should eq 'ossom'
50
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: 'ossom' }, 'ruby')
51
+ expect(bf).to be
52
+ expect(bf.options[:size]).to eq 1000
53
+ expect(bf.options[:bits]).to eq 9585
54
+ expect(bf.options[:hashes]).to eq 6
55
+ expect(bf.options[:key_name]).to eq 'ossom'
57
56
  bf.clear
58
57
  end
59
58
 
60
- %w(ruby lua ruby-test).each do |driver|
59
+ %w[ruby lua ruby-test].each do |driver|
61
60
  it 'should work' do
62
- bf = factory({:size => 1000, :error_rate => 0.01, :key_name => '__test_bf'},driver)
61
+ bf = factory({ size: 1000, error_rate: 0.01, key_name: '__test_bf' }, driver)
63
62
  bf.clear
64
- bf.include?("asdlol").should be false
65
- bf.insert "asdlol"
66
- bf.include?("asdlol").should be true
63
+ expect(bf.include?('asdlol')).to be false
64
+ bf.insert 'asdlol'
65
+ expect(bf.include?('asdlol')).to be true
67
66
  bf.clear
68
- bf.include?("asdlol").should be false
67
+ expect(bf.include?('asdlol')).to be false
69
68
  end
70
69
 
71
70
  it 'should honor the error rate' do
72
- bf = factory({:size => 100, :error_rate => 0.01, :key_name => '__test_bf'},driver)
71
+ bf = factory({ size: 100, error_rate: 0.02, key_name: '__test_bf' }, driver)
73
72
  bf.clear
74
73
  e = test_error_rate bf, 180
75
- e.should be <= bf.options[:error_rate]
74
+ expect(e.round(2)).to be <= bf.options[:error_rate].round(2)
76
75
  bf.clear
77
76
  end
78
77
 
79
- it 'should remove an elemnt from the filter' do
80
-
81
- bf = factory({:size => 100, :error_rate => 0.01, :key_name => '__test_bf'},driver)
82
- bf.insert "asdlolol"
83
- bf.include?("asdlolol").should be true
84
- bf.remove "asdlolol"
85
- bf.include?("asdlolol").should be false
86
-
78
+ it 'should add an element to the filter' do
79
+ bf = factory({ size: 100, error_rate: 0.01, key_name: '__test_bf' }, driver)
80
+ bf.insert 'asdlolol'
81
+ expect(bf.include?('asdlolol')).to be true
87
82
  end
88
83
  end
89
84
 
90
85
  it 'should be a scalable bloom filter' do
91
- bf = factory({:size => 10, :error_rate => 0.01, :key_name => '__test_bf'},'lua')
86
+ bf = factory({ size: 100, error_rate: 0.02, key_name: '__test_bf' }, 'lua')
92
87
  bf.clear
93
- e = test_error_rate(bf, 100)
94
- e.should be <= bf.options[:error_rate]
88
+ e = test_error_rate(bf, 150)
89
+ expect(e).to be <= bf.options[:error_rate]
95
90
  bf.clear
96
-
97
91
  end
98
-
99
- end
92
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by the `rspec --init` command. Conventionally, all
2
4
  # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
5
  # Require this file using `require "spec_helper"` to ensure that it is only
@@ -5,7 +7,6 @@
5
7
  #
6
8
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
9
  RSpec.configure do |config|
8
- config.treat_symbols_as_metadata_keys_with_true_values = true
9
10
  config.run_all_when_everything_filtered = true
10
11
  config.filter_run :focus
11
12
  config.mock_with :flexmock
@@ -17,6 +18,5 @@ RSpec.configure do |config|
17
18
  config.order = 'random'
18
19
  end
19
20
 
20
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
21
- require "redis-bloomfilter"
22
-
21
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
22
+ require 'redis-bloomfilter'
metadata CHANGED
@@ -1,98 +1,92 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-bloomfilter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Laurita
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-24 00:00:00.000000000 Z
11
+ date: 2018-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hiredis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.5'
20
- - - ! '>='
21
- - !ruby/object:Gem::Version
22
- version: 0.5.2
19
+ version: 0.6.1
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '0.5'
30
- - - ! '>='
24
+ - - "~>"
31
25
  - !ruby/object:Gem::Version
32
- version: 0.5.2
26
+ version: 0.6.1
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: redis
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - ~>
31
+ - - "~>"
38
32
  - !ruby/object:Gem::Version
39
- version: '3.0'
40
- - - ! '>='
33
+ version: '4.0'
34
+ - - ">="
41
35
  - !ruby/object:Gem::Version
42
- version: 3.0.4
36
+ version: 4.0.1
43
37
  type: :runtime
44
38
  prerelease: false
45
39
  version_requirements: !ruby/object:Gem::Requirement
46
40
  requirements:
47
- - - ~>
41
+ - - "~>"
48
42
  - !ruby/object:Gem::Version
49
- version: '3.0'
50
- - - ! '>='
43
+ version: '4.0'
44
+ - - ">="
51
45
  - !ruby/object:Gem::Version
52
- version: 3.0.4
46
+ version: 4.0.1
53
47
  - !ruby/object:Gem::Dependency
54
- name: rspec
48
+ name: flexmock
55
49
  requirement: !ruby/object:Gem::Requirement
56
50
  requirements:
57
- - - ! '>='
51
+ - - ">="
58
52
  - !ruby/object:Gem::Version
59
53
  version: '0'
60
54
  type: :development
61
55
  prerelease: false
62
56
  version_requirements: !ruby/object:Gem::Requirement
63
57
  requirements:
64
- - - ! '>='
58
+ - - ">="
65
59
  - !ruby/object:Gem::Version
66
60
  version: '0'
67
61
  - !ruby/object:Gem::Dependency
68
- name: flexmock
62
+ name: rake
69
63
  requirement: !ruby/object:Gem::Requirement
70
64
  requirements:
71
- - - ! '>='
65
+ - - ">="
72
66
  - !ruby/object:Gem::Version
73
67
  version: '0'
74
68
  type: :development
75
69
  prerelease: false
76
70
  version_requirements: !ruby/object:Gem::Requirement
77
71
  requirements:
78
- - - ! '>='
72
+ - - ">="
79
73
  - !ruby/object:Gem::Version
80
74
  version: '0'
81
75
  - !ruby/object:Gem::Dependency
82
- name: rake
76
+ name: rspec
83
77
  requirement: !ruby/object:Gem::Requirement
84
78
  requirements:
85
- - - ! '>='
79
+ - - ">="
86
80
  - !ruby/object:Gem::Version
87
81
  version: '0'
88
82
  type: :development
89
83
  prerelease: false
90
84
  version_requirements: !ruby/object:Gem::Requirement
91
85
  requirements:
92
- - - ! '>='
86
+ - - ">="
93
87
  - !ruby/object:Gem::Version
94
88
  version: '0'
95
- description: ! "\n Adds Redis::Bloomfilter class which can be used as ditributed
89
+ description: "\n Adds Redis::Bloomfilter class which can be used as ditributed
96
90
  bloom filter implementation on Redis.\n A Bloom filter is a space-efficient probabilistic
97
91
  data structure that is used to test whether an element is a member of a set.\n "
98
92
  email:
@@ -101,9 +95,9 @@ executables: []
101
95
  extensions: []
102
96
  extra_rdoc_files: []
103
97
  files:
104
- - .gitignore
105
- - .rspec
106
- - .travis.yml
98
+ - ".gitignore"
99
+ - ".rspec"
100
+ - ".travis.yml"
107
101
  - Gemfile
108
102
  - LICENSE.txt
109
103
  - README.md
@@ -130,17 +124,17 @@ require_paths:
130
124
  - lib
131
125
  required_ruby_version: !ruby/object:Gem::Requirement
132
126
  requirements:
133
- - - ! '>='
127
+ - - ">="
134
128
  - !ruby/object:Gem::Version
135
129
  version: '0'
136
130
  required_rubygems_version: !ruby/object:Gem::Requirement
137
131
  requirements:
138
- - - ! '>='
132
+ - - ">="
139
133
  - !ruby/object:Gem::Version
140
134
  version: '0'
141
135
  requirements: []
142
136
  rubyforge_project: redis-bloomfilter
143
- rubygems_version: 2.2.2
137
+ rubygems_version: 2.7.2
144
138
  signing_key:
145
139
  specification_version: 4
146
140
  summary: Distributed Bloom Filter implementation on Redis