huge_enumerable 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 +20 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +17 -0
- data/huge_enumerable.gemspec +28 -0
- data/lib/huge_enumerable/huge_collection.rb +83 -0
- data/lib/huge_enumerable/huge_combination.rb +118 -0
- data/lib/huge_enumerable/huge_permutation.rb +67 -0
- data/lib/huge_enumerable/huge_product.rb +77 -0
- data/lib/huge_enumerable/version.rb +4 -0
- data/lib/huge_enumerable.rb +357 -0
- data/spec/lib/huge_enumerable/huge_collection_spec.rb +33 -0
- data/spec/lib/huge_enumerable/huge_combination_spec.rb +38 -0
- data/spec/lib/huge_enumerable/huge_permutation_spec.rb +38 -0
- data/spec/lib/huge_enumerable/huge_product_spec.rb +35 -0
- data/spec/lib/huge_enumerable_spec.rb +642 -0
- data/spec/spec_helper.rb +1 -0
- metadata +175 -0
@@ -0,0 +1,642 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HugeEnumerable do
|
4
|
+
|
5
|
+
let(:collection) { ('a'..'z').to_a }
|
6
|
+
|
7
|
+
subject(:enumerable) do
|
8
|
+
klass = Class.new(HugeEnumerable)
|
9
|
+
enum_collection = collection.sort
|
10
|
+
klass.define_method(:collection_size) { enum_collection.size }
|
11
|
+
klass.define_method(:fetch) { |x| enum_collection[x] }
|
12
|
+
klass.send(:public, :next_prime)
|
13
|
+
klass.send(:public, :_fetch)
|
14
|
+
klass.send(:public, :element_or_array)
|
15
|
+
klass.send(:public, :full_cycle_increment)
|
16
|
+
klass.new
|
17
|
+
end
|
18
|
+
|
19
|
+
subject(:emptied_enumerable) do
|
20
|
+
enumerable.tap do |enum|
|
21
|
+
enum.max_array_size = enum.size
|
22
|
+
enum.next_array
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context ".new" do
|
27
|
+
|
28
|
+
context "with no arguments" do
|
29
|
+
|
30
|
+
it "defaults max_array_size to a Numeric" do
|
31
|
+
enumerable.max_array_size.should be_kind_of(Numeric)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "defaults rng to #rand" do
|
35
|
+
enumerable.rng.should eq(enumerable.method(:rand))
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
context "#[]" do
|
43
|
+
|
44
|
+
context "with a positive index" do
|
45
|
+
it "returns the element from the beginning of the collection at the specified index" do
|
46
|
+
enumerable[3].should eql(collection[3])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with a negative index" do
|
51
|
+
it "returns the element from the end of the collection at the specified index" do
|
52
|
+
enumerable[-3].should eql(collection[-3])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with an out of bounds index" do
|
57
|
+
it "returns nil" do
|
58
|
+
enumerable[enumerable.size + 1].should be_nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with a range" do
|
63
|
+
it "returns an array of elements corresponding to indexes within the range" do
|
64
|
+
size = collection.size + 1
|
65
|
+
test_range = (-size..size).to_a
|
66
|
+
ranges = test_range.product(test_range).map { |x| (x.first..x.last) }
|
67
|
+
arrays = ranges.map { |range| [enumerable[range], collection[range]] }
|
68
|
+
arrays.each { |x| x.first.should eq(x.last) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "with a length" do
|
73
|
+
it "returns an array of elements corresponding to starting with index and containing a maximum of length items" do
|
74
|
+
size = collection.size + 1
|
75
|
+
test_range = (-size..size).to_a
|
76
|
+
index_lengths = test_range.product(test_range).map { |x| [x.first, x.last] }
|
77
|
+
arrays = index_lengths.map do |idx_len|
|
78
|
+
index = idx_len.first
|
79
|
+
length = idx_len.last
|
80
|
+
[enumerable[index, length], collection[index, length]]
|
81
|
+
end
|
82
|
+
arrays.each { |x| x.first.should eq(x.last) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
it "relays to #_fetch for index mapping" do
|
88
|
+
enumerable.should_receive(:_fetch).at_least(:once)
|
89
|
+
enumerable[0]
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
context "#each" do
|
95
|
+
|
96
|
+
it "yields #max_array_size items" do
|
97
|
+
items = []
|
98
|
+
total_items = collection.size / 5
|
99
|
+
enumerable.max_array_size = total_items
|
100
|
+
enumerable.each { |x| items << x }
|
101
|
+
items.size.should eql(total_items)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "yields only the remaining items if there are fewer than #max_array_size" do
|
105
|
+
items = []
|
106
|
+
enumerable.max_array_size = collection.size + 1
|
107
|
+
enumerable.each { |x| items << x }
|
108
|
+
items.size.should eql(collection.size)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "relays to #_fetch for index mapping" do
|
112
|
+
enumerable.should_receive(:_fetch).at_least(:once)
|
113
|
+
enumerable.each {}
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
context "#combination" do
|
119
|
+
|
120
|
+
context "with no block" do
|
121
|
+
|
122
|
+
it "returns a new HugeCombination" do
|
123
|
+
enumerable.combination(2).should be_instance_of(HugeCombination)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
context "with a block" do
|
129
|
+
|
130
|
+
# This should really be calls the block from the combination since there is testing duplication here.
|
131
|
+
# However, knowing where the block is called from would need stack investigation or similar.
|
132
|
+
it "calls the block for each combination element" do
|
133
|
+
combo = enumerable.combination(2)
|
134
|
+
enumerable.max_array_size = combo.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
|
135
|
+
index = 0
|
136
|
+
enumerable.combination(2) do |x|
|
137
|
+
x.should eql(combo[index])
|
138
|
+
index += 1
|
139
|
+
end
|
140
|
+
index.should eql(combo.size)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "returns self" do
|
144
|
+
enumerable.combination(2) {}.should equal(enumerable)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
it "uses self a dup of itself as the enumerable" do
|
150
|
+
dupped_enumerable = enumerable.dup
|
151
|
+
enumerable.stub(:dup).and_return(dupped_enumerable)
|
152
|
+
HugeCombination.should_receive(:new).with(dupped_enumerable, 2, enumerable.max_array_size, nil)
|
153
|
+
enumerable.combination(2)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "uses the same max array size" do
|
157
|
+
enumerable.combination(2).max_array_size.should eql(enumerable.max_array_size)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "uses the same random number generator" do
|
161
|
+
enumerable.rng = Proc.new { 1 }
|
162
|
+
enumerable.combination(2).rng.should eq(enumerable.rng)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "calls reset! on the dup" do
|
166
|
+
HugeEnumerable.send(:public, :reset!)
|
167
|
+
dupped_enumerable = enumerable.dup
|
168
|
+
enumerable.stub(:dup).and_return(dupped_enumerable)
|
169
|
+
dupped_enumerable.should_receive(:reset!).and_call_original
|
170
|
+
enumerable.combination(2)
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
context "#max_array_size" do
|
177
|
+
|
178
|
+
context "not explicitly set" do
|
179
|
+
|
180
|
+
it "defaults to DEFAULT_MAX_ARRAY_SIZE if smaller than #collection_size" do
|
181
|
+
enumerable.stub(:collection_size).and_return(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE + 1)
|
182
|
+
enumerable.max_array_size.should eql(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE)
|
183
|
+
end
|
184
|
+
|
185
|
+
it "defaults to #collection_size if smaller than DEFAULT_MAX_ARRAY_SIZE" do
|
186
|
+
enumerable.stub(:collection_size).and_return(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE - 1)
|
187
|
+
enumerable.max_array_size.should eql(HugeEnumerable::DEFAULT_MAX_ARRAY_SIZE - 1)
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
context "#next_array" do
|
195
|
+
|
196
|
+
it "advances to the next array in the collection" do
|
197
|
+
size = collection.size / 5
|
198
|
+
enumerable.max_array_size = size
|
199
|
+
enumerable.next_array.should eql(collection[size...size*2])
|
200
|
+
end
|
201
|
+
|
202
|
+
it "changes #size" do
|
203
|
+
size = collection.size / 5
|
204
|
+
enumerable.max_array_size = size
|
205
|
+
enumerable.next_array
|
206
|
+
enumerable.size.should eql(collection.size - size)
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
context "#empty?" do
|
212
|
+
|
213
|
+
it "returns true if the collection has been entirely emptied by #pop, #shift, or #next_array" do
|
214
|
+
enumerable.max_array_size = collection.size
|
215
|
+
enumerable.next_array
|
216
|
+
enumerable.empty?.should be_true
|
217
|
+
end
|
218
|
+
|
219
|
+
it "returns false if the collection has been not entirely emptied by #pop, #shift, or #next_array" do
|
220
|
+
enumerable.max_array_size = collection.size - 1
|
221
|
+
enumerable.next_array
|
222
|
+
enumerable.empty?.should be_false
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
context "#permutation" do
|
228
|
+
|
229
|
+
context "with no block" do
|
230
|
+
|
231
|
+
it "returns a new HugePermutation" do
|
232
|
+
enumerable.permutation(2).should be_instance_of(HugePermutation)
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
context "with a block" do
|
238
|
+
|
239
|
+
# This should really be calls the block from the combination since there is testing duplication here.
|
240
|
+
# However, knowing where the block is called from would need stack investigation or similar.
|
241
|
+
it "calls the block for each permutation element" do
|
242
|
+
perm = enumerable.permutation(2)
|
243
|
+
enumerable.max_array_size = perm.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
|
244
|
+
index = 0
|
245
|
+
enumerable.permutation(2) do |x|
|
246
|
+
x.should eql(perm[index])
|
247
|
+
index += 1
|
248
|
+
end
|
249
|
+
index.should eql(perm.size)
|
250
|
+
end
|
251
|
+
|
252
|
+
it "returns self" do
|
253
|
+
enumerable.permutation(2) {}.should equal(enumerable)
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
it "uses self a dup of itself as the enumerable" do
|
259
|
+
dupped_enumerable = enumerable.dup
|
260
|
+
enumerable.stub(:dup).and_return(dupped_enumerable)
|
261
|
+
HugePermutation.should_receive(:new).with(dupped_enumerable, 2, enumerable.max_array_size, nil)
|
262
|
+
enumerable.permutation(2)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "uses the same max array size" do
|
266
|
+
enumerable.permutation(2).max_array_size.should eql(enumerable.max_array_size)
|
267
|
+
end
|
268
|
+
|
269
|
+
it "uses the same random number generator" do
|
270
|
+
enumerable.rng = Proc.new { 1 }
|
271
|
+
enumerable.permutation(2).rng.should eq(enumerable.rng)
|
272
|
+
end
|
273
|
+
|
274
|
+
it "calls reset! on the dup" do
|
275
|
+
HugeEnumerable.send(:public, :reset!)
|
276
|
+
dupped_enumerable = enumerable.dup
|
277
|
+
enumerable.stub(:dup).and_return(dupped_enumerable)
|
278
|
+
dupped_enumerable.should_receive(:reset!).and_call_original
|
279
|
+
enumerable.permutation(2)
|
280
|
+
end
|
281
|
+
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
context "#pop" do
|
286
|
+
|
287
|
+
context "on a non empty collection" do
|
288
|
+
context "with no parameter" do
|
289
|
+
it "returns the next element from the end of the collection" do
|
290
|
+
enumerable.pop.should eql(collection.pop)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context "with a parameter" do
|
295
|
+
it "returns an array of the next N elements from the end of the collection" do
|
296
|
+
enumerable.pop(3).should eql(collection.pop(3))
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
it "removes the elements from the end of the collection" do
|
301
|
+
enumerable.pop
|
302
|
+
enumerable.pop(3)
|
303
|
+
enumerable.to_a.should eql(collection[0..-5])
|
304
|
+
end
|
305
|
+
|
306
|
+
it "changes #size" do
|
307
|
+
enumerable.pop
|
308
|
+
enumerable.pop(3)
|
309
|
+
enumerable.size.should eql(collection.size - 4)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "does not harm the original collection" do
|
313
|
+
original = collection.dup
|
314
|
+
enumerable.pop
|
315
|
+
enumerable.pop(3)
|
316
|
+
collection.should eql(original)
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
it "depends on #(private)element_or_array" do
|
322
|
+
enumerable.should_receive(:element_or_array).twice.and_call_original
|
323
|
+
enumerable.pop
|
324
|
+
enumerable.pop(3)
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
context "#product" do
|
330
|
+
|
331
|
+
context "with no block" do
|
332
|
+
|
333
|
+
it "returns a new HugeProduct" do
|
334
|
+
enumerable.product([]).should be_instance_of(HugeProduct)
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
|
339
|
+
context "with a block" do
|
340
|
+
|
341
|
+
# This should really be calls the block from the combination since there is testing duplication here.
|
342
|
+
# However, knowing where the block is called from would need stack investigation or similar.
|
343
|
+
it "calls the block for each product element" do
|
344
|
+
other_enumerable = [1, 2, 3]
|
345
|
+
prod = enumerable.product(other_enumerable)
|
346
|
+
enumerable.max_array_size = prod.size # Hrm. Perhaps having to do this is a reason to have it call collection_each
|
347
|
+
index = 0
|
348
|
+
enumerable.product(other_enumerable) do |x|
|
349
|
+
x.should eql(prod[index])
|
350
|
+
index += 1
|
351
|
+
end
|
352
|
+
index.should eql(prod.size)
|
353
|
+
end
|
354
|
+
|
355
|
+
it "returns self" do
|
356
|
+
enumerable.product([]) {}.should equal(enumerable)
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|
360
|
+
|
361
|
+
it "uses self a dup of itself as the enumerable" do
|
362
|
+
dupped_enumerable = enumerable.dup
|
363
|
+
enumerable.stub(:dup).and_return(dupped_enumerable)
|
364
|
+
other_enumerable = []
|
365
|
+
HugeProduct.should_receive(:new).with(dupped_enumerable, other_enumerable, enumerable.max_array_size, nil)
|
366
|
+
enumerable.product(other_enumerable)
|
367
|
+
end
|
368
|
+
|
369
|
+
it "calls dup on the other enumerable if it is a HugeEnumerable" do
|
370
|
+
other_enumerable = HugeCollection.new([])
|
371
|
+
other_enumerable.should_receive(:dup).and_call_original
|
372
|
+
enumerable.product(other_enumerable)
|
373
|
+
end
|
374
|
+
|
375
|
+
it "calls reset on the other enumerable if it is a HugeEnumerable" do
|
376
|
+
HugeEnumerable.send(:public, :reset!)
|
377
|
+
other_enumerable = HugeCollection.new([])
|
378
|
+
dupped_enumerable = other_enumerable.dup
|
379
|
+
other_enumerable.stub(:dup).and_return(dupped_enumerable)
|
380
|
+
dupped_enumerable.should_receive(:reset!).and_call_original
|
381
|
+
enumerable.product(other_enumerable)
|
382
|
+
end
|
383
|
+
|
384
|
+
it "does not call dup on the other enumerable if it is a HugeEnumerable" do
|
385
|
+
other_enumerable = []
|
386
|
+
other_enumerable.should_not_receive(:dup)
|
387
|
+
enumerable.product(other_enumerable)
|
388
|
+
end
|
389
|
+
|
390
|
+
it "uses the same max array size" do
|
391
|
+
enumerable.product([]).max_array_size.should eql(enumerable.max_array_size)
|
392
|
+
end
|
393
|
+
|
394
|
+
it "uses the same random number generator" do
|
395
|
+
enumerable.rng = Proc.new { 1 }
|
396
|
+
enumerable.product([]).rng.should eq(enumerable.rng)
|
397
|
+
end
|
398
|
+
|
399
|
+
it "calls reset! on the dup" do
|
400
|
+
HugeEnumerable.send(:public, :reset!)
|
401
|
+
dupped_enumerable = enumerable.dup
|
402
|
+
enumerable.stub(:dup).and_return(dupped_enumerable)
|
403
|
+
dupped_enumerable.should_receive(:reset!).and_call_original
|
404
|
+
enumerable.product([])
|
405
|
+
end
|
406
|
+
|
407
|
+
end
|
408
|
+
|
409
|
+
context "#sample" do
|
410
|
+
|
411
|
+
context "on an non empty collection" do
|
412
|
+
context "with no arguments" do
|
413
|
+
it "returns a single element from the collection" do
|
414
|
+
collection.include?(enumerable.sample).should be_true
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
context "with size argument" do
|
419
|
+
it "returns N elements from the collection" do
|
420
|
+
samples = enumerable.sample(3)
|
421
|
+
samples.should have(3).items
|
422
|
+
samples.all? { |item| collection.include?(item) }.should be_true
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
it "returns elements from the collection in a pseudo random pattern" do
|
427
|
+
enumerable.sample(enumerable.size).should_not eq(collection)
|
428
|
+
end
|
429
|
+
|
430
|
+
it "visits each element exactly once before repeating" do
|
431
|
+
samples = []
|
432
|
+
enumerable.size.times { samples << enumerable.sample }
|
433
|
+
samples.uniq.should have(collection.size).items
|
434
|
+
end
|
435
|
+
|
436
|
+
it "does not reorder the original collection" do
|
437
|
+
original = collection.dup
|
438
|
+
enumerable.sample
|
439
|
+
enumerable.sample(3)
|
440
|
+
collection.should eql(original)
|
441
|
+
end
|
442
|
+
|
443
|
+
end
|
444
|
+
|
445
|
+
it "depends on #(private)element_or_array" do
|
446
|
+
enumerable.should_receive(:element_or_array).twice.and_call_original
|
447
|
+
enumerable.sample
|
448
|
+
enumerable.sample(3)
|
449
|
+
end
|
450
|
+
|
451
|
+
end
|
452
|
+
|
453
|
+
context "#shift" do
|
454
|
+
|
455
|
+
context "on a non empty collection" do
|
456
|
+
context "with no parameter" do
|
457
|
+
it "returns the next element from the beginning of the collection" do
|
458
|
+
enumerable.shift.should eql(collection.shift)
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
context "with a parameter" do
|
463
|
+
it "returns an array of the next N elements from the beginning of the collection" do
|
464
|
+
enumerable.shift(3).should eql(collection.shift(3))
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
it "removes the elements from the beginning of the collection" do
|
469
|
+
enumerable.shift
|
470
|
+
enumerable.shift(3)
|
471
|
+
enumerable.to_a.should eql(collection[4..-1])
|
472
|
+
end
|
473
|
+
|
474
|
+
it "changes #size" do
|
475
|
+
enumerable.shift
|
476
|
+
enumerable.shift(3)
|
477
|
+
enumerable.size.should eql(collection.size - 4)
|
478
|
+
end
|
479
|
+
|
480
|
+
it "does not harm the original collection" do
|
481
|
+
original = collection.dup
|
482
|
+
enumerable.shift
|
483
|
+
enumerable.shift(3)
|
484
|
+
collection.should eql(original)
|
485
|
+
end
|
486
|
+
|
487
|
+
end
|
488
|
+
|
489
|
+
it "depends on #(private)element_or_array" do
|
490
|
+
enumerable.should_receive(:element_or_array).twice.and_call_original
|
491
|
+
enumerable.shift
|
492
|
+
enumerable.shift(3)
|
493
|
+
end
|
494
|
+
|
495
|
+
end
|
496
|
+
|
497
|
+
context "#shuffle" do
|
498
|
+
|
499
|
+
it "returns a new HugeEnumerable" do
|
500
|
+
enumerable.shuffle.should_not equal(enumerable)
|
501
|
+
end
|
502
|
+
|
503
|
+
end
|
504
|
+
|
505
|
+
context "#shuffle!" do
|
506
|
+
|
507
|
+
it "randomly alters the order of the sequence" do
|
508
|
+
fake_random = Proc.new { |x| 2 % x }
|
509
|
+
enumerable.max_array_size = enumerable.size
|
510
|
+
original = enumerable.to_a
|
511
|
+
enumerable.shuffle!(fake_random)
|
512
|
+
shuffle1 = enumerable.to_a
|
513
|
+
fake_random = Proc.new { |x| 3 % x }
|
514
|
+
enumerable.shuffle!(fake_random)
|
515
|
+
shuffle2 = enumerable.to_a
|
516
|
+
original.should_not eq(shuffle1)
|
517
|
+
original.should_not eq(shuffle2)
|
518
|
+
shuffle1.should_not eq(shuffle2)
|
519
|
+
end
|
520
|
+
|
521
|
+
it "contains all of the original elements" do
|
522
|
+
enumerable.shuffle!.to_a.sort.should eq(collection)
|
523
|
+
end
|
524
|
+
|
525
|
+
it "does noy alter the original collection" do
|
526
|
+
original = collection.dup
|
527
|
+
enumerable.max_array_size = enumerable.size
|
528
|
+
enumerable.shuffle!
|
529
|
+
enumerable.to_a.should_not eq(original)
|
530
|
+
original.should eq(collection)
|
531
|
+
end
|
532
|
+
|
533
|
+
end
|
534
|
+
|
535
|
+
context "#size" do
|
536
|
+
|
537
|
+
it "returns the current size of the collection" do
|
538
|
+
enumerable.size.should eql(collection.size)
|
539
|
+
end
|
540
|
+
|
541
|
+
end
|
542
|
+
|
543
|
+
context "#(private)next_prime" do
|
544
|
+
|
545
|
+
it "should return the next prime following any integer" do
|
546
|
+
primes = Prime.first(100)
|
547
|
+
x = 0
|
548
|
+
until primes.empty?
|
549
|
+
enumerable.next_prime(x).should eq(primes.first)
|
550
|
+
x += 1
|
551
|
+
primes.shift if x.prime?
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
end
|
556
|
+
|
557
|
+
context "#(private)_fetch" do
|
558
|
+
|
559
|
+
context "with an index outside the range of (sequence_start..sequence_end)" do
|
560
|
+
it "should never relay to fetch" do
|
561
|
+
enumerable.should_not_receive(:fetch)
|
562
|
+
enumerable._fetch(-1 * enumerable.size - 1)
|
563
|
+
enumerable._fetch(enumerable.size)
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
context "with an index inside the range of (sequence_start..sequence_end)" do
|
568
|
+
it "should relay to fetch" do
|
569
|
+
enumerable.should_receive(:fetch).twice
|
570
|
+
enumerable._fetch(0)
|
571
|
+
enumerable._fetch(enumerable.size - 1)
|
572
|
+
end
|
573
|
+
|
574
|
+
it "should map the relative index to an absolute index before calling fetch" do
|
575
|
+
enumerable.shift(3)
|
576
|
+
enumerable.stub(:shuffle_index) { |index| index + 2 }
|
577
|
+
enumerable.should_receive(:fetch).with(5)
|
578
|
+
enumerable._fetch(0)
|
579
|
+
end
|
580
|
+
|
581
|
+
end
|
582
|
+
|
583
|
+
end
|
584
|
+
|
585
|
+
context "#(private)element_or_array" do
|
586
|
+
|
587
|
+
let(:block) { Proc.new { 1 } }
|
588
|
+
|
589
|
+
context "on a non empty collection" do
|
590
|
+
context "with no parameter" do
|
591
|
+
it "returns a single element" do
|
592
|
+
enumerable.element_or_array { block.call }.should eql(1)
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
context "with a nil parameter" do
|
597
|
+
it "returns a single element" do
|
598
|
+
enumerable.element_or_array(nil, &block).should eql(1)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
context "with a non nil parameter" do
|
603
|
+
it "returns an array of N elements" do
|
604
|
+
enumerable.element_or_array(3, &block).should eql([1, 1, 1])
|
605
|
+
end
|
606
|
+
|
607
|
+
it "will not return more items than remain in the collection" do
|
608
|
+
size = enumerable.size
|
609
|
+
enumerable.element_or_array(size + 1, &block).should have(size).items
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
context "on an empty collection" do
|
615
|
+
context "with no parameter" do
|
616
|
+
it "returns nil" do
|
617
|
+
emptied_enumerable.element_or_array(&:block).should be_nil
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
context "with a parameter" do
|
622
|
+
it "returns an empty array" do
|
623
|
+
emptied_enumerable.element_or_array(3, &block).should eql([])
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
it "raises an exception if the parameter is negative" do
|
629
|
+
expect { enumerable.element_or_array(-1, &block) }.to raise_error(ArgumentError, 'negative array size')
|
630
|
+
end
|
631
|
+
|
632
|
+
end
|
633
|
+
|
634
|
+
context "#(private)full_cycle_increment" do
|
635
|
+
|
636
|
+
it "must never return a value equal to the domain size" do
|
637
|
+
(0...100).to_a.all? { |x| enumerable.full_cycle_increment(x) != x }.should be_true
|
638
|
+
end
|
639
|
+
|
640
|
+
end
|
641
|
+
|
642
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'huge_enumerable'
|