inferx 0.1.7 → 0.2.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.
@@ -46,7 +46,7 @@ class Inferx
46
46
  # Spawn an instance of any class.
47
47
  #
48
48
  # @param [Class] klass any class, constructor takes the instance of Redis to
49
- # first argument, and takes the namespace to last argument
49
+ # first argument, and takes the options to last argument
50
50
  # @param [Array] args any arguments
51
51
  # @return [Object] a instance of the class
52
52
  def spawn(klass, *args)
@@ -55,7 +55,7 @@ class Inferx
55
55
 
56
56
  protected
57
57
 
58
- %w(hdel hexists hget hincrby hkeys hsetnx).each do |command|
58
+ %w(hdel hget hgetall hincrby hkeys hsetnx).each do |command|
59
59
  define_method(command) do |*args|
60
60
  @redis.__send__(command, categories_key, *args)
61
61
  end
@@ -14,11 +14,12 @@ class Inferx
14
14
 
15
15
  # Get a category according the name.
16
16
  #
17
- # @param [Symbol] category_name category name
18
- # @return [Inferx::Category] category
17
+ # @param [Symbol] category_name a category name
18
+ # @return [Inferx::Category] a category
19
19
  def get(category_name)
20
- raise ArgumentError, "'#{category_name}' is missing" unless hexists(category_name)
21
- spawn(Category, category_name)
20
+ size = hget(category_name)
21
+ raise ArgumentError, "'#{category_name}' is missing" unless size
22
+ spawn(Category, category_name, size.to_i)
22
23
  end
23
24
  alias [] get
24
25
 
@@ -46,9 +47,11 @@ class Inferx
46
47
  # Apply process for each category.
47
48
  #
48
49
  # @yield a block to be called for every category
49
- # @yieldparam [Inferx::Category] category category
50
+ # @yieldparam [Inferx::Category] category a category
50
51
  def each
51
- all.each { |category_name| yield spawn(Category, category_name) }
52
+ hgetall.each do |category_name, size|
53
+ yield spawn(Category, category_name, size.to_i)
54
+ end
52
55
  end
53
56
  end
54
57
  end
@@ -5,36 +5,34 @@ class Inferx
5
5
 
6
6
  # @param [Redis] redis an instance of Redis
7
7
  # @param [Symbol] name a category name
8
+ # @param [Integer] size total of scores
8
9
  # @param [Hash] options
9
10
  # @option options [String] :namespace namespace of keys to be used to Redis
10
11
  # @option options [Boolean] :manual whether manual save, defaults to false
11
- def initialize(redis, name, options = {})
12
+ def initialize(redis, name, size, options = {})
12
13
  super(redis, options)
13
14
  @name = name
15
+ @size = size
14
16
  end
15
17
 
16
18
  # Get a category name.
17
19
  #
18
20
  # @attribute [r] name
19
21
  # @return [Symbol] a category name
20
- attr_reader :name
22
+
23
+ # Get total of scores.
24
+ #
25
+ # @attribute [r] size
26
+ # @return [Integer] total of scores
27
+ attr_reader :name, :size
21
28
 
22
29
  # Get words with scores in the category.
23
30
  #
24
- # @param [Hash] options
25
- # @option options [Integer] :score lower limit for getting by score
26
- # @option options [Integer] :rank upper limit for getting by rank
27
31
  # @return [Hash<String, Integer>] words with scores
28
- def all(options = {})
29
- words_with_scores = if score = options[:score]
30
- zrevrangebyscore('+inf', score, :withscores => true)
31
- else
32
- rank = options[:rank] || -1
33
- zrevrange(0, rank, :withscores => true)
34
- end
35
-
36
- size = words_with_scores.size
32
+ def all
33
+ words_with_scores = zrevrange(0, -1, :withscores => true)
37
34
  index = 1
35
+ size = words_with_scores.size
38
36
 
39
37
  while index < size
40
38
  words_with_scores[index] = words_with_scores[index].to_i
@@ -68,6 +66,7 @@ class Inferx
68
66
  if increase > 0
69
67
  hincrby(name, increase)
70
68
  @redis.save unless manual?
69
+ @size += increase
71
70
  end
72
71
  end
73
72
  end
@@ -95,43 +94,22 @@ class Inferx
95
94
  if decrease > 0
96
95
  hincrby(name, -decrease)
97
96
  @redis.save unless manual?
97
+ @size -= decrease
98
98
  end
99
99
  end
