commendo 1.0.0 → 1.1.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,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f4d4570da8f16342e587461c8731982f4a82c8ba
4
- data.tar.gz: bdde9ae21c9bb35c7c9b6073a82e651086132617
3
+ metadata.gz: 9504182a72226de650736e2b987eda2734a5f3bf
4
+ data.tar.gz: ff9fb6d451b160f8f2ac4da2d4cb2688621ad7ff
5
5
  SHA512:
6
- metadata.gz: 31406c7ee3846a6046e8e149ca8ada66eaed799977ee615e778a5fb3a2c353165e246deb87b422bef7d5a544a4dddbac3788a4ed879a77a04b13b0f3ed9d7dbc
7
- data.tar.gz: 15e58e641bd237466b80713c64d893ebd66877aa4c4a28241b584875d5e476fc28c45d68194eacd9b74cfef5a4e144c011e892185bd1ee9d27f38fc346c11032
6
+ metadata.gz: 7669f08e1c313865623360a14f72d7686da669c36c7a096485f9da9d9552f904b7cf808bbdaf2379af32d681939a6ab5d587759cbead1fb8037d6869ef91e048
7
+ data.tar.gz: e8a729ece664f9167e0f2efd8212f5d1ac87ab5edd5dc443ff561ae047d81a97ab6d1d5dafccf7b2c8aac209bc38bc34cdd456e01816bba050ad03cd32a8c8b8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # 1.1.0 / 2015-04-01
2
+ * [FEATURE] Add ability to remove a resource from a group and recalculate similarity
3
+
1
4
  # 1.0.0 / 2014-04-22
2
5
  * [FEATURE] Add limits to similarity requests and bump to production release 1.0.0 :)
3
6
 
@@ -38,6 +38,10 @@ module Commendo
38
38
  calculate_similarity_for_resource(resource, 0)
39
39
  end
40
40
 
41
+ def groups(resource)
42
+ redis.zrange(resource_key(resource), 0, -1)
43
+ end
44
+
41
45
  def delete(resource)
42
46
  similar = similar_to(resource)
43
47
  similar.each do |other_resource|
@@ -54,18 +58,17 @@ module Commendo
54
58
  #TODO make this use scan for scaling
55
59
  keys = redis.keys("#{resource_key_base}:*")
56
60
  keys.each_with_index do |key, i|
