qfill 0.0.3 → 0.1.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.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +8 -0
- data/.github/workflows/style.yml +37 -0
- data/.github/workflows/test.yml +55 -0
- data/.rspec +3 -2
- data/.rubocop.yml +26 -0
- data/.rubocop_todo.yml +163 -0
- data/.simplecov +6 -0
- data/CODE_OF_CONDUCT.md +133 -0
- data/Gemfile +31 -0
- data/Guardfile +12 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +2 -2
- data/Rakefile +24 -1
- data/lib/qfill/errors/invalid_index.rb +8 -0
- data/lib/qfill/filter.rb +5 -3
- data/lib/qfill/list.rb +4 -2
- data/lib/qfill/list_set.rb +17 -7
- data/lib/qfill/manager.rb +90 -61
- data/lib/qfill/origin.rb +4 -3
- data/lib/qfill/popper.rb +31 -26
- data/lib/qfill/pusher.rb +34 -23
- data/lib/qfill/result.rb +78 -40
- data/lib/qfill/strategy/base.rb +65 -0
- data/lib/qfill/strategy/drain_to_empty.rb +73 -0
- data/lib/qfill/strategy/drain_to_limit.rb +46 -0
- data/lib/qfill/strategy/sample.rb +42 -0
- data/lib/qfill/strategy/time_slice.rb +105 -0
- data/lib/qfill/strategy.rb +13 -0
- data/lib/qfill/version.rb +3 -1
- data/lib/qfill.rb +13 -10
- data/maintenance-branch +1 -0
- data/qfill.gemspec +15 -13
- data/spec/qfill/filter_spec.rb +35 -26
- data/spec/qfill/list_set_spec.rb +28 -23
- data/spec/qfill/list_spec.rb +35 -27
- data/spec/qfill/manager_spec.rb +715 -284
- data/spec/qfill/origin_spec.rb +45 -35
- data/spec/qfill/popper_spec.rb +36 -30
- data/spec/qfill/pusher_spec.rb +32 -26
- data/spec/qfill/result_spec.rb +49 -38
- data/spec/qfill_spec.rb +6 -5
- data/spec/spec_helper.rb +11 -37
- data/spec/support/helper.rb +13 -0
- data/spec/support/random_object.rb +30 -0
- metadata +52 -23
data/spec/qfill/manager_spec.rb
CHANGED
@@ -1,416 +1,847 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
describe Qfill::Manager do
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
let(:manager) { described_class.new(arguments) }
|
6
|
+
|
7
|
+
describe '#new' do
|
8
|
+
context 'with no arguments' do
|
9
|
+
it 'raises ArgumentError' do
|
10
|
+
expect { described_class.new }.to raise_error(ArgumentError)
|
7
11
|
end
|
8
12
|
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
|
14
|
+
context 'with only popper' do
|
15
|
+
let(:popper) do
|
16
|
+
Qfill::Popper.from_array_of_hashes(
|
17
|
+
[{ name: 'High List',
|
18
|
+
elements: [1, 2, 3] }]
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'raises ArgumentError' do
|
23
|
+
expect { described_class.new(popper: popper) }.to raise_error(ArgumentError)
|
15
24
|
end
|
16
25
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
|
27
|
+
context 'with only pusher' do
|
28
|
+
let(:pusher) do
|
29
|
+
Qfill::Pusher.from_array_of_hashes(
|
30
|
+
[{ name: 'Some Result',
|
31
|
+
ratio: 0.25 }]
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'raises ArgumentError' do
|
36
|
+
expect { described_class.new(pusher: pusher) }.to raise_error(ArgumentError)
|
23
37
|
end
|
24
38
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
:
|
39
|
+
|
40
|
+
context 'with popper and pusher' do
|
41
|
+
let(:popper) do
|
42
|
+
Qfill::Popper.from_array_of_hashes(
|
43
|
+
[{ name: 'High List',
|
44
|
+
elements: [1, 2, 3] }]
|
45
|
+
)
|
46
|
+
end
|
47
|
+
let(:pusher) do
|
48
|
+
Qfill::Pusher.from_array_of_hashes(
|
49
|
+
[{ name: 'Some Result',
|
50
|
+
ratio: 0.25 }]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
let(:arguments) do
|
54
|
+
{
|
55
|
+
pusher: pusher,
|
56
|
+
popper: popper
|
36
57
|
}
|
37
58
|
end
|
38
|
-
|
39
|
-
|
59
|
+
|
60
|
+
it 'does not raise any errors' do
|
61
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
40
62
|
end
|
41
|
-
|
42
|
-
|
63
|
+
|
64
|
+
it 'instantiates with pusher' do
|
65
|
+
expect(described_class.new(arguments).pusher).to eq(pusher)
|
43
66
|
end
|
44
|
-
|
45
|
-
|
67
|
+
|
68
|
+
it 'instantiates with popper' do
|
69
|
+
expect(described_class.new(arguments).popper).to eq(popper)
|
46
70
|
end
|
47
71
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
:
|
59
|
-
|
72
|
+
|
73
|
+
context 'with popper and pusher and all_list_max smaller than # total elements' do
|
74
|
+
let(:popper) do
|
75
|
+
Qfill::Popper.from_array_of_hashes(
|
76
|
+
[{ name: 'High List',
|
77
|
+
elements: [1, 2, 3] }]
|
78
|
+
)
|
79
|
+
end
|
80
|
+
let(:pusher) do
|
81
|
+
Qfill::Pusher.from_array_of_hashes(
|
82
|
+
[{ name: 'Some Result',
|
83
|
+
ratio: 0.25 }]
|
84
|
+
)
|
85
|
+
end
|
86
|
+
let(:arguments) do
|
87
|
+
{
|
88
|
+
pusher: pusher,
|
89
|
+
popper: popper,
|
90
|
+
all_list_max: 2
|
60
91
|
}
|
61
92
|
end
|
62
|
-
|
63
|
-
|
93
|
+
|
94
|
+
it 'does not raise any errors' do
|
95
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
64
96
|
end
|
65
|
-
|
66
|
-
|
97
|
+
|
98
|
+
it 'instantiates with pusher' do
|
99
|
+
expect(described_class.new(arguments).pusher).to eq(pusher)
|
67
100
|
end
|
68
|
-
|
69
|
-
|
101
|
+
|
102
|
+
it 'instantiates with popper' do
|
103
|
+
expect(described_class.new(arguments).popper).to eq(popper)
|
70
104
|
end
|
71
|
-
|
72
|
-
|
105
|
+
|
106
|
+
it 'retains specified all_list_max' do
|
107
|
+
expect(described_class.new(arguments).all_list_max).to eq(2)
|
73
108
|
end
|
74
109
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
:
|
86
|
-
|
110
|
+
|
111
|
+
context 'all_list_max greater than # total elements' do
|
112
|
+
let(:popper) do
|
113
|
+
Qfill::Popper.from_array_of_hashes(
|
114
|
+
[{ name: 'High List',
|
115
|
+
elements: [1, 2, 3] }]
|
116
|
+
)
|
117
|
+
end
|
118
|
+
let(:pusher) do
|
119
|
+
Qfill::Pusher.from_array_of_hashes(
|
120
|
+
[{ name: 'Some Result',
|
121
|
+
ratio: 0.25 }]
|
122
|
+
)
|
123
|
+
end
|
124
|
+
let(:arguments) do
|
125
|
+
{
|
126
|
+
pusher: pusher,
|
127
|
+
popper: popper,
|
128
|
+
all_list_max: 666
|
87
129
|
}
|
88
130
|
end
|
89
|
-
|
90
|
-
|
131
|
+
|
132
|
+
it 'reduces all_list_max to number of elements' do
|
133
|
+
expect(described_class.new(arguments).all_list_max).to eq(3)
|
91
134
|
end
|
92
135
|
end
|
93
136
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
137
|
+
|
138
|
+
context 'when strategy => :sample' do
|
139
|
+
context 'when backfill => false' do
|
140
|
+
let(:popper) do
|
141
|
+
Qfill::Popper.from_array_of_hashes(
|
98
142
|
# We will create 4 queues, high, medium, low, and none.
|
99
143
|
# These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
|
100
144
|
# The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
|
101
145
|
# but not at the expense of hte experience, would be:
|
102
146
|
# high => medium => none => low
|
103
|
-
[{:
|
104
|
-
|
105
|
-
|
106
|
-
{:
|
107
|
-
|
108
|
-
|
109
|
-
{:
|
110
|
-
|
111
|
-
|
112
|
-
{:
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
147
|
+
[{ name: 'high',
|
148
|
+
elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9],
|
149
|
+
backfill: 'medium' },
|
150
|
+
{ name: 'medium',
|
151
|
+
elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9],
|
152
|
+
backfill: 'none' },
|
153
|
+
{ name: 'low',
|
154
|
+
elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9],
|
155
|
+
backfill: false },
|
156
|
+
{ name: 'none',
|
157
|
+
elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9],
|
158
|
+
backfill: 'low' }]
|
159
|
+
)
|
160
|
+
end
|
161
|
+
let(:pusher) do
|
162
|
+
Qfill::Pusher.from_array_of_hashes(
|
163
|
+
[{ name: 'first',
|
164
|
+
list_ratios: {
|
118
165
|
'high' => 0.5,
|
119
166
|
'medium' => 0.1,
|
120
167
|
'none' => 0.4
|
121
168
|
},
|
122
|
-
:
|
123
|
-
{ :
|
124
|
-
:
|
125
|
-
{ :
|
126
|
-
:
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
:
|
169
|
+
ratio: 0.25 },
|
170
|
+
{ name: 'second',
|
171
|
+
ratio: 0.50 },
|
172
|
+
{ name: 'third',
|
173
|
+
ratio: 0.25 }]
|
174
|
+
)
|
175
|
+
end
|
176
|
+
let(:arguments) do
|
177
|
+
{
|
178
|
+
pusher: pusher,
|
179
|
+
popper: popper,
|
180
|
+
all_list_max: 40,
|
181
|
+
strategy: :sample
|
132
182
|
}
|
133
183
|
end
|
134
|
-
|
135
|
-
|
136
|
-
|
184
|
+
|
185
|
+
describe '#new' do
|
186
|
+
it 'does not raise any errors' do
|
187
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
137
188
|
end
|
138
189
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
190
|
+
|
191
|
+
describe '#fill!' do
|
192
|
+
it 'instantiates with pusher' do
|
193
|
+
expect { described_class.new(arguments).fill! }.not_to raise_error
|
142
194
|
end
|
143
195
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
it "should calculate the correct popper total elements" do
|
150
|
-
@manager.popper.get_total_elements.should == 36
|
196
|
+
|
197
|
+
context 'results' do
|
198
|
+
context 'before fill!' do
|
199
|
+
it 'calculates the correct popper total elements' do
|
200
|
+
expect(manager.popper.count_all_elements).to eq(36)
|
151
201
|
end
|
152
|
-
|
153
|
-
|
202
|
+
|
203
|
+
it 'calculates the correct popper primary elements' do
|
204
|
+
expect(manager.popper.count_primary_elements).to eq(36)
|
154
205
|
end
|
155
|
-
|
156
|
-
|
206
|
+
|
207
|
+
it 'calculates the correct pusher total elements' do
|
208
|
+
expect(manager.pusher.count_all_elements).to eq(0)
|
157
209
|
end
|
158
210
|
end
|
159
|
-
|
160
|
-
|
161
|
-
|
211
|
+
|
212
|
+
context 'after fill!' do
|
213
|
+
before do
|
214
|
+
manager.fill!
|
162
215
|
end
|
163
|
-
|
164
|
-
|
216
|
+
|
217
|
+
it 'calculates the correct popper total elements' do
|
218
|
+
expect(manager.popper.count_all_elements).to eq(0)
|
165
219
|
end
|
166
|
-
|
167
|
-
|
220
|
+
|
221
|
+
it 'calculates the correct popper primary elements' do
|
222
|
+
expect(manager.popper.count_primary_elements).to eq(0)
|
168
223
|
end
|
169
|
-
|
170
|
-
|
224
|
+
|
225
|
+
it 'calculates the correct pusher total elements' do
|
226
|
+
expect(manager.pusher.count_all_elements).to eq(36)
|
171
227
|
end
|
172
228
|
end
|
173
229
|
end
|
174
230
|
end
|
175
|
-
|
176
|
-
|
177
|
-
|
231
|
+
|
232
|
+
context 'when backfill => true' do
|
233
|
+
let(:popper) do
|
234
|
+
Qfill::Popper.from_array_of_hashes(
|
178
235
|
# We will create 4 queues, high, medium, low, and none.
|
179
236
|
# These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
|
180
237
|
# The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
|
181
238
|
# but not at the expense of hte experience, would be:
|
182
239
|
# high => medium => none => low
|
183
|
-
[{:
|
184
|
-
|
185
|
-
|
186
|
-
{:
|
187
|
-
|
188
|
-
|
189
|
-
{:
|
190
|
-
|
191
|
-
|
192
|
-
{:
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
240
|
+
[{ name: 'high',
|
241
|
+
elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9],
|
242
|
+
backfill: 'medium' },
|
243
|
+
{ name: 'medium',
|
244
|
+
elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9],
|
245
|
+
backfill: 'none' },
|
246
|
+
{ name: 'low',
|
247
|
+
elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9],
|
248
|
+
backfill: true },
|
249
|
+
{ name: 'none',
|
250
|
+
elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9],
|
251
|
+
backfill: 'low' }]
|
252
|
+
)
|
253
|
+
end
|
254
|
+
let(:pusher) do
|
255
|
+
Qfill::Pusher.from_array_of_hashes(
|
256
|
+
[{ name: 'first',
|
257
|
+
list_ratios: {
|
198
258
|
'high' => 0.5,
|
199
259
|
'medium' => 0.1,
|
200
260
|
'none' => 0.4
|
201
261
|
},
|
202
|
-
:
|
203
|
-
{ :
|
204
|
-
:
|
205
|
-
{ :
|
206
|
-
:
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
:
|
262
|
+
ratio: 0.25 },
|
263
|
+
{ name: 'second',
|
264
|
+
ratio: 0.50 },
|
265
|
+
{ name: 'third',
|
266
|
+
ratio: 0.25 }]
|
267
|
+
)
|
268
|
+
end
|
269
|
+
let(:arguments) do
|
270
|
+
{
|
271
|
+
pusher: pusher,
|
272
|
+
popper: popper,
|
273
|
+
all_list_max: 28,
|
274
|
+
strategy: :sample
|
212
275
|
}
|
213
276
|
end
|
214
|
-
|
215
|
-
|
216
|
-
|
277
|
+
|
278
|
+
describe '#new' do
|
279
|
+
it 'does not raise any errors' do
|
280
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
217
281
|
end
|
218
282
|
end
|
219
|
-
|
220
|
-
|
221
|
-
|
283
|
+
|
284
|
+
describe '#fill!' do
|
285
|
+
it 'instantiates with pusher' do
|
286
|
+
expect { described_class.new(arguments).fill! }.not_to raise_error
|
222
287
|
end
|
223
288
|
end
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
it "should calculate the correct popper total elements" do
|
230
|
-
@manager.popper.get_total_elements.should == 36
|
289
|
+
|
290
|
+
context 'results' do
|
291
|
+
context 'before fill!' do
|
292
|
+
it 'calculates the correct popper total elements' do
|
293
|
+
expect(manager.popper.count_all_elements).to eq(36)
|
231
294
|
end
|
232
|
-
|
233
|
-
|
295
|
+
|
296
|
+
it 'calculates the correct popper primary elements' do
|
297
|
+
expect(manager.popper.count_primary_elements).to eq(27)
|
234
298
|
end
|
235
|
-
|
236
|
-
|
299
|
+
|
300
|
+
it 'calculates the correct pusher total elements' do
|
301
|
+
expect(manager.pusher.count_all_elements).to eq(0)
|
237
302
|
end
|
238
303
|
end
|
239
|
-
|
240
|
-
|
241
|
-
|
304
|
+
|
305
|
+
context 'after fill!' do
|
306
|
+
before do
|
307
|
+
manager.fill!
|
242
308
|
end
|
243
|
-
|
244
|
-
|
309
|
+
|
310
|
+
it 'calculates the correct popper total elements' do
|
311
|
+
expect(manager.popper.count_all_elements).to eq(8)
|
245
312
|
end
|
246
|
-
|
247
|
-
|
313
|
+
|
314
|
+
it 'calculates the correct popper primary elements' do
|
315
|
+
expect(manager.popper.count_primary_elements).to eq(0)
|
248
316
|
end
|
249
|
-
|
250
|
-
|
317
|
+
|
318
|
+
it 'calculates the correct pusher total elements' do
|
319
|
+
expect(manager.pusher.count_all_elements).to eq(28)
|
251
320
|
end
|
252
321
|
end
|
253
322
|
end
|
254
323
|
end
|
255
324
|
end
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
325
|
+
|
326
|
+
context 'when strategy => :drain_to_limit' do
|
327
|
+
context 'when backfill => false' do
|
328
|
+
let(:popper) do
|
329
|
+
Qfill::Popper.from_array_of_hashes(
|
260
330
|
# We will create 4 queues, high, medium, low, and none.
|
261
331
|
# These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
|
262
332
|
# The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
|
263
333
|
# but not at the expense of hte experience, would be:
|
264
334
|
# high => medium => none => low
|
265
|
-
[{:
|
266
|
-
|
267
|
-
|
268
|
-
{:
|
269
|
-
|
270
|
-
|
271
|
-
{:
|
272
|
-
|
273
|
-
|
274
|
-
{:
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
335
|
+
[{ name: 'high',
|
336
|
+
elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12],
|
337
|
+
backfill: 'medium' },
|
338
|
+
{ name: 'medium',
|
339
|
+
elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12],
|
340
|
+
backfill: 'none' },
|
341
|
+
{ name: 'low',
|
342
|
+
elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9 l10 l11 l12],
|
343
|
+
backfill: false },
|
344
|
+
{ name: 'none',
|
345
|
+
elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12],
|
346
|
+
backfill: 'low' }]
|
347
|
+
)
|
348
|
+
end
|
349
|
+
let(:pusher) do
|
350
|
+
Qfill::Pusher.from_array_of_hashes(
|
351
|
+
[{ name: 'first',
|
352
|
+
list_ratios: {
|
280
353
|
'high' => 0.5,
|
281
354
|
'medium' => 0.1,
|
282
355
|
'none' => 0.4
|
283
356
|
},
|
284
|
-
:
|
285
|
-
{ :
|
286
|
-
:
|
287
|
-
{ :
|
288
|
-
:
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
:
|
357
|
+
ratio: 0.25 },
|
358
|
+
{ name: 'second',
|
359
|
+
ratio: 0.50 },
|
360
|
+
{ name: 'third',
|
361
|
+
ratio: 0.25 }]
|
362
|
+
)
|
363
|
+
end
|
364
|
+
let(:arguments) do
|
365
|
+
{
|
366
|
+
pusher: pusher,
|
367
|
+
popper: popper,
|
368
|
+
all_list_max: 40,
|
369
|
+
strategy: :drain_to_limit
|
294
370
|
}
|
295
371
|
end
|
296
|
-
|
297
|
-
|
298
|
-
|
372
|
+
|
373
|
+
describe '#new' do
|
374
|
+
it 'does not raise any errors' do
|
375
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
299
376
|
end
|
300
377
|
end
|
301
|
-
|
302
|
-
|
303
|
-
|
378
|
+
|
379
|
+
describe '#fill!' do
|
380
|
+
it 'instantiates with pusher' do
|
381
|
+
expect { described_class.new(arguments).fill! }.not_to raise_error
|
304
382
|
end
|
305
383
|
end
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
it "should calculate the correct popper total elements" do
|
312
|
-
@manager.popper.get_total_elements.should == 36
|
384
|
+
|
385
|
+
context 'results' do
|
386
|
+
context 'before fill!' do
|
387
|
+
it 'calculates the correct popper total elements' do
|
388
|
+
expect(manager.popper.count_all_elements).to eq(48)
|
313
389
|
end
|
314
|
-
|
315
|
-
|
390
|
+
|
391
|
+
it 'calculates the correct popper primary elements' do
|
392
|
+
expect(manager.popper.count_primary_elements).to eq(48)
|
316
393
|
end
|
317
|
-
|
318
|
-
|
394
|
+
|
395
|
+
it 'calculates the correct pusher total elements' do
|
396
|
+
expect(manager.pusher.count_all_elements).to eq(0)
|
319
397
|
end
|
320
398
|
end
|
321
|
-
|
322
|
-
|
323
|
-
|
399
|
+
|
400
|
+
context 'after fill!' do
|
401
|
+
before do
|
402
|
+
manager.fill!
|
324
403
|
end
|
325
|
-
|
326
|
-
|
404
|
+
|
405
|
+
it 'calculates the correct popper total elements' do
|
406
|
+
expect(manager.popper.count_all_elements).to eq(8) # With drain_to_limit the results do not exactly match the requested ratios.
|
327
407
|
end
|
328
|
-
|
329
|
-
|
408
|
+
|
409
|
+
it 'calculates the correct popper primary elements' do
|
410
|
+
expect(manager.popper.count_primary_elements).to eq(8)
|
330
411
|
end
|
331
|
-
|
332
|
-
|
412
|
+
|
413
|
+
it 'calculates the correct pusher total elements' do
|
414
|
+
expect(manager.pusher.count_all_elements).to eq(40)
|
333
415
|
end
|
334
416
|
end
|
335
417
|
end
|
336
418
|
end
|
337
|
-
|
338
|
-
|
339
|
-
|
419
|
+
|
420
|
+
context 'when backfill => true' do
|
421
|
+
let(:popper) do
|
422
|
+
Qfill::Popper.from_array_of_hashes(
|
340
423
|
# We will create 4 queues, high, medium, low, and none.
|
341
424
|
# These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
|
342
425
|
# The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
|
343
426
|
# but not at the expense of hte experience, would be:
|
344
427
|
# high => medium => none => low
|
345
|
-
[{:
|
346
|
-
|
347
|
-
|
348
|
-
{:
|
349
|
-
|
350
|
-
|
351
|
-
{:
|
352
|
-
|
353
|
-
|
354
|
-
{:
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
428
|
+
[{ name: 'high',
|
429
|
+
elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9],
|
430
|
+
backfill: 'medium' },
|
431
|
+
{ name: 'medium',
|
432
|
+
elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9],
|
433
|
+
backfill: 'none' },
|
434
|
+
{ name: 'low',
|
435
|
+
elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9],
|
436
|
+
backfill: true },
|
437
|
+
{ name: 'none',
|
438
|
+
elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9],
|
439
|
+
backfill: 'low' }]
|
440
|
+
)
|
441
|
+
end
|
442
|
+
let(:pusher) do
|
443
|
+
Qfill::Pusher.from_array_of_hashes(
|
444
|
+
[{ name: 'first',
|
445
|
+
list_ratios: {
|
360
446
|
'high' => 0.5,
|
361
447
|
'medium' => 0.1,
|
362
448
|
'none' => 0.4
|
363
449
|
},
|
364
|
-
:
|
365
|
-
{ :
|
366
|
-
:
|
367
|
-
{ :
|
368
|
-
:
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
:
|
450
|
+
ratio: 0.25 },
|
451
|
+
{ name: 'second',
|
452
|
+
ratio: 0.50 },
|
453
|
+
{ name: 'third',
|
454
|
+
ratio: 0.25 }]
|
455
|
+
)
|
456
|
+
end
|
457
|
+
let(:arguments) do
|
458
|
+
{
|
459
|
+
pusher: pusher,
|
460
|
+
popper: popper,
|
461
|
+
all_list_max: 40,
|
462
|
+
strategy: :drain_to_limit
|
374
463
|
}
|
375
464
|
end
|
376
|
-
|
377
|
-
|
378
|
-
|
465
|
+
|
466
|
+
describe '#new' do
|
467
|
+
it 'does not raise any errors' do
|
468
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
describe '#fill!' do
|
473
|
+
it 'instantiates with pusher' do
|
474
|
+
expect { described_class.new(arguments).fill! }.not_to raise_error
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
context 'results' do
|
479
|
+
context 'before fill!' do
|
480
|
+
it 'calculates the correct popper total elements' do
|
481
|
+
expect(manager.popper.count_all_elements).to eq(36)
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'calculates the correct popper primary elements' do
|
485
|
+
expect(manager.popper.count_primary_elements).to eq(27)
|
486
|
+
end
|
487
|
+
|
488
|
+
it 'calculates the correct pusher total elements' do
|
489
|
+
expect(manager.pusher.count_all_elements).to eq(0)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
context 'after fill!' do
|
494
|
+
before do
|
495
|
+
manager.fill!
|
496
|
+
end
|
497
|
+
|
498
|
+
it 'calculates the correct popper total elements' do
|
499
|
+
expect(manager.popper.count_all_elements).to eq(7) # With drain_to_limit the results do not exactly match the requested ratios.
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'calculates the correct popper primary elements' do
|
503
|
+
expect(manager.popper.count_primary_elements).to eq(0)
|
504
|
+
end
|
505
|
+
|
506
|
+
it 'calculates the correct pusher total elements' do
|
507
|
+
expect(manager.pusher.count_all_elements).to eq(29)
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
context 'when strategy => :drain_to_empty' do
|
515
|
+
context 'when backfill => false' do
|
516
|
+
let(:popper) do
|
517
|
+
Qfill::Popper.from_array_of_hashes(
|
518
|
+
# We will create 4 queues, higspec/qfill/manager_spec.rb:386h, medium, low, and none.
|
519
|
+
# These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
|
520
|
+
# The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
|
521
|
+
# but not at the expense of the experience, would be:
|
522
|
+
# high => medium => none => low
|
523
|
+
[{ name: 'high',
|
524
|
+
elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9],
|
525
|
+
backfill: 'medium' },
|
526
|
+
{ name: 'medium',
|
527
|
+
elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9],
|
528
|
+
backfill: 'none' },
|
529
|
+
{ name: 'low',
|
530
|
+
elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9],
|
531
|
+
backfill: false },
|
532
|
+
{ name: 'none',
|
533
|
+
elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9],
|
534
|
+
backfill: 'low' }]
|
535
|
+
)
|
536
|
+
end
|
537
|
+
let(:pusher) do
|
538
|
+
Qfill::Pusher.from_array_of_hashes(
|
539
|
+
[{ name: 'first',
|
540
|
+
list_ratios: {
|
541
|
+
'high' => 0.5,
|
542
|
+
'medium' => 0.1,
|
543
|
+
'none' => 0.4
|
544
|
+
},
|
545
|
+
preferred: %w[high none] },
|
546
|
+
{ name: 'second',
|
547
|
+
list_ratios: {
|
548
|
+
'high' => 0.5,
|
549
|
+
'medium' => 0.4,
|
550
|
+
'none' => 0.1
|
551
|
+
},
|
552
|
+
preferred: %w[high medium] },
|
553
|
+
{ name: 'third' }]
|
554
|
+
)
|
555
|
+
end
|
556
|
+
let(:arguments) do
|
557
|
+
{
|
558
|
+
pusher: pusher,
|
559
|
+
popper: popper,
|
560
|
+
all_list_max: 40,
|
561
|
+
strategy: :drain_to_empty
|
562
|
+
}
|
563
|
+
end
|
564
|
+
|
565
|
+
describe '#new' do
|
566
|
+
it 'does not raise any errors' do
|
567
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
describe '#fill!' do
|
572
|
+
it 'instantiates with pusher' do
|
573
|
+
expect { described_class.new(arguments).fill! }.not_to raise_error
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
context 'results' do
|
578
|
+
end
|
579
|
+
|
580
|
+
context 'before fill!' do
|
581
|
+
it 'calculates the correct popper total elements' do
|
582
|
+
expect(manager.popper.count_all_elements).to eq(36)
|
583
|
+
end
|
584
|
+
|
585
|
+
it 'calculates the correct popper primary elements' do
|
586
|
+
expect(manager.popper.count_primary_elements).to eq(36)
|
587
|
+
end
|
588
|
+
|
589
|
+
it 'calculates the correct pusher total elements' do
|
590
|
+
expect(manager.pusher.count_all_elements).to eq(0)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
context 'after fill!' do
|
595
|
+
before do
|
596
|
+
manager.fill!
|
597
|
+
end
|
598
|
+
|
599
|
+
it 'calculates the correct popper total elements' do
|
600
|
+
expect(manager.popper.count_all_elements).to eq(0) # With drain_to_limit the results do not exactly match the requested ratios.
|
379
601
|
end
|
602
|
+
|
603
|
+
it 'calculates the correct popper primary elements' do
|
604
|
+
expect(manager.popper.count_primary_elements).to eq(0)
|
605
|
+
end
|
606
|
+
|
607
|
+
it 'calculates the correct pusher total elements' do
|
608
|
+
expect(manager.pusher.count_all_elements).to eq(36)
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
context 'when backfill => true' do
|
614
|
+
let(:popper) do
|
615
|
+
Qfill::Popper.from_array_of_hashes(
|
616
|
+
# We will create 4 queues, high, medium, low, and none.
|
617
|
+
# These might be queue of things that have ratings, and the none queue for things which have not yet been rated.
|
618
|
+
# The backfill route of the queues then, assuming we want people to rate the things that are not yet rated,
|
619
|
+
# but not at the expense of the experience, would be:
|
620
|
+
# high => medium => none => low
|
621
|
+
[{ name: 'high',
|
622
|
+
elements: build_elements('h', 20),
|
623
|
+
backfill: 'medium' },
|
624
|
+
{ name: 'medium',
|
625
|
+
elements: build_elements('m', 20),
|
626
|
+
backfill: 'none' },
|
627
|
+
{ name: 'low',
|
628
|
+
elements: build_elements('l', 20),
|
629
|
+
backfill: true },
|
630
|
+
{ name: 'none',
|
631
|
+
elements: build_elements('n', 20),
|
632
|
+
backfill: 'low' }]
|
633
|
+
)
|
634
|
+
end
|
635
|
+
let(:pusher) do
|
636
|
+
Qfill::Pusher.from_array_of_hashes(
|
637
|
+
[{ name: 'first',
|
638
|
+
list_ratios: {
|
639
|
+
'high' => 0.5,
|
640
|
+
'medium' => 0.1,
|
641
|
+
'none' => 0.4
|
642
|
+
},
|
643
|
+
ratio: 1,
|
644
|
+
preferred: %w[high none] },
|
645
|
+
{ name: 'second',
|
646
|
+
ratio: 0.50 },
|
647
|
+
{ name: 'third',
|
648
|
+
ratio: 0.25 }]
|
649
|
+
)
|
380
650
|
end
|
381
|
-
|
382
|
-
|
383
|
-
|
651
|
+
let(:arguments) do
|
652
|
+
{
|
653
|
+
pusher: pusher,
|
654
|
+
popper: popper,
|
655
|
+
all_list_max: 100,
|
656
|
+
strategy: :drain_to_empty
|
657
|
+
}
|
658
|
+
end
|
659
|
+
|
660
|
+
describe '#new' do
|
661
|
+
it 'does not raise any errors' do
|
662
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
384
663
|
end
|
385
664
|
end
|
386
|
-
|
387
|
-
|
388
|
-
|
665
|
+
|
666
|
+
describe '#fill!' do
|
667
|
+
it 'instantiates with pusher' do
|
668
|
+
expect { described_class.new(arguments).fill! }.not_to raise_error
|
389
669
|
end
|
390
|
-
|
391
|
-
|
392
|
-
|
670
|
+
end
|
671
|
+
|
672
|
+
context 'results' do
|
673
|
+
context 'before fill!' do
|
674
|
+
it 'calculates the correct popper total elements' do
|
675
|
+
expect(manager.popper.count_all_elements).to eq(80)
|
393
676
|
end
|
394
|
-
|
395
|
-
|
677
|
+
|
678
|
+
it 'calculates the correct popper primary elements' do
|
679
|
+
expect(manager.popper.count_primary_elements).to eq(60)
|
396
680
|
end
|
397
|
-
|
398
|
-
|
681
|
+
|
682
|
+
it 'calculates the correct pusher total elements' do
|
683
|
+
expect(manager.pusher.count_all_elements).to eq(0)
|
399
684
|
end
|
400
685
|
end
|
401
|
-
|
402
|
-
|
403
|
-
|
686
|
+
|
687
|
+
context 'after fill!' do
|
688
|
+
before do
|
689
|
+
manager.fill!
|
404
690
|
end
|
405
|
-
|
406
|
-
|
691
|
+
|
692
|
+
it 'calculates the correct leftover popper total elements' do
|
693
|
+
expect(manager.popper.count_all_elements).to eq(20) # TODO???: With drain_to_empty the results do not exactly match the requested ratios.
|
407
694
|
end
|
408
|
-
|
409
|
-
|
695
|
+
|
696
|
+
it 'calculates the correct popper primary elements' do
|
697
|
+
expect(manager.popper.count_primary_elements).to eq(0)
|
410
698
|
end
|
411
|
-
|
412
|
-
|
699
|
+
|
700
|
+
context 'when all_list_max is higher than count of all elements' do
|
701
|
+
it 'reduces all_list_max to original count of all elements' do
|
702
|
+
expect(manager.all_list_max).to eq(80)
|
703
|
+
end
|
704
|
+
|
705
|
+
it 'is greater than count of all elements in the results (pusher)' do
|
706
|
+
expect(manager.all_list_max > manager.pusher.count_all_elements).to eq(true)
|
707
|
+
end
|
413
708
|
end
|
709
|
+
|
710
|
+
context 'when all_list_max is lower than count of all elements' do
|
711
|
+
let(:arguments) do
|
712
|
+
{
|
713
|
+
pusher: pusher,
|
714
|
+
popper: popper,
|
715
|
+
all_list_max: 4,
|
716
|
+
strategy: :drain_to_empty
|
717
|
+
}
|
718
|
+
end
|
719
|
+
|
720
|
+
it 'retains specified all_list_max' do
|
721
|
+
expect(manager.all_list_max).to eq(4)
|
722
|
+
end
|
723
|
+
|
724
|
+
it 'is equal to count of all elements in the results (pusher)' do
|
725
|
+
expect(manager.pusher.count_all_elements).to eq(manager.all_list_max)
|
726
|
+
end
|
727
|
+
end
|
728
|
+
end
|
729
|
+
end
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
context 'when strategy => :time_slice' do
|
734
|
+
let(:elements) { 2400 }
|
735
|
+
let(:window_size) { 20 }
|
736
|
+
let(:pane_size) { 2 }
|
737
|
+
let(:popper) do
|
738
|
+
Qfill::Popper.from_array_of_hashes(
|
739
|
+
[
|
740
|
+
{
|
741
|
+
name: 'data',
|
742
|
+
elements: build_elements('ef', elements)
|
743
|
+
}
|
744
|
+
]
|
745
|
+
)
|
746
|
+
end
|
747
|
+
let(:arguments) do
|
748
|
+
{
|
749
|
+
popper: popper,
|
750
|
+
strategy: :time_slice,
|
751
|
+
strategy_options: {
|
752
|
+
window_size: window_size,
|
753
|
+
window_units: 'minutes',
|
754
|
+
pane_size: pane_size,
|
755
|
+
pane_units: 'seconds'
|
756
|
+
}
|
757
|
+
}
|
758
|
+
end
|
759
|
+
|
760
|
+
shared_examples_for "a good citizen" do
|
761
|
+
describe '#new' do
|
762
|
+
it 'does not raise any errors' do
|
763
|
+
expect { described_class.new(arguments) }.not_to raise_error
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
describe '#fill!' do
|
768
|
+
it 'instantiates with pusher' do
|
769
|
+
expect { described_class.new(arguments).fill! }.not_to raise_error
|
770
|
+
end
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
it_behaves_like "a good citizen"
|
775
|
+
|
776
|
+
context "other options" do
|
777
|
+
let(:elements) { 101 }
|
778
|
+
|
779
|
+
context "5 minutes by 3 seconds" do
|
780
|
+
let(:window_size) { 5 }
|
781
|
+
let(:pane_size) { 3 }
|
782
|
+
|
783
|
+
it_behaves_like "a good citizen"
|
784
|
+
end
|
785
|
+
|
786
|
+
context "7 minutes by 4 seconds" do
|
787
|
+
let(:window_size) { 7 }
|
788
|
+
let(:pane_size) { 4 }
|
789
|
+
|
790
|
+
it_behaves_like "a good citizen"
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
context 'results' do
|
795
|
+
context 'before fill!' do
|
796
|
+
it 'calculates the correct popper total elements' do
|
797
|
+
expect(manager.popper.count_all_elements).to eq(2400)
|
798
|
+
end
|
799
|
+
|
800
|
+
it 'calculates the correct popper primary elements' do
|
801
|
+
expect(manager.popper.count_primary_elements).to eq(2400)
|
802
|
+
end
|
803
|
+
|
804
|
+
it 'calculates the correct pusher total elements' do
|
805
|
+
expect(manager.pusher.count_all_elements).to eq(0)
|
806
|
+
end
|
807
|
+
|
808
|
+
it 'has pusher queues that are "full" (because max starts at 0)' do
|
809
|
+
expect(manager.pusher.queues.count(&:is_full?)).to eq(600)
|
810
|
+
end
|
811
|
+
|
812
|
+
it 'has pusher queues that are not really full' do
|
813
|
+
expect(manager.pusher.queues.count { |x| x.elements.empty? }).to eq(600)
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
817
|
+
context 'after fill!' do
|
818
|
+
before do
|
819
|
+
manager.fill!
|
820
|
+
end
|
821
|
+
|
822
|
+
it 'calculates the correct popper total elements' do
|
823
|
+
expect(manager.popper.count_all_elements).to eq(0)
|
824
|
+
end
|
825
|
+
|
826
|
+
it 'calculates the correct popper primary elements' do
|
827
|
+
expect(manager.popper.count_primary_elements).to eq(0)
|
828
|
+
end
|
829
|
+
|
830
|
+
it 'calculates the correct pusher total elements' do
|
831
|
+
expect(manager.pusher.count_all_elements).to eq(2400)
|
832
|
+
end
|
833
|
+
|
834
|
+
it 'has correct number of pusher queues' do
|
835
|
+
expect(manager.pusher.queues.length).to eq(600)
|
836
|
+
end
|
837
|
+
|
838
|
+
it 'has pusher queues that are full' do
|
839
|
+
expect(manager.pusher.queues.count(&:is_full?)).to eq(600)
|
840
|
+
end
|
841
|
+
|
842
|
+
it 'has pusher queues that are not too full' do
|
843
|
+
elements_per_queue = 2400 / 600
|
844
|
+
expect(manager.pusher.queues.count { |x| x.elements.length == elements_per_queue }).to eq(600)
|
414
845
|
end
|
415
846
|
end
|
416
847
|
end
|