100
100
 
101
- # Get total of scores.
102
- #
103
- # @return [Integer] total of scores
104
- def size
105
- (hget(name) || 0).to_i
106
- end
107
-
108
101
  # Get effectively scores for each word.
109
102
  #
110
- # @param [Array<String>] words a set of words
111
- # @param [Hash<String, Integer>] words_with_scores words with scores
112
- # prepared in advance for reduce access to Redis
103
+ # @param [Array<String>] words an array of words
113
104
  # @return [Array<Integer>] scores for each word
114
- def scores(words, words_with_scores = {})
115
- scores = @redis.pipelined do
116
- words.each do |word|
117
- zscore(word) unless words_with_scores[word]
118
- end
119
- end
120
-
121
- index = 0
122
-
123
- next_score = lambda do
124
- score = scores[index]
125
- index += 1
126
- score ? score.to_i : nil
127
- end
128
-
129
- words.map { |word| words_with_scores[word] || next_score[] }
105
+ def scores(words)
106
+ scores = @redis.pipelined { words.map(&method(:zscore)) }
107
+ scores.map { |score| score ? score.to_i : nil }
130
108
  end
131
109
 
132
110
  private
133
111
 
134
- %w(zrevrange zrevrangebyscore zscore zincrby zremrangebyscore).each do |command|
112
+ %w(zrevrange zscore zincrby zremrangebyscore).each do |command|
135
113
  define_method(command) do |*args|
136
114
  @category_key ||= make_category_key(@name)
137
115
  @redis.__send__(command, @category_key, *args)
@@ -1,3 +1,3 @@
1
1
  class Inferx
2
- VERSION = '0.1.7'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/inferx.rb CHANGED
@@ -9,7 +9,7 @@ class Inferx
9
9
  # {https://github.com/redis/redis-rb redis}
10
10
  #
11
11
  # @option options [String] :namespace namespace of keys to be used to Redis
12
- # @option options [Boolean] :manual whether save manually, defaults to false
12
+ # @option options [Boolean] :manual whether manual save, defaults to false
13
13
  def initialize(options = {})
14
14
  @categories = Categories.new(Redis.new(options), options)
15
15
  end
@@ -24,9 +24,8 @@ class Inferx
24
24
  def score(category, words)
25
25
  size = category.size.to_f
26
26
  return -Float::INFINITY unless size > 0
27
- words_with_scores = category.all(:rank => 500)
28
- scores = category.scores(words, words_with_scores)
29
- scores.inject(0) { |s, score| s + Math.log((score || 0.1) / size) }
27
+ scores = category.scores(words)
28
+ scores.inject(0.0) { |s, score| s + Math.log((score || 0.1) / size) }
30
29
  end
31
30
 
32
31
  # Get a score for each category according to a set of words.
@@ -63,7 +63,7 @@ describe Inferx::Adapter, '#make_categories_key' do
63
63
  end
64
64
 
65
65
  describe Inferx::Adapter, '#make_category_key' do
66
- it 'returns the key for access to to scores stored each by word' do
66
+ it 'returns the key for access to scores stored each by word' do
67
67
  adapter = described_class.new(redis_stub)
68
68
  adapter.make_category_key(:red).should == 'inferx:categories:red'
69
69
  end
@@ -47,9 +47,9 @@ describe Inferx::Categories, '#all' do
47
47
  end
48
48
 
49
49
  describe Inferx::Categories, '#get' do
50
- it 'calls Redis#hexists' do
50
+ it 'calls Redis#hget' do
51
51
  redis = redis_stub do |s|
52
- s.should_receive(:hexists).with('inferx:categories', :red).and_return(true)
52
+ s.should_receive(:hget).with('inferx:categories', :red).and_return('2')
53
53
  end
54
54
 
55
55
  categories = described_class.new(redis)
@@ -58,17 +58,17 @@ describe Inferx::Categories, '#get' do
58
58
 
59
59
  it 'calles Inferx::Category.new with the instance of Redis, the category name and the options' do
60
60
  redis = redis_stub do |s|
61
- s.stub!(:hexists).and_return(true)
61
+ s.stub!(:hget).and_return('2')
62
62
  end
63
63
 
64
- Inferx::Category.should_receive(:new).with(redis, :red, :namespace => 'example', :manual => true)
64
+ Inferx::Category.should_receive(:new).with(redis, :red, 2, :namespace => 'example', :manual => true)
65
65
  categories = described_class.new(redis, :namespace => 'example', :manual => true)