61
+ resource = key.gsub(/^#{resource_key_base}:/, '')
62
+ similarity_key = similarity_key(resource)
63
+ redis.del(similarity_key)
57
64
  yield(key, i, keys.length) if block_given?
58
65
  completed = redis.eval(similarity_lua, keys: [key], argv: [tmp_key_base, resource_key_base, similar_key_base, group_key_base, threshold])
59
66
  if completed == SET_TOO_LARGE_FOR_LUA
60
- calculate_similarity_for_key(key, threshold)
67
+ calculate_similarity_for_key_resource(key, resource, threshold)
61
68
  end
62
69
  end
63
70
  end
64
71
 
65
- def calculate_similarity_for_key(key, threshold)
66
- resource = key.gsub(/^#{resource_key_base}:/, '')
67
- calculate_similarity_for_key_resource(key, resource, threshold)
68
- end
69
72
 
70
73
  def calculate_similarity_for_resource(resource, threshold)
71
74
  key = resource_key(resource)
@@ -73,12 +76,14 @@ module Commendo
73
76
  end
74
77
 
75
78
  def calculate_similarity_for_key_resource(key, resource, threshold)
76
- groups = redis.zrange(resource_key(resource), 0, -1)
79
+ groups = groups(resource)
77
80
  group_keys = groups.map { |group| group_key(group) }
78
81
  tmp_key = "#{tmp_key_base}:#{SecureRandom.uuid}"
79
82
  redis.zunionstore(tmp_key, group_keys)
80
83
  resources = redis.zrange(tmp_key, 0, -1)
81
84
  redis.del(tmp_key)
85
+ similarity_key = similarity_key(resource)
86
+ redis.del(similarity_key)
82
87
  resources.each do |to_compare|
83
88
  next if resource == to_compare
84
89
  redis.eval(pair_comparison_lua, keys: [key, resource_key(to_compare), similarity_key(resource), similarity_key(to_compare)], argv: [tmp_key_base, resource, to_compare, threshold])
@@ -104,7 +109,7 @@ module Commendo
104
109
  end
105
110
 
106
111
  def filtered_similar_to(resource, options = {})
107
- if @tag_set.nil? || (options[:include].nil? && options[:exclude].nil?)
112
+ if @tag_set.nil? || (options[:include].nil? && options[:exclude].nil?) || @tag_set.empty?
108
113
  return similar_to(resource, options[:limit] || 0)
109
114
  else
110
115
  similar = similar_to(resource)
@@ -122,6 +127,20 @@ module Commendo
122
127
  "#{similar_key_base}:#{resource}"
123
128
  end
124
129
 
130
+ def remove_from_groups(resource, *groups)
131
+ resource_key = resource_key(resource)
132
+ redis.zrem(resource_key, groups)
133
+ groups.each do |group|
134
+ group_key = group_key(group)
135
+ redis.zrem(group_key, resource)
136
+ end
137
+ end
138
+
139
+ def remove_from_groups_and_calculate(resource, *groups)
140
+ remove_from_groups(resource, *groups)
141
+ calculate_similarity_for_resource(resource, 0)
142
+ end
143
+
125
144
  private
126
145
 
127
146
  def similarity_lua
@@ -26,20 +26,21 @@ for _,group in ipairs(groups) do
26
26
  end
27
27
  --redis.log(redis.LOG_NOTICE, 'Found ' .. table.getn(group_keys) .. ' group keys')
28
28
 
29
- --TODO change foo
29
+ --TODO change unionfoo to a random slug
30
30
  local tmp_groups_union_key = tmp_key_base .. 'unionfoo'
31
31
  redis.call('ZUNIONSTORE', tmp_groups_union_key, table.getn(group_keys), unpack(group_keys))
32
32
  local resources = redis.call('ZRANGE', tmp_groups_union_key, 0, -1)
33
33
 
34
+ --TODO change 'foo' to something much more unlikely
34
35
  local previous = 'foo'
35
36
  for _,to_compare in ipairs(resources) do
36
37
  --redis.log(redis.LOG_NOTICE, 'Comparing ' .. resource .. ' and ' .. to_compare)
37
38
  if to_compare ~= previous then
38
39
  previous = to_compare
39
- if resource > to_compare then
40
+ if resource ~= to_compare then
40
41
  --redis.log(redis.LOG_NOTICE, 'Calculating similarity for ' .. resource .. ' and ' .. to_compare)
41
42
 
42
- --TODO change bar
43
+ --TODO change bar to a random slug
43
44
  local tmp_pair_intersect_key = tmp_key_base .. 'bar'
44
45
  redis.call('ZINTERSTORE', tmp_pair_intersect_key, 2, resource_key, resource_key_base .. ':' .. to_compare)
45
46
  local intersect = redis.call('ZRANGE', tmp_pair_intersect_key, 0, -1, 'WITHSCORES')
@@ -51,7 +52,7 @@ for _,to_compare in ipairs(resources) do
51
52
  intersect_score = intersect_score + intersect[i+1]
52
53
  end
53
54
 
54
- --TODO change baz
55
+ --TODO change baz to a random slug
55
56
  local tmp_pair_union_key = tmp_key_base .. 'baz'
56
57
  redis.call('ZUNIONSTORE', tmp_pair_union_key, 2, resource_key, resource_key_base .. ':' .. to_compare)
57
58
 
@@ -66,7 +67,7 @@ for _,to_compare in ipairs(resources) do
66
67
  if similarity > threshold then
67
68
  --redis.log(redis.LOG_NOTICE, resource .. ' and ' .. to_compare .. ' scored ' .. similarity)
68
69
  redis.call('ZADD', sim_key_base .. ':' .. resource, similarity, to_compare)
69
- redis.call('ZADD', sim_key_base .. ':' .. to_compare, similarity, resource)
70
+ --redis.call('ZADD', sim_key_base .. ':' .. to_compare, similarity, resource)
70
71
  end
71
72
  end
72
73
  end
@@ -8,6 +8,11 @@ module Commendo
8
8
  @redis, @key_base = redis, key_base
9
9
  end
10
10
 
11
+ def empty?
12
+ cursor, keys = redis.scan(0, match: "#{key_base}:*", count: 1)
13
+ cursor.to_i == 0
14
+ end
15
+
11
16
  def get(resource)
12
17
  redis.smembers(resource_key(resource)).sort
13
18
  end
@@ -1,3 +1,3 @@
1
1
  module Commendo
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -9,134 +9,113 @@ module Commendo
9
9
 
10
10
  class ContentSetTest < Minitest::Test
11
11
 
12
+ def setup
13
+ @redis = Redis.new(db: 15)
14
+ @redis.flushdb
15
+ @key_base = 'CommendoTests'
16
+ @cs = ContentSet.new(@redis, @key_base)
17
+ end
18
+
12
19
  def test_gives_similarity_key_for_resource
13
- redis = Redis.new(db: 15)
14
- key_base = 'CommendoTests'
15
- cs = ContentSet.new(redis, key_base)
16
- assert_equal 'CommendoTests:similar:resource-1', cs.similarity_key('resource-1')
20
+ key_base = 'CommendoTestsFooBarBaz'
21
+ cs = ContentSet.new(@redis, key_base)
22
+ assert_equal 'CommendoTestsFooBarBaz:similar:resource-1', cs.similarity_key('resource-1')
17
23
  end
18
24
 
19
25
  def test_recommends_when_added
20
- redis = Redis.new(db: 15)
21
- redis.flushdb
22
- key_base = 'CommendoTests'
23
- cs = ContentSet.new(redis, key_base)
24
- cs.add('resource-1', 'group-1', 'group-2')
25
- cs.add('resource-2', 'group-1')
26
- cs.add('resource-3', 'group-1', 'group-2')
27
- cs.add('resource-4', 'group-2')
28
- cs.calculate_similarity
26
+ @cs.add('resource-1', 'group-1', 'group-2')
27
+ @cs.add('resource-2', 'group-1')
28
+ @cs.add('resource-3', 'group-1', 'group-2')
29
+ @cs.add('resource-4', 'group-2')
30
+ @cs.calculate_similarity
29
31
  expected = [
30
32
  {resource: 'resource-3', similarity: 1.0},
31
33
  {resource: 'resource-4', similarity: 0.667},
32
34
  {resource: 'resource-2', similarity: 0.667}
33
35
  ]
34
- assert_equal expected, cs.similar_to('resource-1')
36
+ assert_equal expected, @cs.similar_to('resource-1')
35
37
  end
36
38
 
37
39
  def test_recommends_limited_by_number
38
- redis = Redis.new(db: 15)
39
- redis.flushdb
40
- key_base = 'CommendoTests'
41
- cs = ContentSet.new(redis, key_base)
42
- cs.add('resource-1', 'group-1', 'group-2')
43
- cs.add('resource-2', 'group-1')
44
- cs.add('resource-3', 'group-1', 'group-2')
45
- cs.add('resource-4', 'group-2')
46
- cs.calculate_similarity
40
+ @cs.add('resource-1', 'group-1', 'group-2')
41
+ @cs.add('resource-2', 'group-1')
42
+ @cs.add('resource-3', 'group-1', 'group-2')
43
+ @cs.add('resource-4', 'group-2')
44
+ @cs.calculate_similarity
47
45
  expected = [
48
46
  {resource: 'resource-3', similarity: 1.0},
49
47
  {resource: 'resource-4', similarity: 0.667},
50
48
  {resource: 'resource-2', similarity: 0.667}
51
49
  ]
52
- assert_equal expected[0..0], cs.similar_to('resource-1', 1)
53
- assert_equal expected[0..1], cs.similar_to('resource-1', 2)
54
- assert_equal expected, cs.similar_to('resource-1', 3)
55
- assert_equal expected, cs.similar_to('resource-1', 99)
50
+ assert_equal expected[0..0], @cs.similar_to('resource-1', 1)
51
+ assert_equal expected[0..1], @cs.similar_to('resource-1', 2)
52
+ assert_equal expected, @cs.similar_to('resource-1', 3)
53
+ assert_equal expected, @cs.similar_to('resource-1', 99)
56
54
  end
57
55
 
58
56
  def test_recommends_when_added_with_scores
59
- redis = Redis.new(db: 15)
60
- redis.flushdb
61
- key_base = 'CommendoTests'
62
- cs = ContentSet.new(redis, key_base)
63
- cs.add('resource-1', ['group-1', 2], ['group-2', 2])
64
- cs.add('resource-2', ['group-1', 7])
65
- cs.add('resource-3', ['group-1', 2], ['group-2', 2])
66
- cs.add('resource-4', ['group-2', 3])
67
- cs.calculate_similarity
57
+ @cs.add('resource-1', ['group-1', 2], ['group-2', 2])
58
+ @cs.add('resource-2', ['group-1', 7])
59
+ @cs.add('resource-3', ['group-1', 2], ['group-2', 2])
60
+ @cs.add('resource-4', ['group-2', 3])
61
+ @cs.calculate_similarity
68
62
  expected = [
69
63
  {resource: 'resource-3', similarity: 1.0},
70
64
  {resource: 'resource-2', similarity: 0.818},
71
65
  {resource: 'resource-4', similarity: 0.714}
72
66
  ]
73
- assert_equal expected, cs.similar_to('resource-1')
67
+ assert_equal expected, @cs.similar_to('resource-1')
74
68
  end
75
69
 
76
70
  def test_recommends_with_large_number_of_groups
77
- redis = Redis.new(db: 15)
78
- redis.flushdb
79
- key_base = 'CommendoTests'
80
- cs = ContentSet.new(redis, key_base)
81
71
  (0..3000).each do |i|
82
- cs.add('resource-1', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
83
- cs.add('resource-9', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
72
+ @cs.add('resource-1', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
73
+ @cs.add('resource-9', ["group-#{i}", i/100.0], ["group-#{i+1}", i/20.0])
84
74
  end
85
- cs.calculate_similarity
75
+ @cs.calculate_similarity
86
76
  expected = [
87
77
  {resource: 'resource-9', similarity: 1.0}
88
78
  ]
89
- assert_equal expected, cs.similar_to('resource-1')
79
+ assert_equal expected, @cs.similar_to('resource-1')
90
80
  end
91
81
 
92
82
  def test_recommends_when_extra_scores_added
93
- test_recommends_when_added_with_scores
94
- redis = Redis.new(db: 15)
95
- key_base = 'CommendoTests'
96
- cs = ContentSet.new(redis, key_base)
97
- cs.add('resource-3', ['group-1', 1], ['group-3', 2])
98
- cs.add('resource-4', ['group-2', 1])
99
- cs.add_by_group('group-1', ['newource-9', 100], 'resource-2', 'resource-3')
100
- cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
101
- cs.calculate_similarity
83
+ test_recommends_when_added_with_scores #sets up the content set
84
+ @cs.add('resource-3', ['group-1', 1], ['group-3', 2])
85
+ @cs.add('resource-4', ['group-2', 1])
86
+ @cs.add_by_group('group-1', ['newource-9', 100], 'resource-2', 'resource-3')
87
+ @cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
88
+ @cs.calculate_similarity
102
89
  expected = [
103
90
  {resource: 'newource-9', similarity: 1.0},
104
91
  {resource: 'resource-1', similarity: 0.769},
105
92
  {resource: 'resource-3', similarity: 0.706}
106
93
  ]
107
- assert_equal expected, cs.similar_to('resource-2')
94
+ assert_equal expected, @cs.similar_to('resource-2')
108
95
  end
109
96
 
110
97
  def test_recommends_when_added_by_group
111
- redis = Redis.new(db: 15)
112
- redis.flushdb
113
- key_base = 'CommendoTests'
114
- cs = ContentSet.new(redis, key_base)
115
- cs.add_by_group('group-1', 'resource-1', 'resource-2', 'resource-3')
116
- cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
117
- cs.calculate_similarity
98
+ @cs.add_by_group('group-1', 'resource-1', 'resource-2', 'resource-3')
99
+ @cs.add_by_group('group-2', 'resource-1', 'resource-3', 'resource-4')
100
+ @cs.calculate_similarity
118
101
  expected = [
119
102
  {resource: 'resource-3', similarity: 1.0},
120
103
  {resource: 'resource-4', similarity: 0.667},
121
104
  {resource: 'resource-2', similarity: 0.667}
122
105
  ]
123
- assert_equal expected, cs.similar_to('resource-1')
106
+ assert_equal expected, @cs.similar_to('resource-1')
124
107
  end
125
108
 
126
109
  def test_recommends_when_added_by_group_with_scores
127
- redis = Redis.new(db: 15)
128
- redis.flushdb
129
- key_base = 'CommendoTests'
130
- cs = ContentSet.new(redis, key_base)
131
- cs.add_by_group('group-1', ['resource-1', 2], ['resource-2', 3], ['resource-3', 7])
132
- cs.add_by_group('group-2', ['resource-1', 2], ['resource-3', 3], ['resource-4', 5])
133
- cs.calculate_similarity
110
+ @cs.add_by_group('group-1', ['resource-1', 2], ['resource-2', 3], ['resource-3', 7])
111
+ @cs.add_by_group('group-2', ['resource-1', 2], ['resource-3', 3], ['resource-4', 5])
112
+ @cs.calculate_similarity
134
113
  expected = [
135
114
  {resource: 'resource-3', similarity: 1.0},
136
115
  {resource: 'resource-4', similarity: 0.778},
137
116
  {resource: 'resource-2', similarity: 0.714}
138
117
  ]
139
- assert_equal expected, cs.similar_to('resource-1')
118
+ assert_equal expected, @cs.similar_to('resource-1')
140
119
  end
141
120
 
142
121
  def test_recommendations_are_isolated_by_key_base
@@ -148,16 +127,12 @@ module Commendo
148
127
  end
149
128
 
150
129
  def test_calculates_similarity_scores
151
- redis = Redis.new(db: 15)
152
- redis.flushdb
153
- key_base = 'CommendoTests'
154
- cs = ContentSet.new(redis, key_base)
155
130
  (3..23).each do |group|
156
131
  (3..23).each do |res|
157
- cs.add_by_group(group, res) if res % group == 0
132
+ @cs.add_by_group(group, res) if res % group == 0
158
133
  end
159
134
  end
160
- cs.calculate_similarity
135
+ @cs.calculate_similarity
161
136
  expected = [
162
137
  {resource: '9', similarity: 0.667},
163
138
  {resource: '6', similarity: 0.667},
@@ -166,108 +141,124 @@ module Commendo
166
141
  {resource: '21', similarity: 0.286},
167
142
  {resource: '15', similarity: 0.286}
168
143
  ]
169
- assert_equal expected, cs.similar_to(18)
144
+ assert_equal expected, @cs.similar_to(18)
170
145
  end
171
146
 
172
147
  def test_calculates_with_threshold
173
- redis = Redis.new(db: 15)
174
- redis.flushdb
175
- key_base = 'CommendoTests'
176
- cs = ContentSet.new(redis, key_base)
177
148
  (3..23).each do |group|
178
149
  (3..23).each do |res|
179
- cs.add_by_group(group, res) if res % group == 0
150
+ @cs.add_by_group(group, res) if res % group == 0
180
151
  end
181
152
  end
182
- cs.calculate_similarity(0.4)
153
+ @cs.calculate_similarity(0.4)
183
154
  expected = [
184
155
  {resource: '9', similarity: 0.667},
185
156
  {resource: '6', similarity: 0.667},
186
157
  {resource: '12', similarity: 0.5}
187
158
  ]
188
- assert_equal expected, cs.similar_to(18)
159
+ assert_equal expected, @cs.similar_to(18)
189
160
  end
190
161
 
191
162
  def test_calculate_yields_after_each
192
- redis = Redis.new(db: 15)
193
- redis.flushdb
194
- key_base = 'CommendoTests'
195
- cs = ContentSet.new(redis, key_base)
196
163
  (3..23).each do |group|
197
164
  (3..23).each do |res|
198
- cs.add_by_group(group, res) if res % group == 0
165
+ @cs.add_by_group(group, res) if res % group == 0
199
166
  end
200
167
  end
201
168
  expected_keys = ['CommendoTests:resources:3', 'CommendoTests:resources:4', 'CommendoTests:resources:5', 'CommendoTests:resources:6', 'CommendoTests:resources:7', 'CommendoTests:resources:8', 'CommendoTests:resources:9', 'CommendoTests:resources:10', 'CommendoTests:resources:11', 'CommendoTests:resources:12', 'CommendoTests:resources:13', 'CommendoTests:resources:14', 'CommendoTests:resources:15', 'CommendoTests:resources:16', 'CommendoTests:resources:17', 'CommendoTests:resources:18', 'CommendoTests:resources:19', 'CommendoTests:resources:20', 'CommendoTests:resources:21', 'CommendoTests:resources:22', 'CommendoTests:resources:23']
202
169
  actual_keys = []
203
- cs.calculate_similarity { |key, index, total|
170
+ @cs.calculate_similarity { |key, index, total|
204
171
  actual_keys << key
205
172
  }
206
173
  assert_equal expected_keys.sort, actual_keys.sort
207
174
  end
208
175
 
209
- def test_calculate_deletes_old_values_first
210
- skip
211
- end
212
-
213
176
  def test_deletes_resource_from_everywhere
214
- redis = Redis.new(db: 15)
215
- redis.flushdb
216
- key_base = 'CommendoTests'
217
- cs = ContentSet.new(redis, key_base)
218
177
  (3..23).each do |group|
219
178
  (3..23).each do |res|
220
- cs.add_by_group(group, res) if res % group == 0
179
+ @cs.add_by_group(group, res) if res % group == 0
221
180
  end
222
181
  end
223
- cs.calculate_similarity
224
- assert similar_to(cs, 18, 12)
182
+ @cs.calculate_similarity
183
+ assert similar_to(@cs, 18, 12)
184
+
185
+ @cs.delete(12)
186
+ assert_equal [], @cs.similar_to(12)
187
+ refute similar_to(@cs, 18, 12)
225
188
 
226
- cs.delete(12)
227
- assert_equal [], cs.similar_to(12)
228
- refute similar_to(cs, 18, 12)
189
+ @cs.calculate_similarity
190
+ assert_equal [], @cs.similar_to(12)
191
+ refute similar_to(@cs, 18, 12)
192
+ end
229
193
 
230
- cs.calculate_similarity
231
- assert_equal [], cs.similar_to(12)
232
- refute similar_to(cs, 18, 12)
194
+ def test_remove_from_groups
195
+ (3..23).each do |group|
196
+ (3..23).each do |res|
197
+ @cs.add(res, group) if res % group == 0
198
+ end
199
+ end
200
+ resource = 20
201
+ assert_equal ['4','5','10','20'].sort!, @cs.groups(resource).sort!
202
+ @cs.remove_from_groups(resource, 10)
203
+ assert_equal ['4','5','20'].sort!, @cs.groups(resource).sort!
204
+ @cs.remove_from_groups(resource, 4)
205
+ assert_equal ['5','20'].sort!, @cs.groups(resource).sort!
206
+ end
233
207
 
208
+ def test_remove_causes_similarity_to_change_when_recalculated
209
+ (3..23).each do |group|
210
+ (3..23).each do |res|
211
+ @cs.add(res, group) if res % group == 0
212
+ end
213
+ end
214
+ @cs.calculate_similarity
215
+ assert similar_to(@cs, 18, 12)
216
+ @cs.remove_from_groups(18, 6, 3)
217
+ @cs.calculate_similarity
218
+ refute similar_to(@cs, 18, 12)
234
219
  end
235
220
 
236
221
  def test_accepts_incremental_updates
237
- redis = Redis.new(db: 15)
238
- redis.flushdb
239
- key_base = 'CommendoTests'
240
- cs = ContentSet.new(redis, key_base)
241
222
  (3..23).each do |group|
242
223
  (3..23).each do |res|
243
- cs.add(res, group) if res % group == 0
224
+ @cs.add(res, group) if res % group == 0
244
225
  end
245
226
  end
246
- cs.calculate_similarity
247
- assert similar_to(cs, 18, 12)
248
- refute similar_to(cs, 10, 12)
227
+ @cs.calculate_similarity
228
+ assert similar_to(@cs, 18, 12)
229
+ refute similar_to(@cs, 10, 12)
230
+
231
+ @cs.add_and_calculate(12, 'foo', true)
232
+ @cs.add_and_calculate(10, 'foo', true)
233
+ assert similar_to(@cs, 10, 12)
234
+ end
249
235
 
250
- cs.add_and_calculate(12, 'foo', true)
251
- cs.add_and_calculate(10, 'foo', true)
252
- assert similar_to(cs, 10, 12)
236
+ def test_remove_and_calculate
237
+ (3..23).each do |group|
238
+ (3..23).each do |res|
239
+ @cs.add(res, group) if res % group == 0
240
+ end
241
+ end
242
+ @cs.calculate_similarity
243
+ assert similar_to(@cs, 18, 12)
244
+ @cs.remove_from_groups_and_calculate(18, 6, 3)
245
+ refute similar_to(@cs, 18, 12)
253
246
  end
254
247
 
255
248
  def test_filters_include_by_tag_collection
256
- redis = Redis.new(db: 15)
257
- redis.flushdb
258
- ts = TagSet.new(redis, 'CommendoTests:tags')
259
- cs = ContentSet.new(redis, 'CommendoTests', ts)
249
+ ts = TagSet.new(@redis, "#{@key_base}:tags")
250
+ @cs = ContentSet.new(@redis, @key_base, ts)
260
251
  (3..23).each do |group|
261
252
  (3..23).each do |res|
262
- cs.add(res, group) if res % group == 0
253
+ @cs.add(res, group) if res % group == 0
263
254
  ts.add(res, 'mod3') if res.modulo(3).zero?
264
255
  ts.add(res, 'mod4') if res.modulo(4).zero?
265
256
  ts.add(res, 'mod5') if res.modulo(5).zero?
266
257
  end
267
258
  end
268
- cs.calculate_similarity
259
+ @cs.calculate_similarity
269
260
 
270
- actual = cs.filtered_similar_to(10, include: ['mod5'])
261
+ actual = @cs.filtered_similar_to(10, include: ['mod5'])
271
262
  assert_equal 3, actual.length
272
263
  assert contains_resource('5', actual)
273
264
  assert contains_resource('15', actual)
@@ -276,21 +267,19 @@ module Commendo
276
267
  end
277
268
 
278
269
  def test_filters_include_by_tag_collection_and_limit
279
- redis = Redis.new(db: 15)
280
- redis.flushdb
281
- ts = TagSet.new(redis, 'CommendoTests:tags')
282
- cs = ContentSet.new(redis, 'CommendoTests', ts)
270
+ ts = TagSet.new(@redis, "#{@key_base}:tags")
271
+ @cs = ContentSet.new(@redis, @key_base, ts)
283
272
  (3..23).each do |group|
284
273
  (3..23).each do |res|
285
- cs.add(res, group) if res % group == 0
274
+ @cs.add(res, group) if res % group == 0
286
275
  ts.add(res, 'mod3') if res.modulo(3).zero?
287
276
  ts.add(res, 'mod4') if res.modulo(4).zero?
288
277
  ts.add(res, 'mod5') if res.modulo(5).zero?
289
278
  end
290
279
  end
291
- cs.calculate_similarity
280
+ @cs.calculate_similarity
292
281
 
293
- actual = cs.filtered_similar_to(10, include: ['mod5'], limit: 2)
282
+ actual = @cs.filtered_similar_to(10, include: ['mod5'], limit: 2)
294
283
  assert_equal 2, actual.length
295
284
  assert contains_resource('5', actual)
296
285
  #assert contains_resource('15', actual)
@@ -299,44 +288,40 @@ module Commendo
299
288
  end
300
289
 
301
290
  def test_filters_exclude_by_tag_collection
302
- redis = Redis.new(db: 15)
303
- redis.flushdb
304
- ts = TagSet.new(redis, 'CommendoTests:tags')
305
- cs = ContentSet.new(redis, 'CommendoTests', ts)
291
+ ts = TagSet.new(@redis, "#{@key_base}:tags")
292
+ @cs = ContentSet.new(@redis, @key_base, ts)
306
293
  (3..23).each do |group|
307
294
  (3..23).each do |res|
308
- cs.add(res, group) if res % group == 0
295
+ @cs.add(res, group) if res % group == 0
309
296
  ts.add(res, 'mod3') if res.modulo(3).zero?
310
297
  ts.add(res, 'mod4') if res.modulo(4).zero?
311
298
  ts.add(res, 'mod5') if res.modulo(5).zero?
312
299
  end
313
300
  end
314
- cs.calculate_similarity
301
+ @cs.calculate_similarity
315
302
 
316
- actual = cs.filtered_similar_to(10, exclude: ['mod3'])
303
+ actual = @cs.filtered_similar_to(10, exclude: ['mod3'])
317
304
  assert_equal 2, actual.length
318
305
  assert contains_resource('5', actual)
319
306
  assert contains_resource('20', actual)
320
307
  refute contains_resource('15', actual)
321
-
322
308
  end
323
309
 
324
310
  def test_filters_includes_and_exclude_by_tag_collection
325
- redis = Redis.new(db: 15)
326
- redis.flushdb
327
- ts = TagSet.new(redis, 'CommendoTests:tags')
328
- cs = ContentSet.new(redis, 'CommendoTests', ts)
311
+ ts = TagSet.new(@redis, "#{@key_base}:tags")
312
+ @cs = ContentSet.new(@redis, @key_base, ts)
313
+ #Build some test data
329
314
  (3..23).each do |group|
330
315
  (3..23).each do |res|
331
- cs.add(res, group) if res % group == 0
316
+ @cs.add(res, group) if res % group == 0
332
317
  ts.add(res, 'mod3') if res.modulo(3).zero?
333
318
  ts.add(res, 'mod4') if res.modulo(4).zero?
334
319
  ts.add(res, 'mod5') if res.modulo(5).zero?
335
320
  end
336
321
  end
337
- cs.calculate_similarity
322
+ @cs.calculate_similarity
338
323
 
339
- actual = cs.filtered_similar_to(12, include: ['mod4'], exclude: ['mod3', 'mod5'])
324
+ actual = @cs.filtered_similar_to(12, include: ['mod4'], exclude: ['mod3', 'mod5'])
340
325
  assert_equal 3, actual.length
341
326
 
342
327
  refute contains_resource('6', actual)
@@ -349,23 +334,20 @@ module Commendo
349
334
  assert contains_resource('16', actual)
350
335
  refute contains_resource('15', actual)
351
336
  refute contains_resource('20', actual)
352
-
353
337
  end
354
338
 
355
339
  def test_recommends_for_many
356
- redis = Redis.new(db: 15)
357
- redis.flushdb
358
- ts = TagSet.new(redis, 'CommendoTests:tags')
359
- cs = ContentSet.new(redis, 'CommendoTests', ts)
340
+ ts = TagSet.new(@redis, "#{@key_base}:tags")
341
+ @cs = ContentSet.new(@redis, @key_base, ts)
360
342
  (3..23).each do |group|
361
343
  (3..23).each do |res|
362
- cs.add(res, group) if res % group == 0
344
+ @cs.add(res, group) if res % group == 0
363
345
  ts.add(res, 'mod3') if res.modulo(3).zero?
364
346
  ts.add(res, 'mod4') if res.modulo(4).zero?
365
347
  ts.add(res, 'mod5') if res.modulo(5).zero?
366
348
  end
367
349
  end
368
- cs.calculate_similarity
350
+ @cs.calculate_similarity
369
351
  expected = [
370
352
  {resource: '18', similarity: 1.834},
371
353
  {resource: '3', similarity: 1.734},
@@ -379,26 +361,24 @@ module Commendo
379
361
  {resource: '16', similarity: 0.286},
380
362
  {resource: '20', similarity: 0.25}
381
363
  ]
382
- actual = cs.similar_to([12, 6, 9])
364
+ actual = @cs.similar_to([12, 6, 9])
383
365
  assert_equal expected, actual
384
366
  #, include: ['mod4'], exclude: ['mod3', 'mod5']
385
367
  end
386
368
 
387
369
  def test_recommends_for_many_applies_filters
388
- redis = Redis.new(db: 15)
389
- redis.flushdb
390
- ts = TagSet.new(redis, 'CommendoTests:tags')
391
- cs = ContentSet.new(redis, 'CommendoTests', ts)
370
+ ts = TagSet.new(@redis, "#{@key_base}:tags")
371
+ @cs = ContentSet.new(@redis, @key_base, ts)
392
372
  (3..23).each do |group|
393
373
  (3..23).each do |res|
394
- cs.add(res, group) if res % group == 0
374
+ @cs.add(res, group) if res % group == 0
395
375
  ts.add(res, 'mod3') if res.modulo(3).zero?
396
376
  ts.add(res, 'mod4') if res.modulo(4).zero?
397
377
  ts.add(res, 'mod5') if res.modulo(5).zero?
398
378
  end
399
379
  end
400
- cs.calculate_similarity
401
- actual = cs.filtered_similar_to([12, 6, 9], include: ['mod4'], exclude: ['mod3', 'mod5'])
380
+ @cs.calculate_similarity
381
+ actual = @cs.filtered_similar_to([12, 6, 9], include: ['mod4'], exclude: ['mod3', 'mod5'])
402
382
  refute contains_resource('6', actual)
403
383
  refute contains_resource('18', actual)
404
384
  assert contains_resource('4', actual)
@@ -416,7 +396,7 @@ module Commendo
416
396
  end
417
397
 
418
398
  def contains_resource(resource, similarities)
419
- similarities.select { |sim| sim[:resource] == "#{resource}" }.length > 0
399
+ !similarities.select { |sim| sim[:resource] == "#{resource}" }.empty?
420
400
  end
421
401
 
422
402
  end
data/test/tag_set_test.rb CHANGED
@@ -9,118 +9,112 @@ module Commendo
9
9
 
10
10
  class TagSetTest < Minitest::Test
11
11
 
12
+ def setup
13
+ @redis = Redis.new(db: 15)
14
+ @redis.flushdb
15
+ @ts = TagSet.new(@redis, 'TagSetTest')
16
+ end
17
+
12
18
  def test_adds_tags_for_resource
13
- redis = Redis.new(db: 15)
14
- redis.flushdb
15
- ts = TagSet.new(redis, 'TagSetTest')
16
- assert_equal [], ts.get(1)
17
- ts.add(1, 'foo', 'bar', 'baz')
18
- assert_equal ['bar', 'baz','foo'], ts.get(1)
19
- ts.add(1, 'qux', 'qip')
20
- assert_equal ['bar', 'baz', 'foo', 'qip', 'qux'], ts.get(1)
19
+ assert_equal [], @ts.get(1)
20
+ @ts.add(1, 'foo', 'bar', 'baz')
21
+ assert_equal ['bar', 'baz','foo'], @ts.get(1)
22
+ @ts.add(1, 'qux', 'qip')
23
+ assert_equal ['bar', 'baz', 'foo', 'qip', 'qux'], @ts.get(1)
24
+ end
25
+
26
+ def test_empty?
27
+ assert @ts.respond_to? :empty?
28
+ assert @ts.empty?
29
+ @ts.add(1, 'qux', 'qip')
30
+ refute @ts.empty?
31
+ @ts.delete(1)
32
+ assert @ts.empty?
21
33
  end
22
34
 
23
35
  def test_sets_tags_for_resource
24
- redis = Redis.new(db: 15)
25
- redis.flushdb
26
- ts = TagSet.new(redis, 'TagSetTest')
27
- assert_equal [], ts.get(1)
28
- ts.set(1, 'foo', 'bar', 'baz')
29
- assert_equal ['bar', 'baz', 'foo'], ts.get(1)
30
- ts.set(1, 'qux', 'qip')
31
- assert_equal ['qip', 'qux'], ts.get(1)
36
+ assert_equal [], @ts.get(1)
37
+ @ts.set(1, 'foo', 'bar', 'baz')
38
+ assert_equal ['bar', 'baz', 'foo'], @ts.get(1)
39
+ @ts.set(1, 'qux', 'qip')
40
+ assert_equal ['qip', 'qux'], @ts.get(1)
32
41
  end
33
42
 
34
43
  def test_sets_tags_when_empty
35
- redis = Redis.new(db: 15)
36
- redis.flushdb
37
- ts = TagSet.new(redis, 'TagSetTest')
38
- ts.set(1, 'foo', 'bar', 'baz')
39
- ts.set(2, 'qux', 'qip')
40
- assert_equal ['bar', 'baz', 'foo'], ts.get(1)
41
- assert_equal ['qip', 'qux'], ts.get(2)
42
- ts.set(1, *[])
43
- assert_equal [], ts.get(1)
44
- assert_equal ['qip', 'qux'], ts.get(2)
44
+ @ts.set(1, 'foo', 'bar', 'baz')
45
+ @ts.set(2, 'qux', 'qip')
46
+ assert_equal ['bar', 'baz', 'foo'], @ts.get(1)
47
+ assert_equal ['qip', 'qux'], @ts.get(2)
48
+ @ts.set(1, *[])
49
+ assert_equal [], @ts.get(1)
50
+ assert_equal ['qip', 'qux'], @ts.get(2)
45
51
  end
46
52
 
47
53
  def test_deletes_tags_for_resource
48
- redis = Redis.new(db: 15)
49
- redis.flushdb
50
- ts = TagSet.new(redis, 'TagSetTest')
51
- ts.set(1, 'foo', 'bar', 'baz')
52
- ts.set(2, 'qux', 'qip')
53
- assert_equal ['bar', 'baz', 'foo'], ts.get(1)
54
- assert_equal ['qip', 'qux'], ts.get(2)
55
- ts.delete(1)
56
- assert_equal [], ts.get(1)
57
- assert_equal ['qip', 'qux'], ts.get(2)
54
+ @ts.set(1, 'foo', 'bar', 'baz')
55
+ @ts.set(2, 'qux', 'qip')
56
+ assert_equal ['bar', 'baz', 'foo'], @ts.get(1)
57
+ assert_equal ['qip', 'qux'], @ts.get(2)
58
+ @ts.delete(1)
59
+ assert_equal [], @ts.get(1)
60
+ assert_equal ['qip', 'qux'], @ts.get(2)
58
61
  end
59
62
 
60
63
  def test_matches_tags
61
- redis = Redis.new(db: 15)
62
- redis.flushdb
63
- ts = TagSet.new(redis, 'TagSetTest')
64
- ts.set(1, 'foo', 'bar', 'baz')
65
- ts.set(2, 'qux', 'qip')
66
-
67
- assert ts.matches(1, ['foo'])
68
- assert ts.matches(1, ['bar', 'baz'])
69
- assert ts.matches(1, ['bar', 'baz', 'foo'])
70
- refute ts.matches(1, ['qux'])
71
- refute ts.matches(1, ['qip'])
72
-
73
- refute ts.matches(2, ['foo'])
74
- refute ts.matches(2, ['bar', 'baz'])
75
- refute ts.matches(2, ['bar', 'baz', 'foo'])
76
- assert ts.matches(2, ['qux', 'qip'])
77
- assert ts.matches(2, ['qux'])
78
- assert ts.matches(2, ['qip'])
64
+ @ts.set(1, 'foo', 'bar', 'baz')
65
+ @ts.set(2, 'qux', 'qip')
66
+
67
+ assert @ts.matches(1, ['foo'])
68
+ assert @ts.matches(1, ['bar', 'baz'])
69
+ assert @ts.matches(1, ['bar', 'baz', 'foo'])
70
+ refute @ts.matches(1, ['qux'])
71
+ refute @ts.matches(1, ['qip'])
72
+
73
+ refute @ts.matches(2, ['foo'])
74
+ refute @ts.matches(2, ['bar', 'baz'])
75
+ refute @ts.matches(2, ['bar', 'baz', 'foo'])
76
+ assert @ts.matches(2, ['qux', 'qip'])
77
+ assert @ts.matches(2, ['qux'])
78
+ assert @ts.matches(2, ['qip'])
79
79
  end
80
80
 
81
81
  def test_matches_exclude_tags
82
- redis = Redis.new(db: 15)
83
- redis.flushdb
84
- ts = TagSet.new(redis, 'TagSetTest')
85
- ts.set(1, 'foo', 'bar', 'baz')
86
- ts.set(2, 'qux', 'qip')
87
-
88
- refute ts.matches(1, nil, ['foo'])
89
- refute ts.matches(1, [], ['foo'])
90
- refute ts.matches(1, [], ['bar', 'baz'])
91
- refute ts.matches(1, [], ['bar', 'baz', 'foo'])
92
- assert ts.matches(1, [], ['qux'])
93
- assert ts.matches(1, [], ['qip'])
94
-
95
- assert ts.matches(2, nil, ['foo'])
96
- assert ts.matches(2, [], ['foo'])
97
- assert ts.matches(2, [], ['bar', 'baz'])
98
- assert ts.matches(2, [], ['bar', 'baz', 'foo'])
99
- refute ts.matches(2, [], ['qux', 'qip'])
100
- refute ts.matches(2, [], ['qux'])
101
- refute ts.matches(2, [], ['qip'])
82
+ @ts.set(1, 'foo', 'bar', 'baz')
83
+ @ts.set(2, 'qux', 'qip')
84
+
85
+ refute @ts.matches(1, nil, ['foo'])
86
+ refute @ts.matches(1, [], ['foo'])
87
+ refute @ts.matches(1, [], ['bar', 'baz'])
88
+ refute @ts.matches(1, [], ['bar', 'baz', 'foo'])
89
+ assert @ts.matches(1, [], ['qux'])
90
+ assert @ts.matches(1, [], ['qip'])
91
+
92
+ assert @ts.matches(2, nil, ['foo'])
93
+ assert @ts.matches(2, [], ['foo'])
94
+ assert @ts.matches(2, [], ['bar', 'baz'])
95
+ assert @ts.matches(2, [], ['bar', 'baz', 'foo'])
96
+ refute @ts.matches(2, [], ['qux', 'qip'])
97
+ refute @ts.matches(2, [], ['qux'])
98
+ refute @ts.matches(2, [], ['qip'])
102
99
  end
103
100
 
104
101
  def test_matches_include_and_exclude_tags
105
- redis = Redis.new(db: 15)
106
- redis.flushdb
107
- ts = TagSet.new(redis, 'TagSetTest')
108
- ts.set(1, 'foo', 'bar', 'baz')
109
- ts.set(2, 'qux', 'qip')
110
-
111
- refute ts.matches(1, ['foo'], ['bar'])
112
- refute ts.matches(1, ['bar'], ['foo'])
113
-
114
- assert ts.matches(1, ['foo'], [])
115
- assert ts.matches(1, ['foo'], nil)
116
- assert ts.matches(1, ['foo'], ['qux'])
117
-
118
- assert ts.matches(2, ['qip'], ['foo'])
119
- assert ts.matches(2, ['qux'], ['bar', 'baz'])
120
- assert ts.matches(2, ['qip'], ['bar', 'baz', 'foo'])
121
- refute ts.matches(2, ['qip'], ['qux', 'qip'])
122
- refute ts.matches(2, ['qip'], ['qux'])
123
- refute ts.matches(2, ['qux'], ['qip'])
102
+ @ts.set(1, 'foo', 'bar', 'baz')
103
+ @ts.set(2, 'qux', 'qip')
104
+
105
+ refute @ts.matches(1, ['foo'], ['bar'])
106
+ refute @ts.matches(1, ['bar'], ['foo'])
107
+
108
+ assert @ts.matches(1, ['foo'], [])
109
+ assert @ts.matches(1, ['foo'], nil)
110
+ assert @ts.matches(1, ['foo'], ['qux'])
111
+
112
+ assert @ts.matches(2, ['qip'], ['foo'])
113
+ assert @ts.matches(2, ['qux'], ['bar', 'baz'])
114
+ assert @ts.matches(2, ['qip'], ['bar', 'baz', 'foo'])
115
+ refute @ts.matches(2, ['qip'], ['qux', 'qip'])
116
+ refute @ts.matches(2, ['qip'], ['qux'])
117
+ refute @ts.matches(2, ['qux'], ['qip'])
124
118
  end
125
119
 
126
120
  end
metadata CHANGED
@@ -1,111 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: commendo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Styles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-22 00:00:00.000000000 Z
11
+ date: 2015-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: progressbar
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.5'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: yard
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: minitest
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ~>
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: 5.0.8
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ~>
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: 5.0.8
111
111
  description: A Jaccard-similarity recommender using Redis sets
@@ -119,7 +119,7 @@ executables:
119
119
  extensions: []
120
120
  extra_rdoc_files: []
121
121
  files:
122
- - .gitignore
122
+ - ".gitignore"
123
123
  - CHANGELOG.md
124
124
  - Gemfile
125
125
  - LICENSE.txt
@@ -150,17 +150,17 @@ require_paths:
150
150
  - lib
151
151
  required_ruby_version: !ruby/object:Gem::Requirement
152
152
  requirements:
153
- - - '>='
153
+ - - ">="
154
154
  - !ruby/object:Gem::Version
155
155
  version: '0'
156
156
  required_rubygems_version: !ruby/object:Gem::Requirement
157
157
  requirements:
158
- - - '>='
158
+ - - ">="
159
159
  - !ruby/object:Gem::Version
160
160
  version: '0'
161
161
  requirements: []
162
162
  rubyforge_project:
163
- rubygems_version: 2.2.0
163
+ rubygems_version: 2.2.2
164
164
  signing_key:
165
165
  specification_version: 4
166
166
  summary: A Jaccard-similarity recommender using Redis sets