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 +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/commendo/content_set.rb +26 -7
- data/lib/commendo/similarity.lua +6 -5
- data/lib/commendo/tag_set.rb +5 -0
- data/lib/commendo/version.rb +1 -1
- data/test/content_set_test.rb +144 -164
- data/test/tag_set_test.rb +87 -93
- metadata +20 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9504182a72226de650736e2b987eda2734a5f3bf
|
4
|
+
data.tar.gz: ff9fb6d451b160f8f2ac4da2d4cb2688621ad7ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7669f08e1c313865623360a14f72d7686da669c36c7a096485f9da9d9552f904b7cf808bbdaf2379af32d681939a6ab5d587759cbead1fb8037d6869ef91e048
|
7
|
+
data.tar.gz: e8a729ece664f9167e0f2efd8212f5d1ac87ab5edd5dc443ff561ae047d81a97ab6d1d5dafccf7b2c8aac209bc38bc34cdd456e01816bba050ad03cd32a8c8b8
|
data/CHANGELOG.md
CHANGED
data/lib/commendo/content_set.rb
CHANGED
@@ -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
|
-
|
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 =
|
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
|
data/lib/commendo/similarity.lua
CHANGED
@@ -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
|
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
|
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
|
-
|
70
|
+
--redis.call('ZADD', sim_key_base .. ':' .. to_compare, similarity, resource)
|
70
71
|
end
|
71
72
|
end
|
72
73
|
end
|
data/lib/commendo/tag_set.rb
CHANGED
data/lib/commendo/version.rb
CHANGED
data/test/content_set_test.rb
CHANGED
@@ -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
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
cs
|
24
|
-
cs.
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
cs
|
42
|
-
cs.
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
cs
|
63
|
-
cs.
|
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
|
-
|
95
|
-
|
96
|
-
cs
|
97
|
-
cs.
|
98
|
-
cs.
|
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
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
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.
|
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
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
251
|
-
|
252
|
-
|
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
|
-
|
257
|
-
redis
|
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
|
-
|
280
|
-
redis
|
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
|
-
|
303
|
-
redis
|
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
|
-
|
326
|
-
redis
|
327
|
-
|
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
|
-
|
357
|
-
redis
|
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
|
-
|
389
|
-
redis
|
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}" }.
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
ts.set(
|
40
|
-
assert_equal [
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
ts.
|
53
|
-
assert_equal [
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
ts.
|
65
|
-
ts.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
refute ts.matches(
|
71
|
-
refute ts.matches(
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
ts.
|
86
|
-
ts.
|
87
|
-
|
88
|
-
refute ts.matches(1,
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
assert ts.matches(
|
93
|
-
assert ts.matches(
|
94
|
-
|
95
|
-
assert ts.matches(2,
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
106
|
-
|
107
|
-
|
108
|
-
ts.
|
109
|
-
ts.
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
assert ts.matches(
|
116
|
-
assert ts.matches(
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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.
|
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:
|
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.
|
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
|