66
66
  categories.get(:red)
67
67
  end
68
68
 
69
69
  it 'returns an instance of Inferx::Category' do
70
70
  redis = redis_stub do |s|
71
- s.stub!(:hexists).and_return(true)
71
+ s.stub!(:hget).and_return('2')
72
72
  end
73
73
 
74
74
  categories = described_class.new(redis)
@@ -78,7 +78,7 @@ describe Inferx::Categories, '#get' do
78
78
  context 'with a missing category' do
79
79
  it 'raises ArgumentError' do
80
80
  redis = redis_stub do |s|
81
- s.stub!(:hexists).and_return(false)
81
+ s.stub!(:hget).and_return(nil)
82
82
  end
83
83
 
84
84
  categories = described_class.new(redis)
@@ -163,7 +163,7 @@ end
163
163
  describe Inferx::Categories, '#each' do
164
164
  before do
165
165
  @redis = redis_stub do |s|
166
- s.stub!(:hkeys).and_return(%w(red green blue))
166
+ s.stub!(:hgetall).and_return(%w(red green blue))
167
167
  end
168
168
  end
169
169
 
@@ -4,25 +4,30 @@ require 'inferx/category'
4
4
  describe Inferx::Category, '#initialize' do
5
5
  it 'calls Inferx::Adapter#initialize' do
6
6
  redis = redis_stub
7
- category = described_class.new(redis, :red, :namespace => 'example', :manual => true)
7
+ category = described_class.new(redis, :red, 2, :namespace => 'example', :manual => true)
8
8
  category.instance_eval { @redis }.should == redis
9
9
  category.instance_eval { @namespace }.should == 'example'
10
10
  category.should be_manual
11
11
  end
12
12
 
13
13
  it 'sets the category name to the name attribute' do
14
- category = described_class.new(redis_stub, :red)
14
+ category = described_class.new(redis_stub, :red, 2)
15
15
  category.name.should == :red
16
16
  end
17
+
18
+ it 'sets the size to the size attribute' do
19
+ category = described_class.new(redis_stub, :red, 2)
20
+ category.size.should == 2
21
+ end
17
22
  end
18
23
 
19
24
  describe Inferx::Category, '#all' do
20
- it 'calls Redis#revrange' do
25
+ it 'calls Redis#zrevrange' do
21
26
  redis = redis_stub do |s|
22
27
  s.should_receive(:zrevrange).with('inferx:categories:red', 0, -1, :withscores => true).and_return([])
23
28
  end
24
29
 
25
- category = described_class.new(redis, :red)
30
+ category = described_class.new(redis, :red, 2)
26
31
  category.all
27
32
  end
28
33
 
@@ -31,31 +36,9 @@ describe Inferx::Category, '#all' do
31
36
  s.stub!(:zrevrange).with('inferx:categories:red', 0, -1, :withscores => true).and_return(%w(apple 2 strawberry 3))
32
37
  end
33
38
 
34
- category = described_class.new(redis, :red)
39
+ category = described_class.new(redis, :red, 2)
35
40
  category.all.should == {'apple' => 2, 'strawberry' => 3}
36
41
  end
37
-
38
- context 'with the rank option' do
39
- it 'calls Redis#zrevrange' do
40
- redis = redis_stub do |s|
41
- s.should_receive(:zrevrange).with('inferx:categories:red', 0, 1000, :withscores => true).and_return([])
42
- end
43
-
44
- category = described_class.new(redis, :red)
45
- category.all(:rank => 1000)
46
- end
47
- end
48
-
49
- context 'with the score option' do
50
- it 'calls Redis#zrevrangebyscore' do
51
- redis = redis_stub do |s|
52
- s.should_receive(:zrevrangebyscore).with('inferx:categories:red', '+inf', 2, :withscores => true).and_return([])
53
- end
54
-
55
- category = described_class.new(redis, :red)
56
- category.all(:score => 2)
57
- end
58
- end
59
42
  end
60
43
 
61
44
  describe Inferx::Category, '#get' do
@@ -64,7 +47,7 @@ describe Inferx::Category, '#get' do
64
47
  s.should_receive(:zscore).with('inferx:categories:red', 'apple')
65
48
  end
66
49
 
67
- category = described_class.new(redis, :red)
50
+ category = described_class.new(redis, :red, 2)
68
51
  category.get('apple')
69
52
  end
70
53
 
