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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +152 -0
- data/Rakefile +1 -0
- data/lib/qfill.rb +13 -0
- data/lib/qfill/filter.rb +18 -0
- data/lib/qfill/list.rb +18 -0
- data/lib/qfill/list_set.rb +29 -0
- data/lib/qfill/manager.rb +91 -0
- data/lib/qfill/origin.rb +19 -0
- data/lib/qfill/popper.rb +96 -0
- data/lib/qfill/pusher.rb +85 -0
- data/lib/qfill/result.rb +116 -0
- data/lib/qfill/version.rb +3 -0
- data/qfill.gemspec +36 -0
- data/spec/qfill/filter_spec.rb +44 -0
- data/spec/qfill/list_set_spec.rb +36 -0
- data/spec/qfill/list_spec.rb +44 -0
- data/spec/qfill/manager_spec.rb +418 -0
- data/spec/qfill/origin_spec.rb +55 -0
- data/spec/qfill/popper_spec.rb +44 -0
- data/spec/qfill/pusher_spec.rb +38 -0
- data/spec/qfill/result_spec.rb +57 -0
- data/spec/qfill_spec.rb +11 -0
- data/spec/spec_helper.rb +48 -0
- metadata +102 -0
data/lib/qfill/pusher.rb
ADDED
@@ -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
|
data/lib/qfill/result.rb
ADDED
@@ -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
|
data/qfill.gemspec
ADDED
@@ -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
|