qfill 0.0.1

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.
@@ -0,0 +1,85 @@
1
+ #pusher = Qfill::Pusher.new(
2
+ # Qfill::Result.new( :name => "Best Results",
3
+ # :filter => filter3,
4
+ # :ratio => 0.5,
5
+ # :list_ratios => {
6
+ # "High List" => 0.4,
7
+ # "Medium List" => 0.2,
8
+ # "Low List" => 0.4
9
+ # }
10
+ # ),
11
+ # Qfill::Result.new( :name => "More Results",
12
+ # :ratio => 0.5,
13
+ # :list_ratios => {
14
+ # "High List" => 0.2,
15
+ # "Medium List" => 0.4,
16
+ # "Low List" => 0.4
17
+ # }
18
+ # )
19
+ #)
20
+ #
21
+ #pusher = Qfill::Pusher.from_array_of_hashes([
22
+ # { :name => "First Result",
23
+ # :ratio => 0.125,
24
+ # :filter => filter3,
25
+ # :list_ratios => {
26
+ # "High List" => 0.4,
27
+ # "Medium List" => 0.2,
28
+ # "Low List" => 0.4
29
+ # }
30
+ # },
31
+ # { :name => "Second Result",
32
+ # :ratio => 0.25 },
33
+ # { :name => "Third Result",
34
+ # :ratio => 0.125 },
35
+ # { :name => "Fourth Result",
36
+ # :ratio => 0.50 },
37
+ #])
38
+ #
39
+ # Pusher is made up of an array (called queues) of Result objects.
40
+ module Qfill
41
+ class Pusher < Qfill::ListSet
42
+
43
+ def initialize(*args)
44
+ super(*args)
45
+ with_ratio = self.queues.map {|x| x.ratio}.compact
46
+ ratio_to_split = (1 - with_ratio.inject(0, :+))
47
+ if ratio_to_split < 0
48
+ raise ArgumentError, "#{self.class}: mismatched ratios for queues #{with_ratio.join(' + ')} must not total more than 1"
49
+ end
50
+ num_without_ratio = self.queues.length - with_ratio.length
51
+ if num_without_ratio > 0 && ratio_to_split < 1
52
+ equal_portion = ratio_to_split / num_without_ratio
53
+ self.queues.each do |queue|
54
+ if queue.ratio.nil?
55
+ queue.tap do |q|
56
+ q.ratio = equal_portion
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ def current_list
64
+ self.queues[self.current_index]
65
+ end
66
+
67
+ def set_next_as_current!
68
+ next_index = self.current_index + 1
69
+ if (next_index) == self.queues.length
70
+ # If we have iterated through all the queues, then we reset
71
+ self.reset!
72
+ else
73
+ self.current_index = next_index
74
+ end
75
+ end
76
+
77
+ def self.from_array_of_hashes(array_of_hashes = [])
78
+ args = array_of_hashes.map do |hash|
79
+ Qfill::Result.new(hash)
80
+ end
81
+ Qfill::Pusher.new(*args)
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,116 @@
1
+ # Qfill::Result.new(:name => "Best Results",
2
+ # :filter => filter3,
3
+ # :ratio => 0.5,
4
+ # :list_ratios => {
5
+ # "High List" => 0.4,
6
+ # "Medium List" => 0.2,
7
+ # "Low List" => 0.4 } )
8
+ module Qfill
9
+ class Result < Qfill::List
10
+ attr_accessor :ratio, :list_ratios, :fill_tracker, :fill_count, :validate, :filter, :current_list_ratio_index, :max
11
+
12
+ def self.get_limit_from_max_and_ratio(all_list_max, ratio)
13
+ limit = (all_list_max * ratio).round(0)
14
+ # If we rounded down to zero we have to keep at least one.
15
+ # This is because with small origin sets all ratios might round down to 0.
16
+ if limit == 0
17
+ limit += 1
18
+ end
19
+ limit
20
+ end
21
+
22
+ def initialize(options = {})
23
+ super(options)
24
+ @list_ratios = options[:list_ratios] || {}
25
+ with_ratio = self.list_ratio_as_array.map {|tuple| tuple[1]}.compact
26
+ ratio_leftover = (1 - with_ratio.inject(0, :+))
27
+ if ratio_leftover < 0
28
+ raise ArgumentError, "#{self.class}: invalid list_ratios for queue '#{self.name}'. List Ratios (#{with_ratio.join(' + ')}) must not total more than 1"
29
+ end
30
+ @ratio = options[:ratio]
31
+ @max = 0
32
+ @fill_tracker = {}
33
+ @fill_count = 0
34
+ @current_list_ratio_index = 0 # Used by :sample strategy
35
+ @validate = self.use_validation?
36
+ end
37
+
38
+ def list_ratio_full?(list_name, max_from_list)
39
+ self.fill_tracker[list_name] >= max_from_list
40
+ end
41
+
42
+ def push(objects, list_name)
43
+ self.validate!(list_name)
44
+ added = 0
45
+ objects.each do |object|
46
+ if self.allow?(object, list_name)
47
+ self.bump_fill_tracker!(list_name)
48
+ self.add!(object)
49
+ added += 1
50
+ #self.print(list_name)
51
+ end
52
+ end
53
+ return added
54
+ end
55
+
56
+ def print(list_name)
57
+ puts "Added to #{list_name}.\nResult List #{self.name} now has #{self.elements.length} total objects.\nSources:\n #{self.fill_tracker.inspect} "
58
+ end
59
+
60
+ def add!(object)
61
+ self.elements << object
62
+ end
63
+
64
+ def allow?(object, list_name)
65
+ !self.filter.respond_to?(:call) ||
66
+ # If there is a filter, then it must return true to proceed
67
+ self.filter.run(object, list_name)
68
+ end
69
+
70
+ def bump_fill_tracker!(list_name)
71
+ self.fill_tracker[list_name] ||= 0
72
+ self.fill_tracker[list_name] += 1
73
+ self.fill_count += 1
74
+ end
75
+
76
+ # Does the queue being pushed into match one of the list_ratios
77
+ def valid?(list_name)
78
+ self.list_ratios.has_key?(list_name)
79
+ end
80
+
81
+ def validate!(list_name)
82
+ raise ArgumentError, "#{self.class}: #{list_name} is an invalid list_name. Valid list_names are: #{self.list_ratios.keys}" if self.validate && !self.valid?(list_name)
83
+ end
84
+
85
+ def use_validation?
86
+ !self.list_ratios.empty?
87
+ end
88
+
89
+ def list_ratio_as_array
90
+ # [["high",0.4],["medium",0.4],["low",0.2]]
91
+ @list_ratio_as_array ||= self.list_ratios.to_a
92
+ end
93
+
94
+ def current_list_ratio
95
+ self.list_ratio_as_array[self.current_list_ratio_index]
96
+ end
97
+
98
+ def set_next_as_current!
99
+ next_index = self.current_list_ratio_index + 1
100
+ if (next_index) == self.list_ratio_as_array.length
101
+ # If we have iterated through all the list_ratios, then we reset
102
+ self.reset!
103
+ else
104
+ self.current_list_ratio_index = next_index
105
+ end
106
+ end
107
+
108
+ def reset!
109
+ self.current_list_ratio_index = 0
110
+ end
111
+
112
+ def is_full?
113
+ self.elements.length >= self.max
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,3 @@
1
+ module Qfill
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'qfill/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "qfill"
8
+ gem.version = Qfill::VERSION
9
+ gem.authors = ["Peter Boling"]
10
+ gem.email = ["peter.boling@gmail.com"]
11
+ gem.description = %q{Advanced Queue Transformation}
12
+ gem.summary = %q{You have a set of arrays that need to be turned into a different set of arrays
13
+ according to a potentially non-uniform set of rules.
14
+
15
+ Now you can easily turn this:
16
+
17
+ source_a # => [1,2,3,4]
18
+ source_b # => [5,6,7,8,9]
19
+
20
+ into this:
21
+
22
+ result_a # => [1,2]
23
+ result_b # => [3,5,7,9]
24
+ result_c # => [4,6,8]
25
+
26
+ by specifying filters for handling each transformation.
27
+ }
28
+ gem.homepage = ""
29
+
30
+ gem.files = `git ls-files`.split($/)
31
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
32
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
33
+ gem.require_paths = ["lib"]
34
+
35
+ gem.add_development_dependency 'rspec'
36
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ describe Qfill::Filter do
3
+ context "#new" do
4
+ context "with processor" do
5
+ before :each do
6
+ @lambda = -> (object) { !object.nil? }
7
+ end
8
+ it "should instantiate with processor" do
9
+ Qfill::Filter.new(@lambda).should be_a(Qfill::Filter)
10
+ end
11
+ end
12
+
13
+ context "with processor and arguments" do
14
+ before :each do
15
+ @lambda = -> (object, first, second) { !object.nil? && first == first && second == 'second' }
16
+ @arguments = ['first','second']
17
+ end
18
+ it "should instantiate with processor" do
19
+ Qfill::Filter.new(@lambda, *@arguments).should be_a(Qfill::Filter)
20
+ end
21
+ end
22
+ end
23
+
24
+ context "#run" do
25
+ before :each do
26
+ @lambda = -> (object, first, second) { !object.nil? && first == first && second == 'second' }
27
+ @arguments = ['first','second']
28
+ @filter = Qfill::Filter.new(@lambda, *@arguments)
29
+ end
30
+ it "should return the correct result" do
31
+ @filter.run('not nil').should == true
32
+ end
33
+ context "with extra arguments" do
34
+ before :each do
35
+ @lambda = -> (object, special_arg1, special_arg2, first, second, third) { !object.nil? && first == first && second == 'second' && special_arg1 = 'this' && special_arg2 == 'thing' && third == 'third' }
36
+ @arguments = ['first','second','third']
37
+ @filter = Qfill::Filter.new(@lambda, *@arguments)
38
+ end
39
+ it "should properly use arity" do
40
+ @filter.run('not nil', 'this', 'thing').should == true
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ describe Qfill::ListSet do
3
+ context "#new" do
4
+ context "with no arguments" do
5
+ it "should raise ArgumentError" do
6
+ expect { Qfill::ListSet.new() }.to raise_error(ArgumentError)
7
+ end
8
+ end
9
+ context "with arguments" do
10
+ before :each do
11
+ @filter = Qfill::Filter.new( -> (object) { object.is_a?(Numeric)} )
12
+ @origin_queues = [
13
+ Qfill::List.new(
14
+ :name => "High List",
15
+ :elements => [1, 2, 3, 'c'],
16
+ :filter => @filter),
17
+ Qfill::List.new( :name => "Medium List",
18
+ :elements => ['e', 'f', 4, 5],
19
+ :filter => @filter),
20
+ Qfill::List.new( :name => "Low List",
21
+ :elements => [7, 8, 'd'],
22
+ :filter => @filter)
23
+ ]
24
+ end
25
+ it "should not raise any errors" do
26
+ expect { Qfill::ListSet.new(*@origin_queues) }.to_not raise_error
27
+ end
28
+ it "should instantiate with name" do
29
+ popper = Qfill::ListSet.new(*@origin_queues)
30
+ popper.queues.first.elements.should == [1,2,3,'c']
31
+ popper.queues.last.elements.should == [7,8,'d']
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+ describe Qfill::List do
3
+ context "#new" do
4
+ context "with no arguments" do
5
+ it "should raise ArgumentError" do
6
+ expect { Qfill::List.new() }.to raise_error(ArgumentError)
7
+ end
8
+ end
9
+ context "with name" do
10
+ before :each do
11
+ @arguments = { :name => "High List" }
12
+ end
13
+ it "should not raise any errors" do
14
+ expect { Qfill::List.new(@arguments) }.to_not raise_error
15
+ end
16
+ it "should instantiate with name" do
17
+ Qfill::List.new(@arguments).name.should == 'High List'
18
+ end
19
+ end
20
+ context "with elements" do
21
+ before :each do
22
+ @arguments = { :name => "High List",
23
+ :elements => [1,2] }
24
+ end
25
+ it "should instantiate with elements" do
26
+ Qfill::List.new(@arguments).elements.should == [1,2]
27
+ end
28
+ end
29
+ context "with filter" do
30
+ before :each do
31
+ lambda = -> (object) { !object.nil? }
32
+ @filter = Qfill::Filter.new(lambda)
33
+ @arguments = { :name => "High List",
34
+ :elements => [1, 2],
35
+ :filter => @filter }
36
+ end
37
+ it "should instantiate with processor" do
38
+ Qfill::List.new(@arguments).filter.should be_a(Qfill::Filter)
39
+ Qfill::List.new(@arguments).filter.should == @filter
40
+ end
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,418 @@
1
+ require 'spec_helper'
2
+ describe Qfill::Manager do
3
+ context "#new" do
4
+ context "with no arguments" do
5
+ it "should raise ArgumentError" do
6
+ expect { Qfill::Manager.new() }.to raise_error(ArgumentError)
7
+ end
8
+ end
9
+ context "with only popper" do
10
+ it "should raise ArgumentError" do
11
+ popper = Qfill::Popper.from_array_of_hashes(
12
+ [{ :name => "High List",
13
+ :elements => [1,2,3]}] )
14
+ expect { Qfill::Manager.new(:popper => popper) }.to raise_error(ArgumentError)
15
+ end
16
+ end
17
+ context "with only pusher" do
18
+ it "should raise ArgumentError" do
19
+ pusher = Qfill::Pusher.from_array_of_hashes(
20
+ [{ :name => "Some Result",
21
+ :ratio => 0.25 }] )
22
+ expect { Qfill::Manager.new(:pusher => pusher) }.to raise_error(ArgumentError)
23
+ end
24
+ end
25
+ context "with popper and pusher" do
26
+ before :each do
27
+ @popper = Qfill::Popper.from_array_of_hashes(
28
+ [{ :name => "High List",
29
+ :elements => [1,2,3]}] )
30
+ @pusher = Qfill::Pusher.from_array_of_hashes(
31
+ [{ :name => "Some Result",
32
+ :ratio => 0.25 }] )
33
+ @arguments = {
34
+ :pusher => @pusher,
35
+ :popper => @popper
36
+ }
37
+ end
38
+ it "should not raise any errors" do
39
+ expect { Qfill::Manager.new(@arguments) }.to_not raise_error
40
+ end
41
+ it "should instantiate with pusher" do
42
+ Qfill::Manager.new(@arguments).pusher.should == @pusher
43
+ end
44
+ it "should instantiate with popper" do
45
+ Qfill::Manager.new(@arguments).popper.should == @popper
46
+ end
47
+ end
48
+ context "with popper and pusher and all_list_max smaller than # total elements" do
49
+ before :each do
50
+ @popper = Qfill::Popper.from_array_of_hashes(
51
+ [{ :name => "High List",
52
+ :elements => [1,2,3]}] )
53
+ @pusher = Qfill::Pusher.from_array_of_hashes(
54
+ [{ :name => "Some Result",
55
+ :ratio => 0.25 }] )
56
+ @arguments = {
57
+ :pusher => @pusher,
58
+ :popper => @popper,
59
+ :all_list_max => 2
60
+ }
61
+ end
62
+ it "should not raise any errors" do
63
+ expect { Qfill::Manager.new(@arguments) }.to_not raise_error
64
+ end
65
+ it "should instantiate with pusher" do
66
+ Qfill::Manager.new(@arguments).pusher.should == @pusher
67
+ end
68
+ it "should instantiate with popper" do
69
+ Qfill::Manager.new(@arguments).popper.should == @popper
70
+ end
71
+ it "should instantiate with all_list_max" do
72
+ Qfill::Manager.new(@arguments).all_list_max.should == 2
73
+ end
74
+ end
75
+ context "all_list_max greater than # total elements" do
76
+ before :each do
77
+ @popper = Qfill::Popper.from_array_of_hashes(
78
+ [{ :name => "High List",
79
+ :elements => [1,2,3]}] )
80
+ @pusher = Qfill::Pusher.from_array_of_hashes(
81
+ [{ :name => "Some Result",
82
+ :ratio => 0.25 }] )
83
+ @arguments = {
84
+ :pusher => @pusher,
85
+ :popper => @popper,
86
+ :all_list_max => 666
87
+ }
88
+ end
89
+ it "should instantiate with all_list_max" do
90
+ Qfill::Manager.new(@arguments).all_list_max.should == 3
91
+ end
92
+ end
93
+ end
94
+ context "strategy => :sample" do
95
+ context "backfill => false" do
96
+ before :each do
97
+ @popper = Qfill::Popper.from_array_of_hashes(
98
+ # We will create 4 queues, high, medium, low, and none.
99
+ # These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
100
+ # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
101
+ # but not at the expense of hte experience, would be:
102
+ # high => medium => none => low
103
+ [{:name => 'high',
104
+ :elements => %w( h1 h2 h3 h4 h5 h6 h7 h8 h9 ),
105
+ :backfill => 'medium'},
106
+ {:name => "medium",
107
+ :elements => %w( m1 m2 m3 m4 m5 m6 m7 m8 m9 ),
108
+ :backfill => 'none'},
109
+ {:name => 'low',
110
+ :elements => %w( l1 l2 l3 l4 l5 l6 l7 l8 l9 ),
111
+ :backfill => false},
112
+ {:name => 'none',
113
+ :elements => %w( n1 n2 n3 n4 n5 n6 n7 n8 n9 ),
114
+ :backfill => 'low' }] )
115
+ @pusher = Qfill::Pusher.from_array_of_hashes(
116
+ [{ :name => "first",
117
+ :list_ratios => {
118
+ 'high' => 0.5,
119
+ 'medium' => 0.1,
120
+ 'none' => 0.4
121
+ },
122
+ :ratio => 0.25 },
123
+ { :name => "second",
124
+ :ratio => 0.50 },
125
+ { :name => "third",
126
+ :ratio => 0.25 }] )
127
+ @arguments = {
128
+ :pusher => @pusher,
129
+ :popper => @popper,
130
+ :all_list_max => 40,
131
+ :strategy => :sample
132
+ }
133
+ end
134
+ context "#new" do
135
+ it "should not raise any errors" do
136
+ expect { Qfill::Manager.new(@arguments) }.to_not raise_error
137
+ end
138
+ end
139
+ context "#fill!" do
140
+ it "should instantiate with pusher" do
141
+ expect { Qfill::Manager.new(@arguments).fill! }.to_not raise_error
142
+ end
143
+ end
144
+ context "results" do
145
+ before(:each) do
146
+ @manager = Qfill::Manager.new(@arguments)
147
+ end
148
+ context "before fill!" do
149
+ it "should calculate the correct popper total elements" do
150
+ @manager.popper.get_total_elements.should == 36
151
+ end
152
+ it "should calculate the correct popper primary elements" do
153
+ @manager.popper.get_primary_elements == 36
154
+ end
155
+ it "should calculate the correct pusher total elements" do
156
+ @manager.pusher.get_total_elements.should == 0
157
+ end
158
+ end
159
+ context "after fill!" do
160
+ before(:each) do
161
+ @manager.fill!
162
+ end
163
+ it "should calculate the correct popper total elements" do
164
+ @manager.popper.get_total_elements.should == 0
165
+ end
166
+ it "should calculate the correct popper primary elements" do
167
+ @manager.popper.get_primary_elements == 0
168
+ end
169
+ it "should calculate the correct pusher total elements" do
170
+ @manager.pusher.get_total_elements.should == 36
171
+ end
172
+ end
173
+ end
174
+ end
175
+ context "backfill => true" do
176
+ before :each do
177
+ @popper = Qfill::Popper.from_array_of_hashes(
178
+ # We will create 4 queues, high, medium, low, and none.
179
+ # These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
180
+ # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
181
+ # but not at the expense of hte experience, would be:
182
+ # high => medium => none => low
183
+ [{:name => 'high',
184
+ :elements => %w( h1 h2 h3 h4 h5 h6 h7 h8 h9 ),
185
+ :backfill => 'medium'},
186
+ {:name => "medium",
187
+ :elements => %w( m1 m2 m3 m4 m5 m6 m7 m8 m9 ),
188
+ :backfill => 'none'},
189
+ {:name => 'low',
190
+ :elements => %w( l1 l2 l3 l4 l5 l6 l7 l8 l9 ),
191
+ :backfill => true},
192
+ {:name => 'none',
193
+ :elements => %w( n1 n2 n3 n4 n5 n6 n7 n8 n9 ),
194
+ :backfill => 'low' }] )
195
+ @pusher = Qfill::Pusher.from_array_of_hashes(
196
+ [{ :name => "first",
197
+ :list_ratios => {
198
+ 'high' => 0.5,
199
+ 'medium' => 0.1,
200
+ 'none' => 0.4
201
+ },
202
+ :ratio => 0.25 },
203
+ { :name => "second",
204
+ :ratio => 0.50 },
205
+ { :name => "third",
206
+ :ratio => 0.25 }] )
207
+ @arguments = {
208
+ :pusher => @pusher,
209
+ :popper => @popper,
210
+ :all_list_max => 40,
211
+ :strategy => :sample
212
+ }
213
+ end
214
+ context "#new" do
215
+ it "should not raise any errors" do
216
+ expect { Qfill::Manager.new(@arguments) }.to_not raise_error
217
+ end
218
+ end
219
+ context "#fill!" do
220
+ it "should instantiate with pusher" do
221
+ expect { Qfill::Manager.new(@arguments).fill! }.to_not raise_error
222
+ end
223
+ end
224
+ context "results" do
225
+ before(:each) do
226
+ @manager = Qfill::Manager.new(@arguments)
227
+ end
228
+ context "before fill!" do
229
+ it "should calculate the correct popper total elements" do
230
+ @manager.popper.get_total_elements.should == 36
231
+ end
232
+ it "should calculate the correct popper primary elements" do
233
+ @manager.popper.get_primary_elements == 27
234
+ end
235
+ it "should calculate the correct pusher total elements" do
236
+ @manager.pusher.get_total_elements.should == 0
237
+ end
238
+ end
239
+ context "after fill!" do
240
+ before(:each) do
241
+ @manager.fill!
242
+ end
243
+ it "should calculate the correct popper total elements" do
244
+ @manager.popper.get_total_elements.should == 9
245
+ end
246
+ it "should calculate the correct popper primary elements" do
247
+ @manager.popper.get_primary_elements == 0
248
+ end
249
+ it "should calculate the correct pusher total elements" do
250
+ @manager.pusher.get_total_elements.should == 27
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ context "strategy :drain" do
257
+ context "backfill => false" do
258
+ before :each do
259
+ @popper = Qfill::Popper.from_array_of_hashes(
260
+ # We will create 4 queues, high, medium, low, and none.
261
+ # These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
262
+ # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
263
+ # but not at the expense of hte experience, would be:
264
+ # high => medium => none => low
265
+ [{:name => 'high',
266
+ :elements => %w( h1 h2 h3 h4 h5 h6 h7 h8 h9 ),
267
+ :backfill => 'medium'},
268
+ {:name => "medium",
269
+ :elements => %w( m1 m2 m3 m4 m5 m6 m7 m8 m9 ),
270
+ :backfill => 'none'},
271
+ {:name => 'low',
272
+ :elements => %w( l1 l2 l3 l4 l5 l6 l7 l8 l9 ),
273
+ :backfill => false},
274
+ {:name => 'none',
275
+ :elements => %w( n1 n2 n3 n4 n5 n6 n7 n8 n9 ),
276
+ :backfill => 'low' }] )
277
+ @pusher = Qfill::Pusher.from_array_of_hashes(
278
+ [{ :name => "first",
279
+ :list_ratios => {
280
+ 'high' => 0.5,
281
+ 'medium' => 0.1,
282
+ 'none' => 0.4
283
+ },
284
+ :ratio => 0.25 },
285
+ { :name => "second",
286
+ :ratio => 0.50 },
287
+ { :name => "third",
288
+ :ratio => 0.25 }] )
289
+ @arguments = {
290
+ :pusher => @pusher,
291
+ :popper => @popper,
292
+ :all_list_max => 40,
293
+ :strategy => :drain
294
+ }
295
+ end
296
+ context "#new" do
297
+ it "should not raise any errors" do
298
+ expect { Qfill::Manager.new(@arguments) }.to_not raise_error
299
+ end
300
+ end
301
+ context "#fill!" do
302
+ it "should instantiate with pusher" do
303
+ expect { Qfill::Manager.new(@arguments).fill! }.to_not raise_error
304
+ end
305
+ end
306
+ context "results" do
307
+ before(:each) do
308
+ @manager = Qfill::Manager.new(@arguments)
309
+ end
310
+ context "before fill!" do
311
+ it "should calculate the correct popper total elements" do
312
+ @manager.popper.get_total_elements.should == 36
313
+ end
314
+ it "should calculate the correct popper primary elements" do
315
+ @manager.popper.get_primary_elements == 36
316
+ end
317
+ it "should calculate the correct pusher total elements" do
318
+ @manager.pusher.get_total_elements.should == 0
319
+ end
320
+ end
321
+ context "after fill!" do
322
+ before(:each) do
323
+ @manager.fill!
324
+ end
325
+ it "should calculate the correct popper total elements" do
326
+ @manager.popper.get_total_elements.should == 0 # With drain the results do not exactly match the requested ratios.
327
+ end
328
+ it "should calculate the correct popper primary elements" do
329
+ @manager.popper.get_primary_elements == 0
330
+ end
331
+ it "should calculate the correct pusher total elements" do
332
+ @manager.pusher.get_total_elements.should == 36
333
+ end
334
+ end
335
+ end
336
+ end
337
+ context "backfill => true" do
338
+ before :each do
339
+ @popper = Qfill::Popper.from_array_of_hashes(
340
+ # We will create 4 queues, high, medium, low, and none.
341
+ # These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
342
+ # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
343
+ # but not at the expense of hte experience, would be:
344
+ # high => medium => none => low
345
+ [{:name => 'high',
346
+ :elements => %w( h1 h2 h3 h4 h5 h6 h7 h8 h9 ),
347
+ :backfill => 'medium'},
348
+ {:name => "medium",
349
+ :elements => %w( m1 m2 m3 m4 m5 m6 m7 m8 m9 ),
350
+ :backfill => 'none'},
351
+ {:name => 'low',
352
+ :elements => %w( l1 l2 l3 l4 l5 l6 l7 l8 l9 ),
353
+ :backfill => true},
354
+ {:name => 'none',
355
+ :elements => %w( n1 n2 n3 n4 n5 n6 n7 n8 n9 ),
356
+ :backfill => 'low' }] )
357
+ @pusher = Qfill::Pusher.from_array_of_hashes(
358
+ [{ :name => "first",
359
+ :list_ratios => {
360
+ 'high' => 0.5,
361
+ 'medium' => 0.1,
362
+ 'none' => 0.4
363
+ },
364
+ :ratio => 0.25 },
365
+ { :name => "second",
366
+ :ratio => 0.50 },
367
+ { :name => "third",
368
+ :ratio => 0.25 }] )
369
+ @arguments = {
370
+ :pusher => @pusher,
371
+ :popper => @popper,
372
+ :all_list_max => 40,
373
+ :strategy => :drain
374
+ }
375
+ end
376
+ context "#new" do
377
+ it "should not raise any errors" do
378
+ expect { Qfill::Manager.new(@arguments) }.to_not raise_error
379
+ end
380
+ end
381
+ context "#fill!" do
382
+ it "should instantiate with pusher" do
383
+ expect { Qfill::Manager.new(@arguments).fill! }.to_not raise_error
384
+ end
385
+ end
386
+ context "results" do
387
+ before(:each) do
388
+ @manager = Qfill::Manager.new(@arguments)
389
+ end
390
+ context "before fill!" do
391
+ it "should calculate the correct popper total elements" do
392
+ @manager.popper.get_total_elements.should == 36
393
+ end
394
+ it "should calculate the correct popper primary elements" do
395
+ @manager.popper.get_primary_elements == 27
396
+ end
397
+ it "should calculate the correct pusher total elements" do
398
+ @manager.pusher.get_total_elements.should == 0
399
+ end
400
+ end
401
+ context "after fill!" do
402
+ before(:each) do
403
+ @manager.fill!
404
+ end
405
+ it "should calculate the correct popper total elements" do
406
+ @manager.popper.get_total_elements.should == 7 # With drain the results do not exactly match the requested ratios.
407
+ end
408
+ it "should calculate the correct popper primary elements" do
409
+ @manager.popper.get_primary_elements == 0
410
+ end
411
+ it "should calculate the correct pusher total elements" do
412
+ @manager.pusher.get_total_elements.should == 29
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end
418
+ end