@@ -73,7 +56,7 @@ describe Inferx::Category, '#get' do
73
56
  s.stub!(:zscore).with('inferx:categories:red', 'apple').and_return('1')
74
57
  end
75
58
 
76
- category = described_class.new(redis, :red)
59
+ category = described_class.new(redis, :red, 2)
77
60
  category.get('apple').should == 1
78
61
  end
79
62
 
@@ -83,7 +66,7 @@ describe Inferx::Category, '#get' do
83
66
  s.stub!(:zscore).with('inferx:categories:red', 'strawberry').and_return(nil)
84
67
  end
85
68
 
86
- category = described_class.new(redis, :red)
69
+ category = described_class.new(redis, :red, 2)
87
70
  category.get('strawberry').should be_nil
88
71
  end
89
72
  end
@@ -98,8 +81,19 @@ describe Inferx::Category, '#train' do
98
81
  s.should_receive(:save)
99
82
  end
100
83
 
101
- category = described_class.new(redis, :red)
84
+ category = described_class.new(redis, :red, 2)
85
+ category.train(%w(apple strawberry apple strawberry strawberry))
86
+ end
87
+
88
+ it 'increases the size attribute' do
89
+ redis = redis_stub do |s|
90
+ s.stub!(:zincrby)
91
+ s.stub!(:hincrby)
92
+ end
93
+
94
+ category = described_class.new(redis, :red, 2)
102
95
  category.train(%w(apple strawberry apple strawberry strawberry))
96
+ category.size.should == 7
103
97
  end
104
98
 
105
99
  context 'with no update' do
@@ -109,7 +103,7 @@ describe Inferx::Category, '#train' do
109
103
  s.should_not_receive(:save)
110
104
  end
111
105
 
112
- category = described_class.new(redis, :red)
106
+ category = described_class.new(redis, :red, 2)
113
107
  category.train(%w())
114
108
  end
115
109
  end
@@ -122,7 +116,7 @@ describe Inferx::Category, '#train' do
122
116
  s.should_not_receive(:save)
123
117
  end
124
118
 
125
- category = described_class.new(redis, :red, :manual => true)
119
+ category = described_class.new(redis, :red, 2, :manual => true)
126
120
  category.train(%w(apple strawberry apple strawberry strawberry))
127
121
  end
128
122
  end
@@ -138,10 +132,22 @@ describe Inferx::Category, '#untrain' do
138
132
  s.should_receive(:save)
139
133
  end
140
134
 
141
- category = described_class.new(redis, :red)
135
+ category = described_class.new(redis, :red, 7)
142
136
  category.untrain(%w(apple strawberry apple strawberry strawberry))
143
137
  end
144
138
 
139
+ it 'decreases the size attribute' do
140
+ redis = redis_stub do |s|
141
+ s.stub!(:zincrby)
142
+ s.stub!(:zremrangebyscore).and_return(%w(3 -2 1))
143
+ s.stub!(:hincrby)
144
+ end
145
+
146
+ category = described_class.new(redis, :red, 7)
147
+ category.untrain(%w(apple strawberry apple strawberry strawberry))
148
+ category.size.should == 4
149
+ end
150
+
145
151
  context 'with no update' do
146
152
  it 'does not call Redis#hincrby' do
147
153
  redis = redis_stub do |s|
@@ -151,7 +157,7 @@ describe Inferx::Category, '#untrain' do
151
157
  s.should_not_receive(:save)
152
158
  end
153
159
 
154
- category = described_class.new(redis, :red)
160
+ category = described_class.new(redis, :red, 7)
155
161
  category.untrain(%w(apple strawberry apple strawberry strawberry))
156
162
  end
157
163
  end
@@ -165,43 +171,12 @@ describe Inferx::Category, '#untrain' do
165
171
  s.should_not_receive(:save)
166
172
  end
167
173
 
168
- category = described_class.new(redis, :red, :manual => true)
174
+ category = described_class.new(redis, :red, 7, :manual => true)
169
175
  category.untrain(%w(apple strawberry apple strawberry strawberry))
170
176
  end
171
177
  end
172
178
  end
173
179
 
