chars 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +1 -1
- data/.github/workflows/ruby.yml +28 -0
- data/.gitignore +8 -0
- data/.yardopts +1 -1
- data/ChangeLog.md +33 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +1 -1
- data/README.md +130 -50
- data/Rakefile +6 -30
- data/benchmarks/compare.rb +16 -0
- data/benchmarks/substrings.rb +25 -0
- data/chars.gemspec +39 -105
- data/gemspec.yml +9 -5
- data/lib/chars/char_set.rb +371 -162
- data/lib/chars/chars.rb +86 -21
- data/lib/chars/extensions/integer.rb +34 -17
- data/lib/chars/extensions/string.rb +34 -17
- data/lib/chars/string_enumerator.rb +98 -0
- data/lib/chars/version.rb +2 -2
- data/spec/char_set_spec.rb +623 -110
- data/spec/chars_spec.rb +183 -27
- data/spec/extensions/integer_spec.rb +18 -18
- data/spec/extensions/string_spec.rb +20 -18
- data/spec/spec_helper.rb +0 -2
- data/spec/string_enumerator_spec.rb +99 -0
- metadata +57 -77
data/spec/char_set_spec.rb
CHANGED
@@ -1,194 +1,707 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'chars/
|
2
|
+
require 'chars/char_set'
|
3
|
+
|
4
|
+
require 'securerandom'
|
3
5
|
|
4
6
|
describe Chars::CharSet do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@strings = @string_range.to_a
|
7
|
+
let(:integer_range) { (0x41..0x43) }
|
8
|
+
let(:string_range) { ('A'..'Z') }
|
9
|
+
let(:integers) { integer_range.to_a }
|
10
|
+
let(:strings) { string_range.to_a }
|
10
11
|
|
11
|
-
|
12
|
-
end
|
12
|
+
subject { described_class.new(*strings) }
|
13
13
|
|
14
14
|
describe "#initialize" do
|
15
|
-
|
16
|
-
|
15
|
+
context "when given multiple String arguments" do
|
16
|
+
subject { described_class.new(*strings) }
|
17
17
|
|
18
|
-
|
18
|
+
it "must populate the char set with the String's chars" do
|
19
|
+
expect(strings.all? { |s| subject.include_char?(s) }).to be(true)
|
20
|
+
end
|
19
21
|
end
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
context "when given an Array of Strings" do
|
24
|
+
subject { described_class.new(strings) }
|
23
25
|
|
24
|
-
|
26
|
+
it "must populate the char set with the String's chars" do
|
27
|
+
expect(strings.all? { |s| subject.include_char?(s) }).to be(true)
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
|
-
|
28
|
-
|
31
|
+
context "when given a Range of Strings" do
|
32
|
+
subject { described_class.new(string_range) }
|
29
33
|
|
30
|
-
|
34
|
+
it "must populate the char set by enumerating over the String's chars" do
|
35
|
+
expect(strings.all? { |s| subject.include_char?(s) }).to be(true)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
|
-
|
34
|
-
|
39
|
+
context "when given multiple Integer arguments" do
|
40
|
+
subject { described_class.new(*integers) }
|
35
41
|
|
36
|
-
|
42
|
+
it "must populate the char set using the Integers as bytes" do
|
43
|
+
expect(integers.all? { |i| subject.include?(i) }).to be(true)
|
44
|
+
end
|
37
45
|
end
|
38
46
|
|
39
|
-
|
40
|
-
|
47
|
+
context "when given an Array of Integers" do
|
48
|
+
subject { described_class.new(integers) }
|
41
49
|
|
42
|
-
|
50
|
+
it "must populate the char set using the Integers as bytes" do
|
51
|
+
expect(integers.all? { |i| subject.include?(i) }).to be(true)
|
52
|
+
end
|
43
53
|
end
|
44
54
|
|
45
|
-
|
46
|
-
|
55
|
+
context "when given a Range of Integers" do
|
56
|
+
subject { described_class.new(integer_range) }
|
47
57
|
|
48
|
-
|
58
|
+
it "must populate the char set by enumerating over the Integer's bytes" do
|
59
|
+
expect(integers.all? { |i| subject.include?(i) }).to be(true)
|
60
|
+
end
|
49
61
|
end
|
50
62
|
end
|
51
63
|
|
52
|
-
|
53
|
-
|
64
|
+
describe "#include_char?" do
|
65
|
+
it "should include Strings" do
|
66
|
+
expect(subject.include_char?('A')).to be(true)
|
67
|
+
end
|
54
68
|
end
|
55
69
|
|
56
|
-
|
57
|
-
|
70
|
+
describe "#include?" do
|
71
|
+
it "should include Integers" do
|
72
|
+
expect(subject).to include(0x41)
|
73
|
+
end
|
58
74
|
end
|
59
75
|
|
60
|
-
|
61
|
-
|
76
|
+
describe "#select_bytes" do
|
77
|
+
it "should be able to select bytes" do
|
78
|
+
sub_set = subject.select_bytes { |c| c <= 0x42 }
|
62
79
|
|
63
|
-
|
80
|
+
expect(sub_set).to be == [0x41, 0x42]
|
81
|
+
end
|
64
82
|
end
|
65
83
|
|
66
|
-
|
67
|
-
|
84
|
+
describe "#select_chars" do
|
85
|
+
it "should be able to select chars" do
|
86
|
+
sub_set = subject.select_chars { |c| c <= 'B' }
|
68
87
|
|
69
|
-
|
88
|
+
expect(sub_set).to be == ['A', 'B']
|
89
|
+
end
|
70
90
|
end
|
71
91
|
|
72
|
-
|
73
|
-
|
74
|
-
|
92
|
+
describe "#random_byte" do
|
93
|
+
it "should return a random byte" do
|
94
|
+
expect(subject).to include(subject.random_byte)
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when given random: rng" do
|
98
|
+
let(:rng) { SecureRandom }
|
75
99
|
|
76
|
-
|
77
|
-
|
100
|
+
it "must call the #rand method" do
|
101
|
+
expect(rng).to receive(:rand).with(subject.length).and_return(1)
|
102
|
+
|
103
|
+
subject.random_byte(random: rng)
|
104
|
+
end
|
105
|
+
end
|
78
106
|
end
|
79
107
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
108
|
+
describe "#random_char" do
|
109
|
+
it "should return a random char" do
|
110
|
+
expect(subject.include_char?(subject.random_char)).to be(true)
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when given random: rng" do
|
114
|
+
let(:rng) { SecureRandom }
|
115
|
+
|
116
|
+
it "must call the #rand method" do
|
117
|
+
expect(rng).to receive(:rand).with(subject.length).and_return(1)
|
118
|
+
|
119
|
+
subject.random_char(random: rng)
|
120
|
+
end
|
121
|
+
end
|
84
122
|
end
|
85
123
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
124
|
+
describe "#each_random_byte" do
|
125
|
+
let(:n) { 10 }
|
126
|
+
|
127
|
+
it "should iterate over n random bytes" do
|
128
|
+
expect(subject.each_random_byte(n).all? { |b|
|
129
|
+
subject.include?(b)
|
130
|
+
}).to be(true)
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when given random: rng" do
|
134
|
+
let(:rng) { SecureRandom }
|
135
|
+
|
136
|
+
it "must call the #rand method n times" do
|
137
|
+
n.times do
|
138
|
+
expect(rng).to receive(:rand).with(subject.length).and_return(1)
|
139
|
+
end
|
140
|
+
|
141
|
+
subject.each_random_byte(n, random: rng) { |b| }
|
142
|
+
end
|
143
|
+
end
|
90
144
|
end
|
91
145
|
|
92
|
-
|
93
|
-
|
146
|
+
describe "#each_random_char" do
|
147
|
+
let(:n) { 10 }
|
94
148
|
|
95
|
-
|
149
|
+
it "should iterate over n random chars" do
|
150
|
+
expect(subject.each_random_char(10).all? { |c|
|
151
|
+
subject.include_char?(c)
|
152
|
+
}).to be(true)
|
153
|
+
end
|
154
|
+
|
155
|
+
context "when given random: rng" do
|
156
|
+
let(:rng) { SecureRandom }
|
157
|
+
|
158
|
+
it "must call the #rand method n times" do
|
159
|
+
n.times do
|
160
|
+
expect(rng).to receive(:rand).with(subject.length).and_return(1)
|
161
|
+
end
|
162
|
+
|
163
|
+
subject.each_random_char(n, random: rng) { |c| }
|
164
|
+
end
|
165
|
+
end
|
96
166
|
end
|
97
167
|
|
98
|
-
|
99
|
-
|
168
|
+
describe "#random_bytes" do
|
169
|
+
context "when given an Integer" do
|
170
|
+
let(:n) { 10 }
|
171
|
+
|
172
|
+
it "should return a random Array of bytes" do
|
173
|
+
random_bytes = subject.random_bytes(n)
|
174
|
+
|
175
|
+
expect(random_bytes.all? { |b| subject.include?(b) }).to be(true)
|
176
|
+
end
|
177
|
+
|
178
|
+
context "when given random: rng" do
|
179
|
+
let(:rng) { SecureRandom }
|
180
|
+
|
181
|
+
it "must call the #rand method n times" do
|
182
|
+
n.times do
|
183
|
+
expect(rng).to receive(:rand).with(subject.length).and_return(1)
|
184
|
+
end
|
185
|
+
|
186
|
+
subject.random_bytes(n, random: rng)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "when given a Range of lengths" do
|
192
|
+
let(:lengths) { 5..10 }
|
193
|
+
|
194
|
+
it "should return a random Array of bytes with a varying length" do
|
195
|
+
random_bytes = subject.random_bytes(lengths)
|
100
196
|
|
101
|
-
|
197
|
+
expect(random_bytes.length).to be_between(lengths.begin, lengths.end)
|
198
|
+
expect(random_bytes.all? { |b| subject.include?(b) }).to be(true)
|
199
|
+
end
|
200
|
+
|
201
|
+
context "and when given random: rng" do
|
202
|
+
let(:rng) { SecureRandom }
|
203
|
+
|
204
|
+
it "must pass random: to Range#sample" do
|
205
|
+
expect(rng).to receive(:rand).and_return(rand(lengths)).at_least(:once)
|
206
|
+
|
207
|
+
random_bytes = subject.random_bytes(lengths, random: rng)
|
208
|
+
|
209
|
+
expect(random_bytes.length).to be_between(lengths.begin, lengths.end)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
102
213
|
end
|
103
214
|
|
104
|
-
|
105
|
-
|
215
|
+
describe "#random_chars" do
|
216
|
+
context "when given an Integer" do
|
217
|
+
let(:n) { 10 }
|
218
|
+
|
219
|
+
it "should return a random Array of chars" do
|
220
|
+
random_chars = subject.random_chars(n)
|
221
|
+
|
222
|
+
expect(random_chars.all? { |c| subject.include_char?(c) }).to be(true)
|
223
|
+
end
|
224
|
+
|
225
|
+
context "when given random: rng" do
|
226
|
+
let(:rng) { SecureRandom }
|
227
|
+
|
228
|
+
it "must call the #rand method n times" do
|
229
|
+
n.times do
|
230
|
+
expect(rng).to receive(:rand).with(subject.length).and_return(rand(n))
|
231
|
+
end
|
106
232
|
|
107
|
-
|
108
|
-
|
233
|
+
subject.random_chars(n, random: rng)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context "when given a Range of lengths" do
|
239
|
+
let(:lengths) { 5..10 }
|
240
|
+
|
241
|
+
it "should return a random Array of chars with a varying length" do
|
242
|
+
random_chars = subject.random_chars(lengths)
|
243
|
+
|
244
|
+
expect(random_chars.length).to be_between(lengths.begin, lengths.end)
|
245
|
+
expect(random_chars.all? { |c| subject.include_char?(c) }).to be(true)
|
246
|
+
end
|
247
|
+
|
248
|
+
context "and when given random: rng" do
|
249
|
+
let(:rng) { SecureRandom }
|
250
|
+
|
251
|
+
it "must pass random: to Range#sample" do
|
252
|
+
expect(rng).to receive(:rand).and_return(rand(lengths)).at_least(:once)
|
253
|
+
|
254
|
+
random_chars = subject.random_chars(lengths, random: rng)
|
255
|
+
|
256
|
+
expect(random_chars.length).to be_between(lengths.begin, lengths.end)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
109
260
|
end
|
110
261
|
|
111
|
-
|
112
|
-
|
262
|
+
describe "#random_string" do
|
263
|
+
context "when given an Integer" do
|
264
|
+
let(:n) { 10 }
|
113
265
|
|
114
|
-
|
115
|
-
|
266
|
+
it "should return a random String of chars" do
|
267
|
+
random_string = subject.random_string(n)
|
268
|
+
|
269
|
+
expect(random_string.chars.all? { |b|
|
270
|
+
subject.include_char?(b)
|
271
|
+
}).to be(true)
|
272
|
+
end
|
273
|
+
|
274
|
+
context "when given random: rng" do
|
275
|
+
let(:rng) { SecureRandom }
|
276
|
+
|
277
|
+
it "must call the #rand method n times" do
|
278
|
+
expect(rng).to receive(:rand).with(subject.length).and_return(rand(n)).at_least(:once)
|
279
|
+
|
280
|
+
subject.random_chars(n, random: rng)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context "when given a Range of lengths" do
|
286
|
+
let(:lengths) { 5..10 }
|
287
|
+
|
288
|
+
it "should return a random String of chars with a varying length" do
|
289
|
+
random_string = subject.random_string(lengths)
|
290
|
+
|
291
|
+
expect(random_string.length).to be_between(lengths.begin, lengths.end)
|
292
|
+
expect(random_string.chars.all? { |b|
|
293
|
+
subject.include_char?(b)
|
294
|
+
}).to be(true)
|
295
|
+
end
|
296
|
+
|
297
|
+
context "and when given random: rng" do
|
298
|
+
let(:rng) { SecureRandom }
|
299
|
+
|
300
|
+
it "must pass random: to Range#sample" do
|
301
|
+
expect(rng).to receive(:rand).and_return(rand(lengths)).at_least(:once)
|
302
|
+
|
303
|
+
random_string = subject.random_string(lengths, random: rng)
|
304
|
+
|
305
|
+
expect(random_string.length).to be_between(lengths.begin, lengths.end)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
116
309
|
end
|
310
|
+
|
311
|
+
describe "#random_distinct_bytes" do
|
312
|
+
context "when given an Integer" do
|
313
|
+
let(:n) { 10 }
|
314
|
+
|
315
|
+
it "should return a random Array of unique bytes" do
|
316
|
+
random_bytes = subject.random_distinct_bytes(n)
|
317
|
+
|
318
|
+
expect(random_bytes.length).to eq(n)
|
319
|
+
expect(random_bytes.uniq).to be == random_bytes
|
320
|
+
expect(random_bytes.all? { |b| subject.include_byte?(b) }).to be(true)
|
321
|
+
end
|
322
|
+
|
323
|
+
context "when given random: rng" do
|
324
|
+
let(:rng) { SecureRandom }
|
117
325
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
326
|
+
it "must call the Array#shuffle with random: rng" do
|
327
|
+
expect_any_instance_of(Array).to receive(:shuffle).with(random: rng).and_return(subject.bytes.shuffle)
|
328
|
+
|
329
|
+
subject.random_distinct_bytes(n, random: rng)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
context "with a Range of lengths" do
|
335
|
+
let(:lengths) { 5..10 }
|
336
|
+
|
337
|
+
it "should return a random Array of unique bytes with a varying length" do
|
338
|
+
random_bytes = subject.random_distinct_bytes(lengths)
|
339
|
+
|
340
|
+
expect(random_bytes.uniq).to be == random_bytes
|
341
|
+
expect(random_bytes.length).to be_between(lengths.begin, lengths.end)
|
342
|
+
expect(random_bytes.all? { |b| subject.include_byte?(b) }).to be(true)
|
343
|
+
end
|
344
|
+
|
345
|
+
context "and when given random: rng" do
|
346
|
+
let(:rng) { SecureRandom }
|
347
|
+
|
348
|
+
it "must pass random: to Range#sample" do
|
349
|
+
expect(rng).to receive(:rand).and_return(rand(lengths)).at_least(:once)
|
350
|
+
|
351
|
+
random_bytes = subject.random_bytes(lengths, random: rng)
|
352
|
+
|
353
|
+
expect(random_bytes.length).to be_between(lengths.begin, lengths.end)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
122
357
|
end
|
123
358
|
|
124
|
-
|
125
|
-
|
359
|
+
describe "#random_distinct_chars" do
|
360
|
+
let(:n) { 10 }
|
361
|
+
|
362
|
+
it "should return a random Array of unique chars" do
|
363
|
+
random_chars = subject.random_distinct_chars(n)
|
364
|
+
|
365
|
+
expect(random_chars.uniq).to be == random_chars
|
366
|
+
expect(random_chars.all? { |c| subject.include_char?(c) }).to be(true)
|
367
|
+
end
|
368
|
+
|
369
|
+
context "when given random: rng" do
|
370
|
+
let(:rng) { SecureRandom }
|
371
|
+
|
372
|
+
it "must call the Array#shuffle with random: rng" do
|
373
|
+
expect_any_instance_of(Array).to receive(:shuffle).with(random: rng).and_return(subject.bytes.shuffle(random: rng))
|
374
|
+
|
375
|
+
subject.random_distinct_bytes(n, random: rng)
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
context "when given a Range of lengths" do
|
380
|
+
let(:lengths) { 5..10 }
|
126
381
|
|
127
|
-
|
128
|
-
|
382
|
+
it "should return a random Array of unique chars with a varying length" do
|
383
|
+
random_chars = subject.random_distinct_chars(lengths)
|
384
|
+
|
385
|
+
expect(random_chars.uniq).to be == random_chars
|
386
|
+
expect(random_chars.length).to be_between(lengths.begin, lengths.end)
|
387
|
+
expect(random_chars.all? { |c| subject.include_char?(c) }).to be(true)
|
388
|
+
end
|
389
|
+
|
390
|
+
context "and when given random: rng" do
|
391
|
+
let(:rng) { SecureRandom }
|
392
|
+
|
393
|
+
it "must pass random: to Range#sample" do
|
394
|
+
expect(rng).to receive(:rand).and_return(rand(lengths)).at_least(:once)
|
395
|
+
|
396
|
+
random_bytes = subject.random_bytes(lengths, random: rng)
|
397
|
+
|
398
|
+
expect(random_bytes.length).to be_between(lengths.begin, lengths.end)
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
129
402
|
end
|
130
|
-
|
131
|
-
it "should return a random Array of unique bytes" do
|
132
|
-
bytes = @char_set.random_distinct_bytes(10)
|
133
403
|
|
134
|
-
|
135
|
-
|
404
|
+
describe "#==" do
|
405
|
+
it "should be able to be compared with another set of chars" do
|
406
|
+
expect(subject).to be == described_class['A'..'Z']
|
407
|
+
end
|
136
408
|
end
|
137
409
|
|
138
|
-
|
139
|
-
chars
|
410
|
+
describe "#|" do
|
411
|
+
it "should be able to be unioned with another set of chars" do
|
412
|
+
super_set = (subject | described_class['D'])
|
140
413
|
|
141
|
-
|
142
|
-
|
414
|
+
expect(super_set).to be_kind_of(described_class)
|
415
|
+
expect(super_set).to be == described_class['A'..'Z', 'D']
|
416
|
+
end
|
143
417
|
end
|
144
418
|
|
145
|
-
|
146
|
-
|
419
|
+
describe "#-" do
|
420
|
+
it "should be able to be removed from another set of chars" do
|
421
|
+
sub_set = (subject - described_class['B'])
|
422
|
+
|
423
|
+
expect(sub_set).to be_kind_of(described_class)
|
424
|
+
expect(sub_set).to be_subset(subject)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
describe "#each_substring_with_index" do
|
429
|
+
subject { described_class.new(['A', 'B', 'C']) }
|
430
|
+
|
431
|
+
let(:string) { "....AAAA....BBBB....CCCC...." }
|
432
|
+
|
433
|
+
it "must yield each matching substring and index" do
|
434
|
+
expect { |b|
|
435
|
+
subject.each_substring_with_index(string,&b)
|
436
|
+
}.to yield_successive_args(
|
437
|
+
["AAAA", string.index("AAAA")],
|
438
|
+
["BBBB", string.index("BBBB")],
|
439
|
+
["CCCC", string.index("CCCC")]
|
440
|
+
)
|
441
|
+
end
|
442
|
+
|
443
|
+
context "when the string begins with a matching substring" do
|
444
|
+
let(:string) { "AAAA...." }
|
147
445
|
|
148
|
-
|
149
|
-
|
150
|
-
|
446
|
+
it "must yield the first matching substring" do
|
447
|
+
expect(subject.each_substring_with_index(string).first).to eq(
|
448
|
+
["AAAA", 0]
|
449
|
+
)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
context "when the string ends with a matching substring" do
|
454
|
+
let(:string) { "AAAA....BBBB....CCCC" }
|
455
|
+
|
456
|
+
it "must yield the last matching substring" do
|
457
|
+
expect(subject.each_substring_with_index(string).to_a.last).to eq(
|
458
|
+
["CCCC", string.rindex("CCCC")]
|
459
|
+
)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
context "when the entire string is a matching substring" do
|
464
|
+
let(:string) { "AAAAAAAA" }
|
465
|
+
|
466
|
+
it "must yield the entire string" do
|
467
|
+
expect { |b|
|
468
|
+
subject.each_substring_with_index(string,&b)
|
469
|
+
}.to yield_successive_args( [string, 0] )
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
context "when the matching substrings are shorter than the min_length" do
|
474
|
+
let(:min_length) { 2 }
|
475
|
+
|
476
|
+
let(:string) { "AA..B...CC.."}
|
477
|
+
|
478
|
+
it "must ignore the substrings shorter than min_length" do
|
479
|
+
expect { |b|
|
480
|
+
subject.each_substring_with_index(string, min_length: min_length, &b)
|
481
|
+
}.to yield_successive_args(
|
482
|
+
["AA", string.index("AA")],
|
483
|
+
["CC", string.index("CC")]
|
484
|
+
)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
context "when min_length 0" do
|
489
|
+
let(:min_length) { 0 }
|
490
|
+
|
491
|
+
let(:string) { "A.BB..CCC..."}
|
492
|
+
|
493
|
+
it "must yield all matching substrings, regardless of length" do
|
494
|
+
expect { |b|
|
495
|
+
subject.each_substring_with_index(string, min_length: min_length, &b)
|
496
|
+
}.to yield_successive_args(
|
497
|
+
["A", string.index("A")],
|
498
|
+
["BB", string.index("BB")],
|
499
|
+
["CCC", string.index("CCC")]
|
500
|
+
)
|
501
|
+
end
|
502
|
+
end
|
151
503
|
end
|
152
504
|
|
153
|
-
|
154
|
-
|
505
|
+
describe "#substrings_with_indexes" do
|
506
|
+
subject { described_class.new(['A', 'B', 'C']) }
|
507
|
+
|
508
|
+
let(:string) { "....AAAA....BBBB....CCCC...." }
|
155
509
|
|
156
|
-
|
157
|
-
|
158
|
-
|
510
|
+
it "must return the Array of substrings and their indexes" do
|
511
|
+
expect(subject.substrings_with_indexes(string)).to eq(
|
512
|
+
[
|
513
|
+
["AAAA", string.index("AAAA")],
|
514
|
+
["BBBB", string.index("BBBB")],
|
515
|
+
["CCCC", string.index("CCCC")]
|
516
|
+
]
|
517
|
+
)
|
518
|
+
end
|
159
519
|
end
|
160
520
|
|
161
|
-
|
162
|
-
|
521
|
+
describe "#each_substring(&block : (String) ->)" do
|
522
|
+
subject { described_class.new(['A', 'B', 'C']) }
|
523
|
+
|
524
|
+
let(:string) { "....AAAA....BBBB....CCCC...." }
|
525
|
+
|
526
|
+
it "must yield each matching substring" do
|
527
|
+
expect { |b|
|
528
|
+
subject.each_substring(string,&b)
|
529
|
+
}.to yield_successive_args(
|
530
|
+
"AAAA",
|
531
|
+
"BBBB",
|
532
|
+
"CCCC"
|
533
|
+
)
|
534
|
+
end
|
163
535
|
end
|
164
536
|
|
165
|
-
|
166
|
-
|
537
|
+
describe "#substrings" do
|
538
|
+
subject { described_class.new(['A', 'B', 'C']) }
|
539
|
+
|
540
|
+
let(:string) { "....AAAA....BBBB....CCCC...." }
|
167
541
|
|
168
|
-
|
169
|
-
|
542
|
+
it "must return the Array of matching substrings" do
|
543
|
+
expect(subject.substrings(string)).to eq(
|
544
|
+
[
|
545
|
+
"AAAA",
|
546
|
+
"BBBB",
|
547
|
+
"CCCC"
|
548
|
+
]
|
549
|
+
)
|
550
|
+
end
|
170
551
|
end
|
171
552
|
|
172
|
-
|
173
|
-
|
553
|
+
describe "#strings_in" do
|
554
|
+
subject { described_class.new(['A', 'B', 'C']) }
|
555
|
+
|
556
|
+
let(:string) { "AAAA....BBBB....CCCC...." }
|
557
|
+
|
558
|
+
context "when given a block" do
|
559
|
+
it "should find sub-strings from a String belonging to the char set" do
|
560
|
+
expect { |b|
|
561
|
+
subject.strings_in(string,&b)
|
562
|
+
}.to yield_successive_args(
|
563
|
+
"AAAA",
|
564
|
+
"BBBB",
|
565
|
+
"CCCC"
|
566
|
+
)
|
567
|
+
end
|
568
|
+
|
569
|
+
it "must find sub-strings of a minimum length of 4" do
|
570
|
+
expect { |b|
|
571
|
+
subject.strings_in("A...BBB...CCCC",&b)
|
572
|
+
}.to yield_successive_args("CCCC")
|
573
|
+
end
|
574
|
+
|
575
|
+
context "and when the whole string matches the char set" do
|
576
|
+
it "should find one sub-string from a String belonging to the char set" do
|
577
|
+
expect { |b|
|
578
|
+
subject.strings_in("AAAA",&b)
|
579
|
+
}.to yield_successive_args("AAAA")
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
context "when the :length option is given" do
|
584
|
+
it "must find sub-strings of the given minimum length" do
|
585
|
+
expect { |b|
|
586
|
+
subject.strings_in("AAAA...BBBB...CCCCC", :length => 5, &b)
|
587
|
+
}.to yield_successive_args("CCCCC")
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
context "and when the block takes two arguments" do
|
592
|
+
it "must yield the sub-strings and their indexes" do
|
593
|
+
yielded_args = []
|
594
|
+
|
595
|
+
subject.strings_in(string) do |substring,index|
|
596
|
+
yielded_args << [substring, index]
|
597
|
+
end
|
598
|
+
|
599
|
+
expect(yielded_args).to eq(
|
600
|
+
[
|
601
|
+
['AAAA', string.index('AAAA')],
|
602
|
+
['BBBB', string.index('BBBB')],
|
603
|
+
['CCCC', string.index('CCCC')]
|
604
|
+
]
|
605
|
+
)
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
174
609
|
|
175
|
-
|
176
|
-
|
610
|
+
context "when no block is given" do
|
611
|
+
it "must return an Array of sub-strings" do
|
612
|
+
expect(subject.strings_in(string)).to eq(
|
613
|
+
[
|
614
|
+
"AAAA",
|
615
|
+
"BBBB",
|
616
|
+
"CCCC"
|
617
|
+
]
|
618
|
+
)
|
619
|
+
end
|
620
|
+
|
621
|
+
context "when given the :offset option" do
|
622
|
+
it "must return a Hash of the substrings and their indexes" do
|
623
|
+
expect(subject.strings_in(string, :offsets => true)).to eq(
|
624
|
+
{
|
625
|
+
"AAAA" => string.index("AAAA"),
|
626
|
+
"BBBB" => string.index("BBBB"),
|
627
|
+
"CCCC" => string.index("CCCC")
|
628
|
+
}
|
629
|
+
)
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
177
633
|
end
|
178
634
|
|
179
|
-
|
180
|
-
|
635
|
+
describe "#each_string" do
|
636
|
+
let(:length) { 2 }
|
637
|
+
|
638
|
+
let(:expected_strings) do
|
639
|
+
Chars::StringEnumerator.new(subject,length).to_a
|
640
|
+
end
|
641
|
+
|
642
|
+
context "when a block is given" do
|
643
|
+
it "must enumerate through the strings belonging to the character set of the desired length" do
|
644
|
+
expect { |b|
|
645
|
+
subject.each_string_of_length(length,&b)
|
646
|
+
}.to yield_successive_args(*expected_strings)
|
647
|
+
end
|
648
|
+
|
649
|
+
context "when given a Range of lengths" do
|
650
|
+
let(:length) { 1..2 }
|
651
|
+
|
652
|
+
let(:expected_strings) do
|
653
|
+
Chars::StringEnumerator.new(subject,1).to_a +
|
654
|
+
Chars::StringEnumerator.new(subject,2).to_a
|
655
|
+
end
|
656
|
+
|
657
|
+
it "must yield strings of lengths in the Range of lengths" do
|
658
|
+
expect { |b|
|
659
|
+
subject.each_string_of_length(length,&b)
|
660
|
+
}.to yield_successive_args(*expected_strings)
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
context "when given an Array of lengths" do
|
665
|
+
let(:length) { [1,2] }
|
666
|
+
|
667
|
+
let(:expected_strings) do
|
668
|
+
Chars::StringEnumerator.new(subject,1).to_a +
|
669
|
+
Chars::StringEnumerator.new(subject,2).to_a
|
670
|
+
end
|
671
|
+
|
672
|
+
it "must yield strings of lengths in the Range of lengths" do
|
673
|
+
expect { |b|
|
674
|
+
subject.each_string_of_length(length,&b)
|
675
|
+
}.to yield_successive_args(*expected_strings)
|
676
|
+
end
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
context "when no block is given" do
|
681
|
+
it "must return an Enumerator" do
|
682
|
+
expect(subject.each_string_of_length(length)).to be_kind_of(Enumerator)
|
683
|
+
expect(subject.each_string_of_length(length).to_a).to eq(expected_strings)
|
684
|
+
end
|
685
|
+
end
|
181
686
|
end
|
182
687
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
688
|
+
describe "#strings_of_length" do
|
689
|
+
let(:length) { 2 }
|
690
|
+
|
691
|
+
let(:expected_strings) do
|
692
|
+
Chars::StringEnumerator.new(subject,length).to_a
|
693
|
+
end
|
694
|
+
|
695
|
+
it "must return an Enumerator" do
|
696
|
+
expect(subject.strings_of_length(length)).to be_kind_of(Enumerator)
|
697
|
+
expect(subject.strings_of_length(length).to_a).to eq(expected_strings)
|
698
|
+
end
|
188
699
|
end
|
189
700
|
|
190
|
-
|
191
|
-
|
192
|
-
|
701
|
+
describe "#===" do
|
702
|
+
it "should determine if a String is made up of the characters from the char set" do
|
703
|
+
expect(subject).to be === "AABCBAA"
|
704
|
+
expect(subject).to_not be === "AA!!EE"
|
705
|
+
end
|
193
706
|
end
|
194
707
|
end
|