chars 0.2.4 → 0.3.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 +4 -4
- data/.github/workflows/ruby.yml +5 -5
- data/ChangeLog.md +32 -11
- data/Gemfile +2 -0
- data/README.md +64 -13
- data/benchmarks/compare.rb +1 -1
- data/benchmarks/substrings.rb +25 -0
- data/gemspec.yml +1 -0
- data/lib/chars/char_set.rb +291 -56
- data/lib/chars/chars.rb +42 -6
- data/lib/chars/string_enumerator.rb +98 -0
- data/lib/chars/version.rb +1 -1
- data/spec/char_set_spec.rb +415 -31
- data/spec/chars_spec.rb +183 -27
- data/spec/extensions/string_spec.rb +1 -1
- data/spec/string_enumerator_spec.rb +99 -0
- metadata +7 -4
- data/benchmarks/strings_in.rb +0 -23
data/lib/chars/char_set.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'chars/string_enumerator'
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
|
3
5
|
module Chars
|
@@ -42,6 +44,12 @@ module Chars
|
|
42
44
|
#
|
43
45
|
# Creates a new {CharSet}.
|
44
46
|
#
|
47
|
+
# @param [Array<String, Integer, Enumerable>] arguments
|
48
|
+
# The chars for the CharSet.
|
49
|
+
#
|
50
|
+
# @return [CharSet]
|
51
|
+
# The new character set.
|
52
|
+
#
|
45
53
|
# @see #initialize
|
46
54
|
#
|
47
55
|
# @since 0.2.1
|
@@ -172,21 +180,30 @@ module Chars
|
|
172
180
|
#
|
173
181
|
# Returns a random byte from the {CharSet}.
|
174
182
|
#
|
183
|
+
# @param [Random, SecureRandom] random
|
184
|
+
# The random number generator to use.
|
185
|
+
#
|
175
186
|
# @return [Integer]
|
176
187
|
# A random byte value.
|
177
188
|
#
|
178
|
-
def random_byte
|
179
|
-
self.entries[rand(self.length)]
|
189
|
+
def random_byte(random: Random)
|
190
|
+
self.entries[random.rand(self.length)]
|
180
191
|
end
|
181
192
|
|
182
193
|
#
|
183
194
|
# Returns a random character from the {CharSet}.
|
184
195
|
#
|
196
|
+
# @param [Hash{Symbol => Object}] kwargs
|
197
|
+
# Additional keyword arguments.
|
198
|
+
#
|
199
|
+
# @option kwargs [Random, SecureRandom] :random
|
200
|
+
# The random number generator to use.
|
201
|
+
#
|
185
202
|
# @return [String]
|
186
203
|
# A random char value.
|
187
204
|
#
|
188
|
-
def random_char
|
189
|
-
@chars[random_byte]
|
205
|
+
def random_char(**kwargs)
|
206
|
+
@chars[random_byte(**kwargs)]
|
190
207
|
end
|
191
208
|
|
192
209
|
#
|
@@ -195,6 +212,12 @@ module Chars
|
|
195
212
|
# @param [Integer] n
|
196
213
|
# Specifies how many times to pass a random byte to the block.
|
197
214
|
#
|
215
|
+
# @param [Hash{Symbol => Object}] kwargs
|
216
|
+
# Additional keyword arguments.
|
217
|
+
#
|
218
|
+
# @option kwargs [Random, SecureRandom] :random
|
219
|
+
# The random number generator to use.
|
220
|
+
#
|
198
221
|
# @yield [byte]
|
199
222
|
# The block will receive the random bytes.
|
200
223
|
#
|
@@ -204,10 +227,12 @@ module Chars
|
|
204
227
|
# @return [Enumerator]
|
205
228
|
# If no block is given, an enumerator object will be returned.
|
206
229
|
#
|
207
|
-
def each_random_byte(n,&block)
|
208
|
-
return enum_for(__method__,n) unless block_given?
|
230
|
+
def each_random_byte(n,**kwargs,&block)
|
231
|
+
return enum_for(__method__,n,**kwargs) unless block_given?
|
209
232
|
|
210
|
-
n.times
|
233
|
+
n.times do
|
234
|
+
yield random_byte(**kwargs)
|
235
|
+
end
|
211
236
|
return nil
|
212
237
|
end
|
213
238
|
|
@@ -217,6 +242,12 @@ module Chars
|
|
217
242
|
# @param [Integer] n
|
218
243
|
# Specifies how many times to pass a random character to the block.
|
219
244
|
#
|
245
|
+
# @param [Hash{Symbol => Object}] kwargs
|
246
|
+
# Additional keyword arguments.
|
247
|
+
#
|
248
|
+
# @option kwargs [Random, SecureRandom] :random
|
249
|
+
# The random number generator to use.
|
250
|
+
#
|
220
251
|
# @yield [char]
|
221
252
|
# The block will receive the random characters.
|
222
253
|
#
|
@@ -226,10 +257,12 @@ module Chars
|
|
226
257
|
# @return [Enumerator]
|
227
258
|
# If no block is given, an enumerator object will be returned.
|
228
259
|
#
|
229
|
-
def each_random_char(n,&block)
|
230
|
-
return enum_for(__method__,n) unless block_given?
|
260
|
+
def each_random_char(n,**kwargs,&block)
|
261
|
+
return enum_for(__method__,n,**kwargs) unless block_given?
|
231
262
|
|
232
|
-
each_random_byte(n)
|
263
|
+
each_random_byte(n,**kwargs) do |byte|
|
264
|
+
yield @chars[byte]
|
265
|
+
end
|
233
266
|
end
|
234
267
|
|
235
268
|
#
|
@@ -238,17 +271,24 @@ module Chars
|
|
238
271
|
# @param [Integer, Array, Range] length
|
239
272
|
# The length of the Array of random bytes.
|
240
273
|
#
|
274
|
+
# @param [Random, SecureRandom] random
|
275
|
+
# The random number generator to use.
|
276
|
+
#
|
241
277
|
# @return [Array<Integer>]
|
242
278
|
# The randomly selected bytes.
|
243
279
|
#
|
244
|
-
def random_bytes(length)
|
280
|
+
def random_bytes(length, random: Random)
|
245
281
|
case length
|
246
282
|
when Array
|
247
|
-
Array.new(length.sample)
|
283
|
+
Array.new(length.sample(random: random)) do
|
284
|
+
random_byte(random: random)
|
285
|
+
end
|
248
286
|
when Range
|
249
|
-
Array.new(rand(length))
|
287
|
+
Array.new(random.rand(length)) do
|
288
|
+
random_byte(random: random)
|
289
|
+
end
|
250
290
|
else
|
251
|
-
Array.new(length) { random_byte }
|
291
|
+
Array.new(length) { random_byte(random: random) }
|
252
292
|
end
|
253
293
|
end
|
254
294
|
|
@@ -258,17 +298,20 @@ module Chars
|
|
258
298
|
# @param [Integer, Array, Range] length
|
259
299
|
# The length of the Array of random non-repeating bytes.
|
260
300
|
#
|
301
|
+
# @param [Random, SecureRandom] random
|
302
|
+
# The random number generator to use.
|
303
|
+
#
|
261
304
|
# @return [Array<Integer>]
|
262
305
|
# The randomly selected non-repeating bytes.
|
263
306
|
#
|
264
|
-
def random_distinct_bytes(length)
|
265
|
-
shuffled_bytes = bytes.shuffle
|
307
|
+
def random_distinct_bytes(length, random: Random)
|
308
|
+
shuffled_bytes = bytes.shuffle(random: random)
|
266
309
|
|
267
310
|
case length
|
268
311
|
when Array
|
269
|
-
shuffled_bytes[0,length.sample]
|
312
|
+
shuffled_bytes[0,length.sample(random: random)]
|
270
313
|
when Range
|
271
|
-
shuffled_bytes[0,rand(length)]
|
314
|
+
shuffled_bytes[0,random.rand(length)]
|
272
315
|
else
|
273
316
|
shuffled_bytes[0,length]
|
274
317
|
end
|
@@ -280,11 +323,17 @@ module Chars
|
|
280
323
|
# @param [Integer, Array, Range] length
|
281
324
|
# The length of the Array of random characters.
|
282
325
|
#
|
326
|
+
# @param [Hash{Symbol => Object}] kwargs
|
327
|
+
# Additional keyword arguments.
|
328
|
+
#
|
329
|
+
# @option kwargs [Random, SecureRandom] :random
|
330
|
+
# The random number generator to use.
|
331
|
+
#
|
283
332
|
# @return [Array<String>]
|
284
333
|
# The randomly selected characters.
|
285
334
|
#
|
286
|
-
def random_chars(length)
|
287
|
-
random_bytes(length).map { |byte| @chars[byte] }
|
335
|
+
def random_chars(length,**kwargs)
|
336
|
+
random_bytes(length,**kwargs).map { |byte| @chars[byte] }
|
288
337
|
end
|
289
338
|
|
290
339
|
#
|
@@ -294,13 +343,19 @@ module Chars
|
|
294
343
|
# @param [Integer, Array, Range] length
|
295
344
|
# The length of the String of random characters.
|
296
345
|
#
|
346
|
+
# @param [Hash{Symbol => Object}] kwargs
|
347
|
+
# Additional keyword arguments.
|
348
|
+
#
|
349
|
+
# @option kwargs [Random, SecureRandom] :random
|
350
|
+
# The random number generator to use.
|
351
|
+
#
|
297
352
|
# @return [String]
|
298
353
|
# The String of randomly selected characters.
|
299
354
|
#
|
300
355
|
# @see random_chars
|
301
356
|
#
|
302
|
-
def random_string(length)
|
303
|
-
random_chars(length).join
|
357
|
+
def random_string(length,**kwargs)
|
358
|
+
random_chars(length,**kwargs).join
|
304
359
|
end
|
305
360
|
|
306
361
|
#
|
@@ -310,11 +365,17 @@ module Chars
|
|
310
365
|
# @param [Integer, Array, Range] length
|
311
366
|
# The length of the Array of random non-repeating characters.
|
312
367
|
#
|
368
|
+
# @param [Hash{Symbol => Object}] kwargs
|
369
|
+
# Additional keyword arguments.
|
370
|
+
#
|
371
|
+
# @option kwargs [Random, SecureRandom] :random
|
372
|
+
# The random number generator to use.
|
373
|
+
#
|
313
374
|
# @return [Array<Integer>]
|
314
375
|
# The randomly selected non-repeating characters.
|
315
376
|
#
|
316
|
-
def random_distinct_chars(length)
|
317
|
-
random_distinct_bytes(length).map { |byte| @chars[byte] }
|
377
|
+
def random_distinct_chars(length,**kwargs)
|
378
|
+
random_distinct_bytes(length,**kwargs).map { |byte| @chars[byte] }
|
318
379
|
end
|
319
380
|
|
320
381
|
#
|
@@ -324,13 +385,161 @@ module Chars
|
|
324
385
|
# @param [Integer, Array, Range] length
|
325
386
|
# The length of the String of random non-repeating characters.
|
326
387
|
#
|
388
|
+
# @param [Hash{Symbol => Object}] kwargs
|
389
|
+
# Additional keyword arguments.
|
390
|
+
#
|
391
|
+
# @option kwargs [Random, SecureRandom] :random
|
392
|
+
# The random number generator to use.
|
393
|
+
#
|
327
394
|
# @return [String]
|
328
395
|
# The String of randomly selected non-repeating characters.
|
329
396
|
#
|
330
397
|
# @see random_distinct_chars
|
331
398
|
#
|
332
|
-
def random_distinct_string(length)
|
333
|
-
random_distinct_chars(length).join
|
399
|
+
def random_distinct_string(length,**kwargs)
|
400
|
+
random_distinct_chars(length,**kwargs).join
|
401
|
+
end
|
402
|
+
|
403
|
+
#
|
404
|
+
# Enumerates over all substrings and their indices within the given string,
|
405
|
+
# of minimum length and that are made up of characters from the {CharSet}.
|
406
|
+
#
|
407
|
+
# @param [String] data
|
408
|
+
# The data to find sub-strings within.
|
409
|
+
#
|
410
|
+
# @param [Integer] min_length
|
411
|
+
# The minimum length of sub-strings found within the given data.
|
412
|
+
#
|
413
|
+
# @yield [match, index]
|
414
|
+
# The given block will be passed every matched sub-string and it's index.
|
415
|
+
#
|
416
|
+
# @yield [String] match
|
417
|
+
# A sub-string containing the characters from the {CharSet}.
|
418
|
+
#
|
419
|
+
# @yield [Integer] index
|
420
|
+
# The index the sub-string was found at.
|
421
|
+
#
|
422
|
+
# @return [Enumerator]
|
423
|
+
# If no block is given, an Enumerator object will be returned.
|
424
|
+
#
|
425
|
+
# @since 0.3.0
|
426
|
+
#
|
427
|
+
def each_substring_with_index(data, min_length: 4)
|
428
|
+
unless block_given?
|
429
|
+
return enum_for(__method__,data, min_length: min_length)
|
430
|
+
end
|
431
|
+
|
432
|
+
return if data.size < min_length
|
433
|
+
|
434
|
+
index = 0
|
435
|
+
|
436
|
+
match_start = nil
|
437
|
+
match_end = nil
|
438
|
+
|
439
|
+
while index < data.size
|
440
|
+
unless match_start
|
441
|
+
if self.include_char?(data[index])
|
442
|
+
match_start = index
|
443
|
+
end
|
444
|
+
else
|
445
|
+
unless self.include_char?(data[index])
|
446
|
+
match_end = index
|
447
|
+
match_length = (match_end - match_start)
|
448
|
+
|
449
|
+
if match_length >= min_length
|
450
|
+
match = data[match_start,match_length]
|
451
|
+
|
452
|
+
yield match, match_start
|
453
|
+
end
|
454
|
+
|
455
|
+
match_start = match_end = nil
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
index += 1
|
460
|
+
end
|
461
|
+
|
462
|
+
# yield the remaining match
|
463
|
+
if match_start
|
464
|
+
yield data[match_start, data.size - match_start], match_start
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
#
|
469
|
+
# Returns an Array of all substrings and their indices within the given
|
470
|
+
# string, of minimum length and that are made up of characters from the
|
471
|
+
# {CharSet}.
|
472
|
+
#
|
473
|
+
# @param [String] data
|
474
|
+
# The data to find sub-strings within.
|
475
|
+
#
|
476
|
+
# @param [Hash{Symbol => Object}] kwargs
|
477
|
+
# Keyword arguments for {#each_substring_with_index}.
|
478
|
+
#
|
479
|
+
# @option kwargs [Integer] :min_length
|
480
|
+
# The minimum length of sub-strings found within the given data.
|
481
|
+
#
|
482
|
+
# @return [Array<(String, Integer)>]
|
483
|
+
# Tthe array of substrings and their indices within the given `data`.
|
484
|
+
#
|
485
|
+
# @see #each_substring_with_index
|
486
|
+
#
|
487
|
+
# @since 0.3.0
|
488
|
+
#
|
489
|
+
def substrings_with_indexes(data,**kwargs)
|
490
|
+
each_substring_with_index(data,**kwargs).to_a
|
491
|
+
end
|
492
|
+
|
493
|
+
#
|
494
|
+
# Enumerates over all substrings within the given string, of minimum length
|
495
|
+
# and that are made up of characters from the {CharSet}.
|
496
|
+
#
|
497
|
+
# @param [String] data
|
498
|
+
# The data to find sub-strings within.
|
499
|
+
#
|
500
|
+
# @param [Hash{Symbol => Object}] kwargs
|
501
|
+
# Keyword arguments for {#each_substring_with_index}.
|
502
|
+
#
|
503
|
+
# @option kwargs [Integer] :min_length
|
504
|
+
# The minimum length of sub-strings found within the given data.
|
505
|
+
#
|
506
|
+
# @return [Enumerator]
|
507
|
+
# If no block is given, an Enumerator object will be returned.
|
508
|
+
#
|
509
|
+
# @see #each_substring_with_index
|
510
|
+
#
|
511
|
+
# @since 0.3.0
|
512
|
+
#
|
513
|
+
def each_substring(data,**kwargs)
|
514
|
+
return enum_for(__method__,data,**kwargs) unless block_given?
|
515
|
+
|
516
|
+
each_substring_with_index(data,**kwargs) do |substring,index|
|
517
|
+
yield substring
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
#
|
522
|
+
# Returns an Array of all substrings within the given string,
|
523
|
+
# of minimum length and that are made up of characters from the {CharSet}.
|
524
|
+
#
|
525
|
+
# @param [String] data
|
526
|
+
# The data to find sub-strings within.
|
527
|
+
#
|
528
|
+
# @param [Hash{Symbol => Object}] kwargs
|
529
|
+
# Keyword arguments for {#each_substring_with_index}.
|
530
|
+
#
|
531
|
+
# @option kwargs [Integer] :min_length
|
532
|
+
# The minimum length of sub-strings found within the given data.
|
533
|
+
#
|
534
|
+
# @see #each_substring
|
535
|
+
#
|
536
|
+
# @return [Array<String>]
|
537
|
+
# Tthe array of substrings within the given `data`.
|
538
|
+
#
|
539
|
+
# @since 0.3.0
|
540
|
+
#
|
541
|
+
def substrings(data,**kwargs)
|
542
|
+
each_substring(data,**kwargs).to_a
|
334
543
|
end
|
335
544
|
|
336
545
|
#
|
@@ -364,49 +573,75 @@ module Chars
|
|
364
573
|
# @return [Array, Hash]
|
365
574
|
# If no block is given, an Array or Hash of sub-strings is returned.
|
366
575
|
#
|
576
|
+
# @deprecated
|
577
|
+
# Use {#each_substring_with_index}, {#substrings_with_index},
|
578
|
+
# {#each_substring}, or {#substrings} instead.
|
579
|
+
#
|
367
580
|
def strings_in(data,options={},&block)
|
581
|
+
kwargs = {min_length: options.fetch(:length,4)}
|
582
|
+
|
368
583
|
unless block
|
369
584
|
if options[:offsets]
|
370
|
-
|
371
|
-
block = lambda { |offset,substring| found[offset] = substring }
|
585
|
+
return Hash[substrings_with_indexes(data,**kwargs)]
|
372
586
|
else
|
373
|
-
|
374
|
-
block = lambda { |substring| found << substring }
|
587
|
+
return substrings(data,**kwargs)
|
375
588
|
end
|
376
|
-
|
377
|
-
strings_in(data,options,&block)
|
378
|
-
return found
|
379
589
|
end
|
380
590
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
sub_index = (index + min_length)
|
389
|
-
|
390
|
-
while self.include_char?(data[sub_index,1])
|
391
|
-
sub_index += 1
|
392
|
-
end
|
393
|
-
|
394
|
-
match = data[index...sub_index]
|
591
|
+
case block.arity
|
592
|
+
when 2
|
593
|
+
each_substring_with_index(data,**kwargs,&block)
|
594
|
+
else
|
595
|
+
each_substring(data,**kwargs,&block)
|
596
|
+
end
|
597
|
+
end
|
395
598
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
599
|
+
#
|
600
|
+
# Enumerates through every possible string belonging to the {CharSet} and
|
601
|
+
# of the given length.
|
602
|
+
#
|
603
|
+
# @param [Range, Array, Integer] length
|
604
|
+
# The desired length(s) of each string.
|
605
|
+
#
|
606
|
+
# @yield [string]
|
607
|
+
# The given block will be passed each sequential string.
|
608
|
+
#
|
609
|
+
# @yieldparam [String] string
|
610
|
+
# A string belonging to {#char_set} and `length` long.
|
611
|
+
#
|
612
|
+
# @return [Enumerator]
|
613
|
+
# If no block is given, an Enumerator will be returned.
|
614
|
+
#
|
615
|
+
# @since 0.3.0
|
616
|
+
#
|
617
|
+
def each_string_of_length(length,&block)
|
618
|
+
return enum_for(__method__,length) unless block
|
402
619
|
|
403
|
-
|
404
|
-
|
405
|
-
|
620
|
+
case length
|
621
|
+
when Range, Array
|
622
|
+
length.each do |len|
|
623
|
+
StringEnumerator.new(self,len).each(&block)
|
406
624
|
end
|
625
|
+
else
|
626
|
+
StringEnumerator.new(self,length).each(&block)
|
407
627
|
end
|
408
628
|
end
|
409
629
|
|
630
|
+
#
|
631
|
+
# Returns an Enumerator that enumerates through every possible string
|
632
|
+
# belonging to the {CharSEt} and of the given length.
|
633
|
+
#
|
634
|
+
# @param [Range, Array, Integer] length
|
635
|
+
# The desired length(s) of each string.
|
636
|
+
#
|
637
|
+
# @return [Enumerator]
|
638
|
+
#
|
639
|
+
# @see #each_string
|
640
|
+
#
|
641
|
+
def strings_of_length(length)
|
642
|
+
each_string_of_length(length)
|
643
|
+
end
|
644
|
+
|
410
645
|
#
|
411
646
|
# Creates a new CharSet object by unioning the {CharSet} with another
|
412
647
|
# {CharSet}.
|
data/lib/chars/chars.rb
CHANGED
@@ -6,6 +6,11 @@ module Chars
|
|
6
6
|
# The numeric decimal character set
|
7
7
|
NUMERIC = CharSet['0'..'9']
|
8
8
|
|
9
|
+
# @see NUMERIC
|
10
|
+
#
|
11
|
+
# @since 0.3.0
|
12
|
+
DIGITS = NUMERIC
|
13
|
+
|
9
14
|
# The octal character set
|
10
15
|
OCTAL = CharSet['0'..'7']
|
11
16
|
|
@@ -31,7 +36,7 @@ module Chars
|
|
31
36
|
ALPHA_NUMERIC = ALPHA | NUMERIC
|
32
37
|
|
33
38
|
# The punctuation character set
|
34
|
-
PUNCTUATION = CharSet['
|
39
|
+
PUNCTUATION = CharSet['\'', '"', '`', ',', ';', ':', '~', '-',
|
35
40
|
'(', ')', '[', ']', '{', '}', '.', '?', '!']
|
36
41
|
|
37
42
|
# The symbolic character set
|
@@ -41,7 +46,11 @@ module Chars
|
|
41
46
|
]
|
42
47
|
|
43
48
|
# The space character set
|
44
|
-
|
49
|
+
#
|
50
|
+
# @since 0.3.0
|
51
|
+
WHITESPACE = CharSet[' ', "\f", "\n", "\r", "\t", "\v"]
|
52
|
+
|
53
|
+
SPACE = WHITESPACE
|
45
54
|
|
46
55
|
# The set of printable characters (not including spaces)
|
47
56
|
VISIBLE = ALPHA_NUMERIC | CharSet[
|
@@ -51,7 +60,7 @@ module Chars
|
|
51
60
|
]
|
52
61
|
|
53
62
|
# The set of printable characters (including spaces)
|
54
|
-
PRINTABLE = ALPHA_NUMERIC | PUNCTUATION | SYMBOLS |
|
63
|
+
PRINTABLE = ALPHA_NUMERIC | PUNCTUATION | SYMBOLS | CharSet[' ']
|
55
64
|
|
56
65
|
# The control-char character set
|
57
66
|
CONTROL = CharSet[0..0x1f, 0x7f]
|
@@ -74,6 +83,20 @@ module Chars
|
|
74
83
|
NUMERIC
|
75
84
|
end
|
76
85
|
|
86
|
+
#
|
87
|
+
# Alias for {numeric}.
|
88
|
+
#
|
89
|
+
# @return [CharSet]
|
90
|
+
# The decimal-digit character set.
|
91
|
+
#
|
92
|
+
# @see numeric
|
93
|
+
#
|
94
|
+
# @since 0.3.0
|
95
|
+
#
|
96
|
+
def self.digits
|
97
|
+
numeric
|
98
|
+
end
|
99
|
+
|
77
100
|
#
|
78
101
|
# The octal-digit character set.
|
79
102
|
#
|
@@ -195,15 +218,28 @@ module Chars
|
|
195
218
|
end
|
196
219
|
|
197
220
|
#
|
198
|
-
# The
|
221
|
+
# The whitespace character set.
|
199
222
|
#
|
200
223
|
# @return [CharSet]
|
201
|
-
# The
|
224
|
+
# The whitespace character set.
|
202
225
|
#
|
203
226
|
# @see SPACE
|
204
227
|
#
|
228
|
+
# @since 0.3.0
|
229
|
+
#
|
230
|
+
def self.whitespace
|
231
|
+
WHITESPACE
|
232
|
+
end
|
233
|
+
|
234
|
+
#
|
235
|
+
# The whitespace character set.
|
236
|
+
#
|
237
|
+
# @return [CharSet]
|
238
|
+
#
|
239
|
+
# @see #whitespace
|
240
|
+
#
|
205
241
|
def self.space
|
206
|
-
|
242
|
+
whitespace
|
207
243
|
end
|
208
244
|
|
209
245
|
#
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Chars
|
2
|
+
#
|
3
|
+
# Enumerates through every possible string belonging to a character set and
|
4
|
+
# of a given length.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
#
|
8
|
+
# @since 0.3.0
|
9
|
+
#
|
10
|
+
class StringEnumerator
|
11
|
+
|
12
|
+
include Enumerable
|
13
|
+
|
14
|
+
# The character set to generate the strings from.
|
15
|
+
#
|
16
|
+
# @return [CharSet]
|
17
|
+
attr_reader :char_set
|
18
|
+
|
19
|
+
# The desired length of each string.
|
20
|
+
#
|
21
|
+
# @return [Integer]
|
22
|
+
attr_reader :length
|
23
|
+
|
24
|
+
#
|
25
|
+
# Initializes the string enumerator.
|
26
|
+
#
|
27
|
+
# @param [Chars::CharSet] char_set
|
28
|
+
# The character set to generate the strings from.
|
29
|
+
#
|
30
|
+
# @param [Integer] length
|
31
|
+
# The desired length of each string.
|
32
|
+
#
|
33
|
+
def initialize(char_set,length)
|
34
|
+
@char_set = char_set
|
35
|
+
@length = length
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Enumerates through every possible string belonging to {#char_set} and
|
40
|
+
# {#length} long.
|
41
|
+
#
|
42
|
+
# @yield [string]
|
43
|
+
# The given block will be passed each sequential string.
|
44
|
+
#
|
45
|
+
# @yieldparam [String] string
|
46
|
+
# A string belonging to {#char_set} and {#length} long.
|
47
|
+
#
|
48
|
+
# @return [Enumerator]
|
49
|
+
# If no block is given, an Enumerator will be returned.
|
50
|
+
#
|
51
|
+
def each
|
52
|
+
return enum_for(__method__) unless block_given?
|
53
|
+
|
54
|
+
if @char_set.empty?
|
55
|
+
return
|
56
|
+
elsif @length == 0
|
57
|
+
yield ""
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
chars = char_set.chars
|
62
|
+
first_char = chars.first
|
63
|
+
last_char = chars.last
|
64
|
+
|
65
|
+
next_char = {}
|
66
|
+
|
67
|
+
chars.each_cons(2) do |c1,c2|
|
68
|
+
next_char[c1] = c2
|
69
|
+
end
|
70
|
+
|
71
|
+
string = String.new(first_char * @length)
|
72
|
+
|
73
|
+
last_index = @length - 1
|
74
|
+
|
75
|
+
loop do
|
76
|
+
chars.each do |c|
|
77
|
+
string[last_index] = c
|
78
|
+
|
79
|
+
yield string.dup
|
80
|
+
end
|
81
|
+
|
82
|
+
last_index.downto(0) do |i|
|
83
|
+
if string[i] == last_char
|
84
|
+
string[i] = first_char
|
85
|
+
|
86
|
+
if i == 0
|
87
|
+
return
|
88
|
+
end
|
89
|
+
else
|
90
|
+
string[i] = next_char[string[i]]
|
91
|
+
break
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/lib/chars/version.rb
CHANGED