174
- describe Inferx::Category, '#size' do
175
- it 'calls Redis#hget' do
176
- redis = redis_stub do |s|
177
- s.should_receive(:hget).with('inferx:categories', :red)
178
- end
179
-
180
- category = described_class.new(redis, :red)
181
- category.size
182
- end
183
-
184
- it 'returns total of the score of the words as Integer' do
185
- redis = redis_stub do |s|
186
- s.stub!(:hget).and_return('1')
187
- end
188
-
189
- category = described_class.new(redis, :red)
190
- category.size.should == 1
191
- end
192
-
193
- context 'with the missing key' do
194
- it 'returns 0' do
195
- redis = redis_stub do |s|
196
- s.stub!(:hget).and_return(nil)
197
- end
198
-
199
- category = described_class.new(redis, :red)
200
- category.size.should == 0
201
- end
202
- end
203
- end
204
-
205
180
  describe Inferx::Category, '#scores' do
206
181
  it 'calls Redis#zscore' do
207
182
  redis = redis_stub do |s|
@@ -209,7 +184,7 @@ describe Inferx::Category, '#scores' do
209
184
  s.should_receive(:zscore).with('inferx:categories:red', 'strawberry')
210
185
  end
211
186
 
212
- category = described_class.new(redis, :red)
187
+ category = described_class.new(redis, :red, 2)
213
188
  category.scores(%w(apple strawberry))
214
189
  end
215
190
 
@@ -218,21 +193,8 @@ describe Inferx::Category, '#scores' do
218
193
  s.stub!(:pipelined).and_return(%w(2 3))
219
194
  end
220
195
 
221
- category = described_class.new(redis, :red)
196
+ category = described_class.new(redis, :red, 2)
222
197
  scores = category.scores(%w(apple strawberry))
223
198
  scores.should == [2, 3]
224
199
  end
225
-
226
- context 'with words with scores' do
227
- it 'returns the scores to use the cache' do
228
- redis = redis_stub do |s|
229
- s.should_not_receive(:zscore).with('inferx:categories:red', 'strawberry')
230
- s.stub!(:pipelined).and_return { |&block| block.call; [2] }
231
- end
232
-
233
- category = described_class.new(redis, :red)
234
- scores = category.scores(%w(apple strawberry), 'strawberry' => 3, 'hoge' => 1)
235
- scores.should == [2, 3]
236
- end
237
- end
238
200
  end
data/spec/inferx_spec.rb CHANGED
@@ -12,7 +12,7 @@ describe Inferx do
12
12
  end
13
13
 
14
14
  describe Inferx, '#initialize' do
15
- it "calls #{described_class}::Categories.new with a connection of Redis and the options" do
15
+ it "calls #{described_class}::Categories.new with an instance of Redis and the options" do
16
16
  redis = redis_stub
17
17
  Inferx::Categories.should_receive(:new).with(redis, :namespace => 'example', :manual => true)
18
18
  described_class.new(:namespace => 'example', :manual => true)
@@ -30,11 +30,10 @@ describe Inferx, '#score' do
30
30
  @inferx = described_class.new
31
31
  end
32
32
 
33
- it "calls #{described_class}::Categories#size, #{described_class}::Category#all and #{described_class}::Category#scores" do
33
+ it "calls #{described_class}::Category#size and #{described_class}::Category#scores" do
34
34
  category = mock.tap do |m|
35
35
  m.should_receive(:size).and_return(5)
36
- m.should_receive(:all).with(:rank => 500).and_return('apple' => 2)
37
- m.should_receive(:scores).with(%w(apple), 'apple' => 2).and_return([2])
36
+ m.should_receive(:scores).with(%w(apple)).and_return([2])
38
37
  end
39
38
 
40
39
  @inferx.score(category, %w(apple))
@@ -51,7 +50,6 @@ describe Inferx, '#score' do
51
50
  category = stub.tap do |s|
52
51
  s.stub!(:size).and_return(size)
53
52
  s.stub!(:scores).and_return(scores)
54
- s.stub!(:all)
55
53
  end
56
54
 
57
55
  @inferx.score(category, words).should == expected
@@ -67,6 +65,17 @@ describe Inferx, '#score' do
67
65
  score.should be_infinite
68
66
  score.should < 0
69
67
  end
68
+
69
+ it 'returns 0.0 if the words are empty' do
70
+ category = stub.tap do |s|
71
+ s.stub!(:size).and_return(2)
72
+ s.stub!(:scores).and_return([])
73
+ end
74
+
75
+ score = @inferx.score(category, [])
76
+ score.should be_a(Float)
77
+ score.should be_zero
78
+ end
70
79
  end
71
80
 
72
81
  describe Inferx, '#classifications' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inferx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-16 00:00:00.000000000 Z
12
+ date: 2012-04-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis