string_pattern 1.4.0 → 1.4.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.
@@ -1,918 +1,920 @@
1
- SP_ADD_TO_RUBY = true if !defined?(SP_ADD_TO_RUBY)
2
- require_relative 'string/pattern/add_to_ruby' if SP_ADD_TO_RUBY
3
-
4
- # SP_ADD_TO_RUBY: (TrueFalse, default: true) You need to add this constant value before requiring the library if you want to modify the default.
5
- # If true it will add 'generate' and 'validate' methods to the classes: Array, String and Symbol. Also it will add 'generate' method to Kernel
6
- # aliases: 'gen' for 'generate' and 'val' for 'validate'
7
- # Examples of use:
8
- # "(,3:N,) ,3:N,-,2:N,-,2:N".split(",").generate #>(937) 980-65-05
9
- # %w{( 3:N ) 1:_ 3:N - 2:N - 2:N}.gen #>(045) 448-63-09
10
- # ["1:L", "5-10:LN", "-", "3:N"].gen #>zqWihV-746
11
- # gen("10:N") #>3433409877
12
- # "20-30:@".gen #>dkj34MljjJD-df@jfdluul.dfu
13
- # "10:L/N/[/-./%d%]".validate("12ds6f--.s") #>[:value, :string_set_not_allowed]
14
- # "20-40:@".validate(my_email)
15
- # national_chars: (Array, default: english alphabet)
16
- # Set of characters that will be used when using T pattern
17
- # optimistic: (TrueFalse, default: true)
18
- # If true it will check on the strings of the array positions if they have the pattern format and assume in that case that is a pattern.
19
- # dont_repeat: (TrueFalse, default: false)
20
- # If you want to generate for example 1000 strings and be sure all those strings are different you can set it to true
21
- class StringPattern
22
- class << self
23
- attr_accessor :national_chars, :optimistic, :dont_repeat, :cache, :cache_values
24
- end
25
- @national_chars = (('a'..'z').to_a + ('A'..'Z').to_a).join
26
- @optimistic = true
27
- @cache = Hash.new()
28
- @cache_values = Hash.new()
29
- @dont_repeat = false
30
- NUMBER_SET = ('0'..'9').to_a
31
- SPECIAL_SET = [' ', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '+', '=', '{', '}', '[', ']', "'", ';', ':', '?', '>', '<', '`', '|', '/', '"']
32
- ALPHA_SET_LOWER = ('a'..'z').to_a
33
- ALPHA_SET_CAPITAL = ('A'..'Z').to_a
34
-
35
-
36
- Pattern = Struct.new(:min_length, :max_length, :symbol_type, :required_data, :excluded_data, :data_provided,
37
- :string_set, :all_characters_set)
38
-
39
- ###############################################
40
- # Analyze the pattern supplied and returns an object of Pattern structure including:
41
- # min_length, max_length, symbol_type, required_data, excluded_data, data_provided, string_set, all_characters_set
42
- ###############################################
43
- def StringPattern.analyze(pattern, silent:false)
44
- return @cache[pattern.to_s] unless @cache[pattern.to_s].nil?
45
- min_length, max_length, symbol_type=pattern.to_s.scan(/(\d+)-(\d+):(.+)/)[0]
46
- if min_length.nil?
47
- min_length, symbol_type=pattern.to_s.scan(/^!?(\d+):(.+)/)[0]
48
- max_length=min_length
49
- if min_length.nil?
50
- puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}" unless silent
51
- return pattern.to_s
52
- end
53
- end
54
-
55
- symbol_type='!'+symbol_type if pattern.to_s[0]=='!'
56
- min_length=min_length.to_i
57
- max_length=max_length.to_i
58
-
59
- required_data=Array.new
60
- excluded_data=Array.new
61
- required=false
62
- excluded=false
63
- data_provided=Array.new
64
- a=symbol_type
65
- begin_provided=a.index('[')
66
- excluded_end_tag=false
67
- unless begin_provided.nil?
68
- c=begin_provided+1
69
- until c==a.size or (a[c..c]==']' and a[c..c+1]!=']]')
70
- if a[c..c+1]==']]'
71
- data_provided.push(']')
72
- c=c+2
73
- elsif a[c..c+1]=='%%' and !excluded then
74
- data_provided.push('%')
75
- c=c+2
76
- else
77
- if a[c..c]=='/' and !excluded
78
- if a[c..c+1]=='//'
79
- data_provided.push(a[c..c])
80
- if required
81
- required_data.push([a[c..c]])
82
- end
83
- c=c+1
84
- else
85
- if !required
86
- required=true
87
- else
88
- required=false
89
- end
90
- end
91
- else
92
- if required
93
- required_data.push([a[c..c]])
94
- else
95
- if a[c..c]=='%'
96
- if a[c..c+1]=='%%' and excluded
97
- excluded_data.push([a[c..c]])
98
- c=c+1
99
- else
100
- if !excluded
101
- excluded=true
102
- else
103
- excluded=false
104
- excluded_end_tag=true
105
- end
106
- end
107
- else
108
- if excluded
109
- excluded_data.push([a[c..c]])
110
- end
111
- end
112
-
113
- end
114
- if excluded==false and excluded_end_tag==false
115
- data_provided.push(a[c..c])
116
- end
117
- excluded_end_tag=false
118
- end
119
- c=c+1
120
- end
121
- end
122
- symbol_type=symbol_type[0..begin_provided].to_s + symbol_type[c..symbol_type.size].to_s
123
- end
124
-
125
- required=false
126
- required_symbol=''
127
- if symbol_type.include?("/")
128
- symbol_type.chars.each {|stc|
129
- if stc=='/'
130
- if !required
131
- required=true
132
- else
133
- required=false
134
- end
135
- else
136
- if required
137
- required_symbol+=stc
138
- end
139
- end
140
- }
141
- end
142
-
143
- national_set=@national_chars.chars
144
-
145
- if symbol_type.include?('L') then
146
- alpha_set = ALPHA_SET_LOWER + ALPHA_SET_CAPITAL
147
- elsif symbol_type.include?('x')
148
- alpha_set=ALPHA_SET_LOWER
149
- if symbol_type.include?('X')
150
- alpha_set = alpha_set + ALPHA_SET_CAPITAL
151
- end
152
- elsif symbol_type.include?('X')
153
- alpha_set=ALPHA_SET_CAPITAL
154
- end
155
- if symbol_type.include?('T')
156
- alpha_set=alpha_set+national_set
157
- end
158
-
159
- unless required_symbol.nil?
160
- if required_symbol.include?('x')
161
- required_data.push ALPHA_SET_LOWER
162
- end
163
- if required_symbol.include?('X')
164
- required_data.push ALPHA_SET_CAPITAL
165
- end
166
- if required_symbol.include?('L')
167
- required_data.push(ALPHA_SET_CAPITAL+ALPHA_SET_LOWER)
168
- end
169
- if required_symbol.include?('T')
170
- required_data.push national_set
171
- end
172
- required_symbol=required_symbol.downcase
173
- end
174
- string_set=Array.new
175
- all_characters_set=ALPHA_SET_CAPITAL+ALPHA_SET_LOWER+NUMBER_SET+SPECIAL_SET+data_provided+national_set
176
-
177
- if symbol_type.include?('_')
178
- unless symbol_type.include?('$')
179
- string_set.push(' ')
180
- end
181
- if required_symbol.include?('_')
182
- required_data.push([' '])
183
- end
184
- end
185
-
186
- symbol_type = symbol_type.downcase
187
-
188
- if symbol_type.include?('x') or symbol_type.include?('l') or symbol_type.include?('t')
189
- string_set = string_set + alpha_set
190
- end
191
- if symbol_type.include?('n')
192
- string_set = string_set + NUMBER_SET
193
- end
194
- if symbol_type.include?('$')
195
- string_set = string_set + SPECIAL_SET
196
- end
197
- if symbol_type.include?('*')
198
- string_set = string_set+all_characters_set
199
- end
200
- if data_provided.size!=0
201
- string_set = string_set + data_provided
202
- end
203
- unless required_symbol.empty?
204
- if required_symbol.include?('n')
205
- required_data.push NUMBER_SET
206
- end
207
- if required_symbol.include?('$')
208
- required_data.push SPECIAL_SET
209
- end
210
- end
211
- unless excluded_data.empty?
212
- string_set=string_set-excluded_data.flatten
213
- end
214
- string_set.uniq!
215
- @cache[pattern.to_s]=Pattern.new(min_length, max_length, symbol_type, required_data, excluded_data, data_provided,
216
- string_set, all_characters_set)
217
- return @cache[pattern.to_s]
218
- end
219
-
220
- ###############################################
221
- # Generate a random string based on the pattern supplied
222
- # (if SP_ADD_TO_RUBY==true, by default is true) To simplify its use it is part of the String, Array, Symbol and Kernel Ruby so can be easily used also like this:
223
- # "10-15:Ln/x/".generate #generate method on String class (alias: gen)
224
- # ['(', :'3:N', ')', :'6-8:N'].generate #generate method on Array class (alias: gen)
225
- # generate("10-15:Ln/x/") #generate Ruby Kernel method
226
- # generate(['(', :'3:N', ')', :'6-8:N']) #generate Ruby Kernel method
227
- # "(,3:N,) ,3:N,-,2:N,-,2:N".split(",").generate #>(937) #generate method on Array class (alias: gen)
228
- # %w{( 3:N ) 1:_ 3:N - 2:N - 2:N}.gen #generate method on Array class, using alias gen method
229
- # Input:
230
- # pattern: array or string of different patterns. A pattern is a string with this info:
231
- # "length:symbol_type" or "min_length-max_length:symbol_type"
232
- # In case an array supplied, the positions using a string pattern should be supplied as symbols if StringPattern.optimistic==false
233
- #
234
- # These are the possible string patterns you will be able to supply:
235
- # If at the beginning we supply the character ! the resulting string won't fulfill the pattern. This character need to be the first character of the pattern.
236
- # min_length -- minimum length of the string
237
- # max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided
238
- # symbol_type -- the type of the string we want.
239
- # you can use a combination of any ot these:
240
- # x for alpha in lowercase
241
- # X for alpha in capital letters
242
- # L for all kind of alpha in capital and lower letters
243
- # T For the national characters defined on StringPattern.national_chars
244
- # n for number
245
- # $ for special characters (includes space)
246
- # _ for space
247
- # * all characters
248
- # [characters] the characters we want. If we want to add also the ] character you have to write: ]]. If we want to add also the % character you have to write: %%
249
- # %characters% the characters we don't want on the resulting string. %% to exclude the character %
250
- # /symbols or characters/ If we want these characters to be included on the resulting string. If we want to add also the / character you have to write: //
251
- # We can supply 0 to allow empty strings, this character need to be at the beginning
252
- # If you want to include the character " use \"
253
- # If you want to include the character \ use \\
254
- # If you want to include the character [ use \[
255
- # Another uses: @ for email
256
- # Examples:
257
- # [:"6:X", :"3-8:_N"]
258
- # # it will return a string starting with 6 capital letters and then a string containing numbers and space from 3 to 8 characters, for example: "LDJKKD34 555"
259
- # [:"6-15:L_N", "fixed text", :"3:N"]
260
- # # it will return a string of 6-15 characters containing Letters-spaces-numbers, then the text: 'fixed text' and at the end a string of 3 characters containing numbers, for example: ["L_N",6,15],"fixed text",["N",3] "3 Am399 afixed text882"
261
- # "10-20:LN[=#]"
262
- # # it will return a string of 10-20 characters containing Letters and/or numbers and/or the characters = and #, for example: eiyweQFWeL#do4Vl
263
- # "30:TN_[#=]/x/"
264
- # # it will return a string of 30 characters containing national characters defined on StringPattern.national_chars and/or numbers and/or spaces and/or the characters # = and it is necessary the resultant string includes lower alpha chars. For example: HaEdQTzJ3=OtXMh1mAPqv7NCy=upLy
265
- # "10:N[%0%]"
266
- # # 10 characters length containing numbers and excluding the character 0, for example: 3523497757
267
- # "10:N[%0%/AB/]"
268
- # # 10 characters length containing numbers and excluding the character 0 and necessary to contain the characters A B, for example: 3AA4AA57BB
269
- # "!10:N[%0%/AB/]"
270
- # # it will generate a string that doesn't fulfill the pattern supplied, examples:
271
- # # a6oMQ4JK9g
272
- # # /Y<N6Aa[ae
273
- # # 3444439A34B32
274
- # "10:N[%0%/AB/]", errors: [:length]
275
- # # it will generate a string following the pattern and with the errors supplied, in this case, length, example: AB44
276
- # Output:
277
- # the generated string
278
- ###############################################
279
- def StringPattern.generate(pattern, expected_errors: [], **synonyms)
280
- tries = 0
281
- begin
282
- good_result = true
283
- tries+=1
284
- string=''
285
-
286
- expected_errors=synonyms[:errors] if synonyms.keys.include?(:errors)
287
-
288
- if expected_errors.kind_of?(Symbol)
289
- expected_errors=[expected_errors]
290
- end
291
-
292
- if pattern.kind_of?(Array)
293
- pattern.each {|pat|
294
- if pat.kind_of?(Symbol)
295
- if pat.to_s.scan(/^!?\d+-?\d*:.+/).size>0
296
- string<<StringPattern.generate(pat.to_s, expected_errors: expected_errors)
297
- else
298
- string<<pat.to_s
299
- end
300
- elsif pat.kind_of?(String) then
301
- if @optimistic and pat.to_s.scan(/^!?\d+-?\d*:.+/).size>0
302
- string<<StringPattern.generate(pat.to_s, expected_errors: expected_errors)
303
- else
304
- string<<pat
305
- end
306
- else
307
- puts "StringPattern.generate: it seems you supplied wrong array of patterns: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
308
- return ''
309
- end
310
- }
311
- return string
312
- elsif pattern.kind_of?(String) or pattern.kind_of?(Symbol)
313
- patt=StringPattern.analyze(pattern)
314
- min_length=patt.min_length
315
- max_length=patt.max_length
316
- symbol_type=patt.symbol_type
317
-
318
- required_data=patt.required_data
319
- excluded_data=patt.excluded_data
320
- string_set=patt.string_set
321
- all_characters_set=patt.all_characters_set
322
-
323
- required_chars=Array.new
324
- unless required_data.size==0
325
- required_data.each {|rd|
326
- required_chars<<rd if rd.size==1
327
- }
328
- unless excluded_data.size==0
329
- if (required_chars.flatten & excluded_data.flatten).size>0
330
- puts "pattern argument not valid on StringPattern.generate, a character cannot be required and excluded at the same time: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
331
- return ''
332
- end
333
- end
334
- end
335
- string_set_not_allowed=Array.new
336
-
337
- else
338
- puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
339
- return pattern.to_s
340
- end
341
-
342
-
343
- allow_empty=false
344
- deny_pattern=false
345
- if symbol_type[0..0]=='!'
346
- deny_pattern=true
347
- possible_errors=[:length, :value, :required_data, :excluded_data, :string_set_not_allowed]
348
- (rand(possible_errors.size)+1).times {
349
- expected_errors<<possible_errors.sample
350
- }
351
- expected_errors.uniq!
352
- if symbol_type[1..1]=='0'
353
- allow_empty=true
354
- end
355
- elsif symbol_type[0..0]=='0' then
356
- allow_empty=true
357
- end
358
-
359
- if expected_errors.include?(:min_length) or expected_errors.include?(:length) or
360
- expected_errors.include?(:max_length)
361
- allow_empty=!allow_empty
362
- elsif expected_errors.include?(:value) or
363
- expected_errors.include?(:excluded_data) or
364
- expected_errors.include?(:required_data) or
365
- expected_errors.include?(:string_set_not_allowed) and allow_empty
366
- allow_empty=false
367
- end
368
-
369
- length=min_length
370
- symbol_type_orig=symbol_type
371
-
372
- expected_errors_left=expected_errors.dup
373
-
374
- symbol_type=symbol_type_orig
375
-
376
- unless deny_pattern
377
- if required_data.size==0 and expected_errors_left.include?(:required_data)
378
- puts "required data not supplied on pattern so it won't be possible to generate a wrong string. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
379
- return ''
380
- end
381
-
382
- if excluded_data.size==0 and expected_errors_left.include?(:excluded_data)
383
- puts "excluded data not supplied on pattern so it won't be possible to generate a wrong string. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
384
- return ''
385
- end
386
-
387
- if expected_errors_left.include?(:string_set_not_allowed)
388
- string_set_not_allowed=all_characters_set-string_set
389
- if string_set_not_allowed.size==0 then
390
- puts "all characters are allowed so it won't be possible to generate a wrong string. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
391
- return ''
392
- end
393
- end
394
- end
395
-
396
- if expected_errors_left.include?(:min_length) or
397
- expected_errors_left.include?(:max_length) or
398
- expected_errors_left.include?(:length)
399
- if expected_errors_left.include?(:min_length) or
400
- (min_length>0 and expected_errors_left.include?(:length) and rand(2)==0)
401
- if min_length>0
402
- if allow_empty
403
- length=rand(min_length).to_i
404
- else
405
- length=rand(min_length-1).to_i+1
406
- end
407
- if required_data.size>length and required_data.size<min_length
408
- length=required_data.size
409
- end
410
- expected_errors_left.delete(:length)
411
- expected_errors_left.delete(:min_length)
412
- else
413
- puts "min_length is 0 so it won't be possible to generate a wrong string smaller than 0 characters. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
414
- return ''
415
- end
416
- elsif expected_errors_left.include?(:max_length) or expected_errors_left.include?(:length)
417
- length=max_length+1+rand(max_length).to_i
418
- expected_errors_left.delete(:length)
419
- expected_errors_left.delete(:max_length)
420
- end
421
- else
422
- if allow_empty and rand(7)==1
423
- length=0
424
- else
425
- if max_length==min_length
426
- length=min_length
427
- else
428
- length=min_length + rand(max_length - min_length + 1)
429
- end
430
- end
431
- end
432
-
433
- if deny_pattern
434
- if required_data.size==0 and expected_errors_left.include?(:required_data)
435
- expected_errors_left.delete(:required_data)
436
- end
437
-
438
- if excluded_data.size==0 and expected_errors_left.include?(:excluded_data)
439
- expected_errors_left.delete(:excluded_data)
440
- end
441
-
442
- if expected_errors_left.include?(:string_set_not_allowed)
443
- string_set_not_allowed=all_characters_set-string_set
444
- if string_set_not_allowed.size==0
445
- expected_errors_left.delete(:string_set_not_allowed)
446
- end
447
- end
448
-
449
- if symbol_type=='!@' and expected_errors_left.size==0 and !expected_errors.include?(:length) and
450
- (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data))
451
- expected_errors_left.push(:value)
452
- end
453
-
454
- end
455
-
456
- string = ''
457
- if symbol_type!='@' and symbol_type!='!@' and length!=0 and string_set.size!=0
458
- if string_set.size!=0
459
- 1.upto(length) {|i| string << string_set.sample.to_s
460
- }
461
- end
462
- if required_data.size>0
463
- positions_to_set=(0..(string.size-1)).to_a
464
- required_data.each {|rd|
465
- if (string.chars & rd).size>0
466
- rd_to_set=(string.chars & rd).sample
467
- else
468
- rd_to_set=rd.sample
469
- end
470
- if ((0 ... string.length).find_all {|i| string[i, 1] == rd_to_set}).size==0
471
- if positions_to_set.size==0
472
- puts "pattern not valid on StringPattern.generate, not possible to generate a valid string: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
473
- return ''
474
- else
475
- k=positions_to_set.sample
476
- string[k]=rd_to_set
477
- positions_to_set.delete(k)
478
- end
479
- else
480
- k=((0 ... string.length).find_all {|i| string[i, 1] == rd_to_set}).sample
481
- positions_to_set.delete(k)
482
- end
483
- }
484
- end
485
- excluded_data.each {|ed|
486
- if (string.chars & ed).size>0
487
- (string.chars & ed).each {|s|
488
- string.gsub!(s, string_set.sample)
489
- }
490
- end
491
- }
492
-
493
- if expected_errors_left.include?(:value)
494
- string_set_not_allowed=all_characters_set-string_set if string_set_not_allowed.size==0
495
- if string_set_not_allowed.size==0
496
- puts "Not possible to generate a non valid string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
497
- return ''
498
- end
499
- (rand(string.size)+1).times {
500
- string[rand(string.size)]=(all_characters_set-string_set).sample
501
- }
502
- expected_errors_left.delete(:value)
503
- end
504
-
505
- if expected_errors_left.include?(:required_data) and required_data.size>0
506
- (rand(required_data.size)+1).times {
507
- chars_to_remove=required_data.sample
508
- chars_to_remove.each {|char_to_remove|
509
- string.gsub!(char_to_remove, (string_set-chars_to_remove).sample)
510
- }
511
- }
512
- expected_errors_left.delete(:required_data)
513
- end
514
-
515
- if expected_errors_left.include?(:excluded_data) and excluded_data.size>0
516
- (rand(string.size)+1).times {
517
- string[rand(string.size)]=excluded_data.sample.sample
518
- }
519
- expected_errors_left.delete(:excluded_data)
520
- end
521
-
522
- if expected_errors_left.include?(:string_set_not_allowed)
523
- string_set_not_allowed=all_characters_set-string_set if string_set_not_allowed.size==0
524
- if string_set_not_allowed.size>0
525
- (rand(string.size)+1).times {
526
- string[rand(string.size)]=string_set_not_allowed.sample
527
- }
528
- expected_errors_left.delete(:string_set_not_allowed)
529
- end
530
- end
531
-
532
- elsif (symbol_type=='@' or symbol_type=='!@') and length>0
533
- if min_length>6 and length<6
534
- length=6
535
- end
536
- if deny_pattern and
537
- (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data) or
538
- expected_errors.include?(:string_set_not_allowed))
539
- expected_errors_left.push(:value)
540
- expected_errors.push(:value)
541
- expected_errors.uniq!
542
- expected_errors_left.uniq!
543
- end
544
-
545
- expected_errors_left_orig=expected_errors_left.dup
546
- tries=0
547
- begin
548
- expected_errors_left=expected_errors_left_orig.dup
549
- tries+=1
550
- string=''
551
- alpha_set=ALPHA_SET_LOWER + ALPHA_SET_CAPITAL
552
- string_set=alpha_set + NUMBER_SET + ['.'] + ['_'] + ['-']
553
- string_set_not_allowed=all_characters_set-string_set
554
-
555
- extension='.'
556
- at_sign='@'
557
-
558
- if expected_errors_left.include?(:value)
559
- if rand(2)==1
560
- extension=(all_characters_set-['.']).sample
561
- expected_errors_left.delete(:value)
562
- expected_errors_left.delete(:required_data)
563
- end
564
- if rand(2)==1
565
- 1.upto(rand(7)) {|i| extension << alpha_set.sample.downcase
566
- }
567
- (rand(extension.size)+1).times {
568
- extension[rand(extension.size)]=(string_set-alpha_set-['.']).sample
569
- }
570
- expected_errors_left.delete(:value)
571
- else
572
- 1.upto(rand(3)+2) {|i| extension << alpha_set.sample.downcase
573
- }
574
- end
575
- if rand(2)==1
576
- at_sign=(string_set-['@']).sample
577
- expected_errors_left.delete(:value)
578
- expected_errors_left.delete(:required_data)
579
- end
580
- else
581
- if length>6
582
- 1.upto(rand(3)+2) {|i| extension << alpha_set.sample.downcase
583
- }
584
- else
585
- 1.upto(2) {|i| extension << alpha_set.sample.downcase
586
- }
587
- end
588
- end
589
- length_e=length-extension.size - 1
590
- length1=rand(length_e-1) + 1
591
- length2=length_e-length1
592
- 1.upto(length1) {|i| string << string_set.sample}
593
-
594
- string << at_sign
595
-
596
- domain=''
597
- domain_set=alpha_set + NUMBER_SET + ['.'] + ['-']
598
- 1.upto(length2) {|i| domain << domain_set.sample.downcase
599
- }
600
-
601
- if expected_errors.include?(:value) and rand(2)==1 and domain.size>0
602
- (rand(domain.size)+1).times {
603
- domain[rand(domain.size)]=(all_characters_set-domain_set).sample
604
- }
605
- expected_errors_left.delete(:value)
606
- end
607
- string << domain << extension
608
-
609
- if expected_errors_left.include?(:value) or expected_errors_left.include?(:string_set_not_allowed)
610
- (rand(string.size)+1).times {
611
- string[rand(string.size)]=string_set_not_allowed.sample
612
- }
613
- expected_errors_left.delete(:value)
614
- expected_errors_left.delete(:string_set_not_allowed)
615
- end
616
-
617
- error_regular_expression=false
618
-
619
- if deny_pattern and expected_errors.include?(:length)
620
- good_result=true #it is already with wrong length
621
- else
622
- # I'm doing this because many times the regular expression checking hangs with these characters
623
- wrong=%w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@)
624
- if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings
625
- if string.index('@').to_i>0 and
626
- string[0..(string.index('@')-1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join==string[0..(string.index('@')-1)] and
627
- string[(string.index('@')+1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join==string[string[(string.index('@')+1)..-1]]
628
- error_regular_expression=false
629
- else
630
- error_regular_expression=true
631
- end
632
- else
633
- error_regular_expression=true
634
- end
635
-
636
- if expected_errors.size==0
637
- if error_regular_expression
638
- good_result=false
639
- else
640
- good_result=true
641
- end
642
- elsif expected_errors_left.size==0 and
643
- (expected_errors-[:length, :min_length, :max_length]).size==0
644
- good_result=true
645
- elsif expected_errors!=[:length]
646
- if !error_regular_expression
647
- good_result=false
648
- elsif expected_errors.include?(:value)
649
- good_result=true
650
- end
651
- end
652
- end
653
-
654
- end until good_result or tries>100
655
- unless good_result
656
- puts "Not possible to generate an email on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
657
- return ''
658
- end
659
- end
660
- if @dont_repeat
661
- if @cache_values[pattern.to_s].nil?
662
- @cache_values[pattern.to_s]=Array.new()
663
- @cache_values[pattern.to_s].push(string)
664
- good_result=true
665
- elsif @cache_values[pattern.to_s].include?(string)
666
- good_result = false
667
- else
668
- @cache_values[pattern.to_s].push(string)
669
- good_result=true
670
- end
671
- end
672
- if pattern.kind_of?(Symbol) and symbol_type[-1]=="&"
673
- if @cache_values[pattern.__id__].nil?
674
- @cache_values[pattern.__id__]=Array.new()
675
- @cache_values[pattern.__id__].push(string)
676
- good_result=true
677
- elsif @cache_values[pattern.__id__].include?(string)
678
- good_result = false
679
- else
680
- @cache_values[pattern.__id__].push(string)
681
- good_result=true
682
- end
683
- end
684
- end until good_result or tries>10000
685
- unless good_result
686
- puts "Not possible to generate the string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
687
- puts "Take in consideration if you are using StringPattern.dont_repeat=true that you don't try to generate more strings that are possible to be generated"
688
- return ''
689
- end
690
-
691
- return string
692
- end
693
-
694
-
695
- ##############################################
696
- # This method is defined to validate if the text_to_validate supplied follows the pattern
697
- # It works also with array of patterns but in that case will return only true or false
698
- # input:
699
- # text (String) (synonyms: text_to_validate, validate) -- The text to validate
700
- # pattern -- symbol with this info: "length:symbol_type" or "min_length-max_length:symbol_type"
701
- # min_length -- minimum length of the string
702
- # max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided
703
- # symbol_type -- the type of the string we want.
704
- # expected_errors (Array of symbols) (optional) (synonyms: errors) -- :length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
705
- # not_expected_errors (Array of symbols) (optional) (synonyms: not_errors, non_expected_errors) -- :length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
706
- # example:
707
- # validate(text: "This text will be validated", pattern: :"10-20:Xn", expected_errors: [:value, :max_length])
708
- #
709
- # Output:
710
- # if expected_errors and not_expected_errors are not supplied: an array with all detected errors
711
- # if expected_errors or not_expected_errors supplied: true or false
712
- # if array of patterns supplied, it will return true or false
713
- ###############################################
714
- def StringPattern.validate(text: '', pattern: '', expected_errors: [], not_expected_errors: [], **synonyms)
715
- text_to_validate=text
716
- text_to_validate=synonyms[:text_to_validate] if synonyms.keys.include?(:text_to_validate)
717
- text_to_validate=synonyms[:validate] if synonyms.keys.include?(:validate)
718
- expected_errors=synonyms[:errors] if synonyms.keys.include?(:errors)
719
- not_expected_errors=synonyms[:not_errors] if synonyms.keys.include?(:not_errors)
720
- not_expected_errors=synonyms[:non_expected_errors] if synonyms.keys.include?(:non_expected_errors)
721
- #:length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
722
- if (expected_errors.include?(:min_length) or expected_errors.include?(:max_length)) and !expected_errors.include?(:length)
723
- expected_errors.push(:length)
724
- end
725
- if (not_expected_errors.include?(:min_length) or not_expected_errors.include?(:max_length)) and !not_expected_errors.include?(:length)
726
- not_expected_errors.push(:length)
727
- end
728
- if pattern.kind_of?(Array) and pattern.size==1
729
- pattern=pattern[0]
730
- elsif pattern.kind_of?(Array) and pattern.size>1 then
731
- total_min_length=0
732
- total_max_length=0
733
- all_errors_collected=Array.new
734
- result=true
735
- num_patt=0
736
- patterns=Array.new
737
- pattern.each {|pat|
738
- if (pat.kind_of?(String) and (!StringPattern.optimistic or
739
- (StringPattern.optimistic and pat.to_s.scan(/(\d+)-(\d+):(.+)/).size==0 and pat.to_s.scan(/^!?(\d+):(.+)/).size==0))) #fixed text
740
- symbol_type=''
741
- min_length=max_length=pat.length
742
- elsif pat.kind_of?(Symbol) or (pat.kind_of?(String) and StringPattern.optimistic and
743
- (pat.to_s.scan(/(\d+)-(\d+):(.+)/).size>0 or pat.to_s.scan(/^!?(\d+):(.+)/).size>0))
744
- patt=StringPattern.analyze(pat)
745
- min_length=patt.min_length
746
- max_length=patt.max_length
747
- symbol_type=patt.symbol_type
748
- else
749
- puts "String pattern class not supported (#{pat.class} for #{pat})"
750
- end
751
-
752
- patterns.push({pattern: pat, min_length: min_length, max_length: max_length, symbol_type: symbol_type})
753
-
754
- total_min_length+=min_length
755
- total_max_length+=max_length
756
-
757
- if num_patt==(pattern.size-1) # i am in the last one
758
- if text_to_validate.length<total_min_length
759
- all_errors_collected.push(:length)
760
- all_errors_collected.push(:min_length)
761
- end
762
-
763
- if text_to_validate.length>total_max_length
764
- all_errors_collected.push(:length)
765
- all_errors_collected.push(:max_length)
766
- end
767
-
768
- end
769
- num_patt+=1
770
-
771
-
772
- }
773
-
774
- num_patt=0
775
- patterns.each {|patt|
776
-
777
- tmp_result=false
778
- (patt[:min_length]..patt[:max_length]).each {|n|
779
- res=StringPattern.validate(text: text_to_validate[0..n-1], pattern: patt[:pattern], not_expected_errors: not_expected_errors)
780
- if res.kind_of?(Array)
781
- all_errors_collected+=res
782
- end
783
-
784
- if res.kind_of?(TrueClass) or (res.kind_of?(Array) and res.size==0) #valid
785
- #we pass in the next one the rest of the pattern array list: pattern: pattern[num_patt+1..pattern.size]
786
- res=StringPattern.validate(text: text_to_validate[n..text_to_validate.length], pattern: pattern[num_patt+1..pattern.size], expected_errors: expected_errors, not_expected_errors: not_expected_errors)
787
-
788
- if res.kind_of?(Array)
789
- if ((all_errors_collected + res)-expected_errors).size>0
790
- tmp_result=false
791
- else
792
- all_errors_collected+=res
793
- tmp_result=true
794
- end
795
- elsif res.kind_of?(TrueClass) then
796
- tmp_result=true
797
- end
798
- return true if tmp_result
799
- end
800
- }
801
-
802
- unless tmp_result
803
- return false
804
- end
805
- num_patt+=1
806
- }
807
- return result
808
- end
809
-
810
- if (pattern.kind_of?(String) and (!StringPattern.optimistic or
811
- (StringPattern.optimistic and pattern.to_s.scan(/(\d+)-(\d+):(.+)/).size==0 and pattern.to_s.scan(/^!?(\d+):(.+)/).size==0))) #fixed text
812
- symbol_type=''
813
- min_length=max_length=pattern.length
814
- else #symbol
815
- patt=StringPattern.analyze(pattern)
816
- min_length=patt.min_length
817
- max_length=patt.max_length
818
- symbol_type=patt.symbol_type
819
-
820
- required_data=patt.required_data
821
- excluded_data=patt.excluded_data
822
- string_set=patt.string_set
823
- all_characters_set=patt.all_characters_set
824
-
825
- required_chars=Array.new
826
- required_data.each {|rd|
827
- required_chars<<rd if rd.size==1
828
- }
829
- if (required_chars.flatten & excluded_data.flatten).size>0
830
- puts "pattern argument not valid on StringPattern.validate, a character cannot be required and excluded at the same time: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
831
- return ''
832
- end
833
-
834
- end
835
-
836
- if text_to_validate.nil?
837
- return false
838
- end
839
- detected_errors=Array.new
840
-
841
- if text_to_validate.length<min_length
842
- detected_errors.push(:min_length)
843
- detected_errors.push(:length)
844
- end
845
- if text_to_validate.length>max_length
846
- detected_errors.push(:max_length)
847
- detected_errors.push(:length)
848
- end
849
-
850
- if symbol_type=='' #fixed text
851
- if pattern.to_s!=text.to_s #not equal
852
- detected_errors.push(:value)
853
- detected_errors.push(:required_data)
854
- end
855
- else # pattern supplied
856
- if symbol_type!='@'
857
- if required_data.size>0
858
- required_data.each {|rd|
859
- if (text_to_validate.chars & rd).size==0
860
- detected_errors.push(:value)
861
- detected_errors.push(:required_data)
862
- break
863
- end
864
- }
865
- end
866
- if excluded_data.size>0
867
- if (excluded_data & text_to_validate.chars).size>0
868
- detected_errors.push(:value)
869
- detected_errors.push(:excluded_data)
870
- end
871
- end
872
- string_set_not_allowed=all_characters_set-string_set
873
- text_to_validate.chars.each {|st|
874
- if string_set_not_allowed.include?(st)
875
- detected_errors.push(:value)
876
- detected_errors.push(:string_set_not_allowed)
877
- break
878
- end
879
- }
880
- else #symbol_type=="@"
881
- string=text_to_validate
882
- wrong=%w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@)
883
- if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings
884
- if string.index('@').to_i>0 and
885
- string[0..(string.index('@')-1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join==string[0..(string.index('@')-1)] and
886
- string[(string.index('@')+1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join==string[string[(string.index('@')+1)..-1]]
887
- error_regular_expression=false
888
- else
889
- error_regular_expression=true
890
- end
891
- else
892
- error_regular_expression=true
893
- end
894
-
895
- if error_regular_expression
896
- detected_errors.push(:value)
897
- end
898
-
899
- end
900
- end
901
-
902
- if expected_errors.size==0 and not_expected_errors.size==0
903
- return detected_errors
904
- else
905
- if expected_errors & detected_errors == expected_errors
906
- if (not_expected_errors & detected_errors).size > 0
907
- return false
908
- else
909
- return true
910
- end
911
- else
912
- return false
913
- end
914
- end
915
- end
916
-
917
- end
918
-
1
+ SP_ADD_TO_RUBY = true if !defined?(SP_ADD_TO_RUBY)
2
+ require_relative 'string/pattern/add_to_ruby' if SP_ADD_TO_RUBY
3
+
4
+ # SP_ADD_TO_RUBY: (TrueFalse, default: true) You need to add this constant value before requiring the library if you want to modify the default.
5
+ # If true it will add 'generate' and 'validate' methods to the classes: Array, String and Symbol. Also it will add 'generate' method to Kernel
6
+ # aliases: 'gen' for 'generate' and 'val' for 'validate'
7
+ # Examples of use:
8
+ # "(,3:N,) ,3:N,-,2:N,-,2:N".split(",").generate #>(937) 980-65-05
9
+ # %w{( 3:N ) 1:_ 3:N - 2:N - 2:N}.gen #>(045) 448-63-09
10
+ # ["1:L", "5-10:LN", "-", "3:N"].gen #>zqWihV-746
11
+ # gen("10:N") #>3433409877
12
+ # "20-30:@".gen #>dkj34MljjJD-df@jfdluul.dfu
13
+ # "10:L/N/[/-./%d%]".validate("12ds6f--.s") #>[:value, :string_set_not_allowed]
14
+ # "20-40:@".validate(my_email)
15
+ # national_chars: (Array, default: english alphabet)
16
+ # Set of characters that will be used when using T pattern
17
+ # optimistic: (TrueFalse, default: true)
18
+ # If true it will check on the strings of the array positions if they have the pattern format and assume in that case that is a pattern.
19
+ # dont_repeat: (TrueFalse, default: false)
20
+ # If you want to generate for example 1000 strings and be sure all those strings are different you can set it to true
21
+ class StringPattern
22
+ class << self
23
+ attr_accessor :national_chars, :optimistic, :dont_repeat, :cache, :cache_values
24
+ end
25
+ @national_chars = (('a'..'z').to_a + ('A'..'Z').to_a).join
26
+ @optimistic = true
27
+ @cache = Hash.new()
28
+ @cache_values = Hash.new()
29
+ @dont_repeat = false
30
+ NUMBER_SET = ('0'..'9').to_a
31
+ SPECIAL_SET = [' ', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '+', '=', '{', '}', '[', ']', "'", ';', ':', '?', '>', '<', '`', '|', '/', '"']
32
+ ALPHA_SET_LOWER = ('a'..'z').to_a
33
+ ALPHA_SET_CAPITAL = ('A'..'Z').to_a
34
+
35
+
36
+ Pattern = Struct.new(:min_length, :max_length, :symbol_type, :required_data, :excluded_data, :data_provided,
37
+ :string_set, :all_characters_set)
38
+
39
+ ###############################################
40
+ # Analyze the pattern supplied and returns an object of Pattern structure including:
41
+ # min_length, max_length, symbol_type, required_data, excluded_data, data_provided, string_set, all_characters_set
42
+ ###############################################
43
+ def StringPattern.analyze(pattern, silent: false)
44
+ return @cache[pattern.to_s] unless @cache[pattern.to_s].nil?
45
+ min_length, max_length, symbol_type = pattern.to_s.scan(/(\d+)-(\d+):(.+)/)[0]
46
+ if min_length.nil?
47
+ min_length, symbol_type = pattern.to_s.scan(/^!?(\d+):(.+)/)[0]
48
+ max_length = min_length
49
+ if min_length.nil?
50
+ puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}" unless silent
51
+ return pattern.to_s
52
+ end
53
+ end
54
+
55
+ symbol_type = '!' + symbol_type if pattern.to_s[0] == '!'
56
+ min_length = min_length.to_i
57
+ max_length = max_length.to_i
58
+
59
+ required_data = Array.new
60
+ excluded_data = Array.new
61
+ required = false
62
+ excluded = false
63
+ data_provided = Array.new
64
+ a = symbol_type
65
+ begin_provided = a.index('[')
66
+ excluded_end_tag = false
67
+ unless begin_provided.nil?
68
+ c = begin_provided + 1
69
+ until c == a.size or (a[c..c] == ']' and a[c..c + 1] != ']]')
70
+ if a[c..c + 1] == ']]'
71
+ data_provided.push(']')
72
+ c = c + 2
73
+ elsif a[c..c + 1] == '%%' and !excluded then
74
+ data_provided.push('%')
75
+ c = c + 2
76
+ else
77
+ if a[c..c] == '/' and !excluded
78
+ if a[c..c + 1] == '//'
79
+ data_provided.push(a[c..c])
80
+ if required
81
+ required_data.push([a[c..c]])
82
+ end
83
+ c = c + 1
84
+ else
85
+ if !required
86
+ required = true
87
+ else
88
+ required = false
89
+ end
90
+ end
91
+ else
92
+ if required
93
+ required_data.push([a[c..c]])
94
+ else
95
+ if a[c..c] == '%'
96
+ if a[c..c + 1] == '%%' and excluded
97
+ excluded_data.push([a[c..c]])
98
+ c = c + 1
99
+ else
100
+ if !excluded
101
+ excluded = true
102
+ else
103
+ excluded = false
104
+ excluded_end_tag = true
105
+ end
106
+ end
107
+ else
108
+ if excluded
109
+ excluded_data.push([a[c..c]])
110
+ end
111
+ end
112
+
113
+ end
114
+ if excluded == false and excluded_end_tag == false
115
+ data_provided.push(a[c..c])
116
+ end
117
+ excluded_end_tag = false
118
+ end
119
+ c = c + 1
120
+ end
121
+ end
122
+ symbol_type = symbol_type[0..begin_provided].to_s + symbol_type[c..symbol_type.size].to_s
123
+ end
124
+
125
+ required = false
126
+ required_symbol = ''
127
+ if symbol_type.include?("/")
128
+ symbol_type.chars.each {|stc|
129
+ if stc == '/'
130
+ if !required
131
+ required = true
132
+ else
133
+ required = false
134
+ end
135
+ else
136
+ if required
137
+ required_symbol += stc
138
+ end
139
+ end
140
+ }
141
+ end
142
+
143
+ national_set = @national_chars.chars
144
+
145
+ if symbol_type.include?('L') then
146
+ alpha_set = ALPHA_SET_LOWER + ALPHA_SET_CAPITAL
147
+ elsif symbol_type.include?('x')
148
+ alpha_set = ALPHA_SET_LOWER
149
+ if symbol_type.include?('X')
150
+ alpha_set = alpha_set + ALPHA_SET_CAPITAL
151
+ end
152
+ elsif symbol_type.include?('X')
153
+ alpha_set = ALPHA_SET_CAPITAL
154
+ else
155
+ alpha_set = []
156
+ end
157
+ if symbol_type.include?('T')
158
+ alpha_set = alpha_set + national_set
159
+ end
160
+
161
+ unless required_symbol.nil?
162
+ if required_symbol.include?('x')
163
+ required_data.push ALPHA_SET_LOWER
164
+ end
165
+ if required_symbol.include?('X')
166
+ required_data.push ALPHA_SET_CAPITAL
167
+ end
168
+ if required_symbol.include?('L')
169
+ required_data.push(ALPHA_SET_CAPITAL + ALPHA_SET_LOWER)
170
+ end
171
+ if required_symbol.include?('T')
172
+ required_data.push national_set
173
+ end
174
+ required_symbol = required_symbol.downcase
175
+ end
176
+ string_set = Array.new
177
+ all_characters_set = ALPHA_SET_CAPITAL + ALPHA_SET_LOWER + NUMBER_SET + SPECIAL_SET + data_provided + national_set
178
+
179
+ if symbol_type.include?('_')
180
+ unless symbol_type.include?('$')
181
+ string_set.push(' ')
182
+ end
183
+ if required_symbol.include?('_')
184
+ required_data.push([' '])
185
+ end
186
+ end
187
+
188
+ symbol_type = symbol_type.downcase
189
+
190
+ if symbol_type.include?('x') or symbol_type.include?('l') or symbol_type.include?('t')
191
+ string_set = string_set + alpha_set
192
+ end
193
+ if symbol_type.include?('n')
194
+ string_set = string_set + NUMBER_SET
195
+ end
196
+ if symbol_type.include?('$')
197
+ string_set = string_set + SPECIAL_SET
198
+ end
199
+ if symbol_type.include?('*')
200
+ string_set = string_set + all_characters_set
201
+ end
202
+ if data_provided.size != 0
203
+ string_set = string_set + data_provided
204
+ end
205
+ unless required_symbol.empty?
206
+ if required_symbol.include?('n')
207
+ required_data.push NUMBER_SET
208
+ end
209
+ if required_symbol.include?('$')
210
+ required_data.push SPECIAL_SET
211
+ end
212
+ end
213
+ unless excluded_data.empty?
214
+ string_set = string_set - excluded_data.flatten
215
+ end
216
+ string_set.uniq!
217
+ @cache[pattern.to_s] = Pattern.new(min_length, max_length, symbol_type, required_data, excluded_data, data_provided,
218
+ string_set, all_characters_set)
219
+ return @cache[pattern.to_s]
220
+ end
221
+
222
+ ###############################################
223
+ # Generate a random string based on the pattern supplied
224
+ # (if SP_ADD_TO_RUBY==true, by default is true) To simplify its use it is part of the String, Array, Symbol and Kernel Ruby so can be easily used also like this:
225
+ # "10-15:Ln/x/".generate #generate method on String class (alias: gen)
226
+ # ['(', :'3:N', ')', :'6-8:N'].generate #generate method on Array class (alias: gen)
227
+ # generate("10-15:Ln/x/") #generate Ruby Kernel method
228
+ # generate(['(', :'3:N', ')', :'6-8:N']) #generate Ruby Kernel method
229
+ # "(,3:N,) ,3:N,-,2:N,-,2:N".split(",").generate #>(937) #generate method on Array class (alias: gen)
230
+ # %w{( 3:N ) 1:_ 3:N - 2:N - 2:N}.gen #generate method on Array class, using alias gen method
231
+ # Input:
232
+ # pattern: array or string of different patterns. A pattern is a string with this info:
233
+ # "length:symbol_type" or "min_length-max_length:symbol_type"
234
+ # In case an array supplied, the positions using a string pattern should be supplied as symbols if StringPattern.optimistic==false
235
+ #
236
+ # These are the possible string patterns you will be able to supply:
237
+ # If at the beginning we supply the character ! the resulting string won't fulfill the pattern. This character need to be the first character of the pattern.
238
+ # min_length -- minimum length of the string
239
+ # max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided
240
+ # symbol_type -- the type of the string we want.
241
+ # you can use a combination of any ot these:
242
+ # x for alpha in lowercase
243
+ # X for alpha in capital letters
244
+ # L for all kind of alpha in capital and lower letters
245
+ # T For the national characters defined on StringPattern.national_chars
246
+ # n for number
247
+ # $ for special characters (includes space)
248
+ # _ for space
249
+ # * all characters
250
+ # [characters] the characters we want. If we want to add also the ] character you have to write: ]]. If we want to add also the % character you have to write: %%
251
+ # %characters% the characters we don't want on the resulting string. %% to exclude the character %
252
+ # /symbols or characters/ If we want these characters to be included on the resulting string. If we want to add also the / character you have to write: //
253
+ # We can supply 0 to allow empty strings, this character need to be at the beginning
254
+ # If you want to include the character " use \"
255
+ # If you want to include the character \ use \\
256
+ # If you want to include the character [ use \[
257
+ # Another uses: @ for email
258
+ # Examples:
259
+ # [:"6:X", :"3-8:_N"]
260
+ # # it will return a string starting with 6 capital letters and then a string containing numbers and space from 3 to 8 characters, for example: "LDJKKD34 555"
261
+ # [:"6-15:L_N", "fixed text", :"3:N"]
262
+ # # it will return a string of 6-15 characters containing Letters-spaces-numbers, then the text: 'fixed text' and at the end a string of 3 characters containing numbers, for example: ["L_N",6,15],"fixed text",["N",3] "3 Am399 afixed text882"
263
+ # "10-20:LN[=#]"
264
+ # # it will return a string of 10-20 characters containing Letters and/or numbers and/or the characters = and #, for example: eiyweQFWeL#do4Vl
265
+ # "30:TN_[#=]/x/"
266
+ # # it will return a string of 30 characters containing national characters defined on StringPattern.national_chars and/or numbers and/or spaces and/or the characters # = and it is necessary the resultant string includes lower alpha chars. For example: HaEdQTzJ3=OtXMh1mAPqv7NCy=upLy
267
+ # "10:N[%0%]"
268
+ # # 10 characters length containing numbers and excluding the character 0, for example: 3523497757
269
+ # "10:N[%0%/AB/]"
270
+ # # 10 characters length containing numbers and excluding the character 0 and necessary to contain the characters A B, for example: 3AA4AA57BB
271
+ # "!10:N[%0%/AB/]"
272
+ # # it will generate a string that doesn't fulfill the pattern supplied, examples:
273
+ # # a6oMQ4JK9g
274
+ # # /Y<N6Aa[ae
275
+ # # 3444439A34B32
276
+ # "10:N[%0%/AB/]", errors: [:length]
277
+ # # it will generate a string following the pattern and with the errors supplied, in this case, length, example: AB44
278
+ # Output:
279
+ # the generated string
280
+ ###############################################
281
+ def StringPattern.generate(pattern, expected_errors: [], **synonyms)
282
+ tries = 0
283
+ begin
284
+ good_result = true
285
+ tries += 1
286
+ string = ''
287
+
288
+ expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors)
289
+
290
+ if expected_errors.kind_of?(Symbol)
291
+ expected_errors = [expected_errors]
292
+ end
293
+
294
+ if pattern.kind_of?(Array)
295
+ pattern.each {|pat|
296
+ if pat.kind_of?(Symbol)
297
+ if pat.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
298
+ string << StringPattern.generate(pat.to_s, expected_errors: expected_errors)
299
+ else
300
+ string << pat.to_s
301
+ end
302
+ elsif pat.kind_of?(String) then
303
+ if @optimistic and pat.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
304
+ string << StringPattern.generate(pat.to_s, expected_errors: expected_errors)
305
+ else
306
+ string << pat
307
+ end
308
+ else
309
+ puts "StringPattern.generate: it seems you supplied wrong array of patterns: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
310
+ return ''
311
+ end
312
+ }
313
+ return string
314
+ elsif pattern.kind_of?(String) or pattern.kind_of?(Symbol)
315
+ patt = StringPattern.analyze(pattern)
316
+ min_length = patt.min_length
317
+ max_length = patt.max_length
318
+ symbol_type = patt.symbol_type
319
+
320
+ required_data = patt.required_data
321
+ excluded_data = patt.excluded_data
322
+ string_set = patt.string_set
323
+ all_characters_set = patt.all_characters_set
324
+
325
+ required_chars = Array.new
326
+ unless required_data.size == 0
327
+ required_data.each {|rd|
328
+ required_chars << rd if rd.size == 1
329
+ }
330
+ unless excluded_data.size == 0
331
+ if (required_chars.flatten & excluded_data.flatten).size > 0
332
+ puts "pattern argument not valid on StringPattern.generate, a character cannot be required and excluded at the same time: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
333
+ return ''
334
+ end
335
+ end
336
+ end
337
+ string_set_not_allowed = Array.new
338
+
339
+ else
340
+ puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
341
+ return pattern.to_s
342
+ end
343
+
344
+
345
+ allow_empty = false
346
+ deny_pattern = false
347
+ if symbol_type[0..0] == '!'
348
+ deny_pattern = true
349
+ possible_errors = [:length, :value, :required_data, :excluded_data, :string_set_not_allowed]
350
+ (rand(possible_errors.size) + 1).times {
351
+ expected_errors << possible_errors.sample
352
+ }
353
+ expected_errors.uniq!
354
+ if symbol_type[1..1] == '0'
355
+ allow_empty = true
356
+ end
357
+ elsif symbol_type[0..0] == '0' then
358
+ allow_empty = true
359
+ end
360
+
361
+ if expected_errors.include?(:min_length) or expected_errors.include?(:length) or
362
+ expected_errors.include?(:max_length)
363
+ allow_empty = !allow_empty
364
+ elsif expected_errors.include?(:value) or
365
+ expected_errors.include?(:excluded_data) or
366
+ expected_errors.include?(:required_data) or
367
+ expected_errors.include?(:string_set_not_allowed) and allow_empty
368
+ allow_empty = false
369
+ end
370
+
371
+ length = min_length
372
+ symbol_type_orig = symbol_type
373
+
374
+ expected_errors_left = expected_errors.dup
375
+
376
+ symbol_type = symbol_type_orig
377
+
378
+ unless deny_pattern
379
+ if required_data.size == 0 and expected_errors_left.include?(:required_data)
380
+ puts "required data not supplied on pattern so it won't be possible to generate a wrong string. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
381
+ return ''
382
+ end
383
+
384
+ if excluded_data.size == 0 and expected_errors_left.include?(:excluded_data)
385
+ puts "excluded data not supplied on pattern so it won't be possible to generate a wrong string. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
386
+ return ''
387
+ end
388
+
389
+ if expected_errors_left.include?(:string_set_not_allowed)
390
+ string_set_not_allowed = all_characters_set - string_set
391
+ if string_set_not_allowed.size == 0 then
392
+ puts "all characters are allowed so it won't be possible to generate a wrong string. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
393
+ return ''
394
+ end
395
+ end
396
+ end
397
+
398
+ if expected_errors_left.include?(:min_length) or
399
+ expected_errors_left.include?(:max_length) or
400
+ expected_errors_left.include?(:length)
401
+ if expected_errors_left.include?(:min_length) or
402
+ (min_length > 0 and expected_errors_left.include?(:length) and rand(2) == 0)
403
+ if min_length > 0
404
+ if allow_empty
405
+ length = rand(min_length).to_i
406
+ else
407
+ length = rand(min_length - 1).to_i + 1
408
+ end
409
+ if required_data.size > length and required_data.size < min_length
410
+ length = required_data.size
411
+ end
412
+ expected_errors_left.delete(:length)
413
+ expected_errors_left.delete(:min_length)
414
+ else
415
+ puts "min_length is 0 so it won't be possible to generate a wrong string smaller than 0 characters. StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
416
+ return ''
417
+ end
418
+ elsif expected_errors_left.include?(:max_length) or expected_errors_left.include?(:length)
419
+ length = max_length + 1 + rand(max_length).to_i
420
+ expected_errors_left.delete(:length)
421
+ expected_errors_left.delete(:max_length)
422
+ end
423
+ else
424
+ if allow_empty and rand(7) == 1
425
+ length = 0
426
+ else
427
+ if max_length == min_length
428
+ length = min_length
429
+ else
430
+ length = min_length + rand(max_length - min_length + 1)
431
+ end
432
+ end
433
+ end
434
+
435
+ if deny_pattern
436
+ if required_data.size == 0 and expected_errors_left.include?(:required_data)
437
+ expected_errors_left.delete(:required_data)
438
+ end
439
+
440
+ if excluded_data.size == 0 and expected_errors_left.include?(:excluded_data)
441
+ expected_errors_left.delete(:excluded_data)
442
+ end
443
+
444
+ if expected_errors_left.include?(:string_set_not_allowed)
445
+ string_set_not_allowed = all_characters_set - string_set
446
+ if string_set_not_allowed.size == 0
447
+ expected_errors_left.delete(:string_set_not_allowed)
448
+ end
449
+ end
450
+
451
+ if symbol_type == '!@' and expected_errors_left.size == 0 and !expected_errors.include?(:length) and
452
+ (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data))
453
+ expected_errors_left.push(:value)
454
+ end
455
+
456
+ end
457
+
458
+ string = ''
459
+ if symbol_type != '@' and symbol_type != '!@' and length != 0 and string_set.size != 0
460
+ if string_set.size != 0
461
+ 1.upto(length) {|i| string << string_set.sample.to_s
462
+ }
463
+ end
464
+ if required_data.size > 0
465
+ positions_to_set = (0..(string.size - 1)).to_a
466
+ required_data.each {|rd|
467
+ if (string.chars & rd).size > 0
468
+ rd_to_set = (string.chars & rd).sample
469
+ else
470
+ rd_to_set = rd.sample
471
+ end
472
+ if ((0...string.length).find_all {|i| string[i, 1] == rd_to_set}).size == 0
473
+ if positions_to_set.size == 0
474
+ puts "pattern not valid on StringPattern.generate, not possible to generate a valid string: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
475
+ return ''
476
+ else
477
+ k = positions_to_set.sample
478
+ string[k] = rd_to_set
479
+ positions_to_set.delete(k)
480
+ end
481
+ else
482
+ k = ((0...string.length).find_all {|i| string[i, 1] == rd_to_set}).sample
483
+ positions_to_set.delete(k)
484
+ end
485
+ }
486
+ end
487
+ excluded_data.each {|ed|
488
+ if (string.chars & ed).size > 0
489
+ (string.chars & ed).each {|s|
490
+ string.gsub!(s, string_set.sample)
491
+ }
492
+ end
493
+ }
494
+
495
+ if expected_errors_left.include?(:value)
496
+ string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0
497
+ if string_set_not_allowed.size == 0
498
+ puts "Not possible to generate a non valid string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
499
+ return ''
500
+ end
501
+ (rand(string.size) + 1).times {
502
+ string[rand(string.size)] = (all_characters_set - string_set).sample
503
+ }
504
+ expected_errors_left.delete(:value)
505
+ end
506
+
507
+ if expected_errors_left.include?(:required_data) and required_data.size > 0
508
+ (rand(required_data.size) + 1).times {
509
+ chars_to_remove = required_data.sample
510
+ chars_to_remove.each {|char_to_remove|
511
+ string.gsub!(char_to_remove, (string_set - chars_to_remove).sample)
512
+ }
513
+ }
514
+ expected_errors_left.delete(:required_data)
515
+ end
516
+
517
+ if expected_errors_left.include?(:excluded_data) and excluded_data.size > 0
518
+ (rand(string.size) + 1).times {
519
+ string[rand(string.size)] = excluded_data.sample.sample
520
+ }
521
+ expected_errors_left.delete(:excluded_data)
522
+ end
523
+
524
+ if expected_errors_left.include?(:string_set_not_allowed)
525
+ string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0
526
+ if string_set_not_allowed.size > 0
527
+ (rand(string.size) + 1).times {
528
+ string[rand(string.size)] = string_set_not_allowed.sample
529
+ }
530
+ expected_errors_left.delete(:string_set_not_allowed)
531
+ end
532
+ end
533
+
534
+ elsif (symbol_type == '@' or symbol_type == '!@') and length > 0
535
+ if min_length > 6 and length < 6
536
+ length = 6
537
+ end
538
+ if deny_pattern and
539
+ (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data) or
540
+ expected_errors.include?(:string_set_not_allowed))
541
+ expected_errors_left.push(:value)
542
+ expected_errors.push(:value)
543
+ expected_errors.uniq!
544
+ expected_errors_left.uniq!
545
+ end
546
+
547
+ expected_errors_left_orig = expected_errors_left.dup
548
+ tries = 0
549
+ begin
550
+ expected_errors_left = expected_errors_left_orig.dup
551
+ tries += 1
552
+ string = ''
553
+ alpha_set = ALPHA_SET_LOWER + ALPHA_SET_CAPITAL
554
+ string_set = alpha_set + NUMBER_SET + ['.'] + ['_'] + ['-']
555
+ string_set_not_allowed = all_characters_set - string_set
556
+
557
+ extension = '.'
558
+ at_sign = '@'
559
+
560
+ if expected_errors_left.include?(:value)
561
+ if rand(2) == 1
562
+ extension = (all_characters_set - ['.']).sample
563
+ expected_errors_left.delete(:value)
564
+ expected_errors_left.delete(:required_data)
565
+ end
566
+ if rand(2) == 1
567
+ 1.upto(rand(7)) {|i| extension << alpha_set.sample.downcase
568
+ }
569
+ (rand(extension.size) + 1).times {
570
+ extension[rand(extension.size)] = (string_set - alpha_set - ['.']).sample
571
+ }
572
+ expected_errors_left.delete(:value)
573
+ else
574
+ 1.upto(rand(3) + 2) {|i| extension << alpha_set.sample.downcase
575
+ }
576
+ end
577
+ if rand(2) == 1
578
+ at_sign = (string_set - ['@']).sample
579
+ expected_errors_left.delete(:value)
580
+ expected_errors_left.delete(:required_data)
581
+ end
582
+ else
583
+ if length > 6
584
+ 1.upto(rand(3) + 2) {|i| extension << alpha_set.sample.downcase
585
+ }
586
+ else
587
+ 1.upto(2) {|i| extension << alpha_set.sample.downcase
588
+ }
589
+ end
590
+ end
591
+ length_e = length - extension.size - 1
592
+ length1 = rand(length_e - 1) + 1
593
+ length2 = length_e - length1
594
+ 1.upto(length1) {|i| string << string_set.sample}
595
+
596
+ string << at_sign
597
+
598
+ domain = ''
599
+ domain_set = alpha_set + NUMBER_SET + ['.'] + ['-']
600
+ 1.upto(length2) {|i| domain << domain_set.sample.downcase
601
+ }
602
+
603
+ if expected_errors.include?(:value) and rand(2) == 1 and domain.size > 0
604
+ (rand(domain.size) + 1).times {
605
+ domain[rand(domain.size)] = (all_characters_set - domain_set).sample
606
+ }
607
+ expected_errors_left.delete(:value)
608
+ end
609
+ string << domain << extension
610
+
611
+ if expected_errors_left.include?(:value) or expected_errors_left.include?(:string_set_not_allowed)
612
+ (rand(string.size) + 1).times {
613
+ string[rand(string.size)] = string_set_not_allowed.sample
614
+ }
615
+ expected_errors_left.delete(:value)
616
+ expected_errors_left.delete(:string_set_not_allowed)
617
+ end
618
+
619
+ error_regular_expression = false
620
+
621
+ if deny_pattern and expected_errors.include?(:length)
622
+ good_result = true #it is already with wrong length
623
+ else
624
+ # I'm doing this because many times the regular expression checking hangs with these characters
625
+ wrong = %w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@)
626
+ if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings
627
+ if string.index('@').to_i > 0 and
628
+ string[0..(string.index('@') - 1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join == string[0..(string.index('@') - 1)] and
629
+ string[(string.index('@') + 1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join == string[string[(string.index('@') + 1)..-1]]
630
+ error_regular_expression = false
631
+ else
632
+ error_regular_expression = true
633
+ end
634
+ else
635
+ error_regular_expression = true
636
+ end
637
+
638
+ if expected_errors.size == 0
639
+ if error_regular_expression
640
+ good_result = false
641
+ else
642
+ good_result = true
643
+ end
644
+ elsif expected_errors_left.size == 0 and
645
+ (expected_errors - [:length, :min_length, :max_length]).size == 0
646
+ good_result = true
647
+ elsif expected_errors != [:length]
648
+ if !error_regular_expression
649
+ good_result = false
650
+ elsif expected_errors.include?(:value)
651
+ good_result = true
652
+ end
653
+ end
654
+ end
655
+
656
+ end until good_result or tries > 100
657
+ unless good_result
658
+ puts "Not possible to generate an email on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
659
+ return ''
660
+ end
661
+ end
662
+ if @dont_repeat
663
+ if @cache_values[pattern.to_s].nil?
664
+ @cache_values[pattern.to_s] = Array.new()
665
+ @cache_values[pattern.to_s].push(string)
666
+ good_result = true
667
+ elsif @cache_values[pattern.to_s].include?(string)
668
+ good_result = false
669
+ else
670
+ @cache_values[pattern.to_s].push(string)
671
+ good_result = true
672
+ end
673
+ end
674
+ if pattern.kind_of?(Symbol) and symbol_type[-1] == "&"
675
+ if @cache_values[pattern.__id__].nil?
676
+ @cache_values[pattern.__id__] = Array.new()
677
+ @cache_values[pattern.__id__].push(string)
678
+ good_result = true
679
+ elsif @cache_values[pattern.__id__].include?(string)
680
+ good_result = false
681
+ else
682
+ @cache_values[pattern.__id__].push(string)
683
+ good_result = true
684
+ end
685
+ end
686
+ end until good_result or tries > 10000
687
+ unless good_result
688
+ puts "Not possible to generate the string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
689
+ puts "Take in consideration if you are using StringPattern.dont_repeat=true that you don't try to generate more strings that are possible to be generated"
690
+ return ''
691
+ end
692
+
693
+ return string
694
+ end
695
+
696
+
697
+ ##############################################
698
+ # This method is defined to validate if the text_to_validate supplied follows the pattern
699
+ # It works also with array of patterns but in that case will return only true or false
700
+ # input:
701
+ # text (String) (synonyms: text_to_validate, validate) -- The text to validate
702
+ # pattern -- symbol with this info: "length:symbol_type" or "min_length-max_length:symbol_type"
703
+ # min_length -- minimum length of the string
704
+ # max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided
705
+ # symbol_type -- the type of the string we want.
706
+ # expected_errors (Array of symbols) (optional) (synonyms: errors) -- :length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
707
+ # not_expected_errors (Array of symbols) (optional) (synonyms: not_errors, non_expected_errors) -- :length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
708
+ # example:
709
+ # validate(text: "This text will be validated", pattern: :"10-20:Xn", expected_errors: [:value, :max_length])
710
+ #
711
+ # Output:
712
+ # if expected_errors and not_expected_errors are not supplied: an array with all detected errors
713
+ # if expected_errors or not_expected_errors supplied: true or false
714
+ # if array of patterns supplied, it will return true or false
715
+ ###############################################
716
+ def StringPattern.validate(text: '', pattern: '', expected_errors: [], not_expected_errors: [], **synonyms)
717
+ text_to_validate = text
718
+ text_to_validate = synonyms[:text_to_validate] if synonyms.keys.include?(:text_to_validate)
719
+ text_to_validate = synonyms[:validate] if synonyms.keys.include?(:validate)
720
+ expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors)
721
+ not_expected_errors = synonyms[:not_errors] if synonyms.keys.include?(:not_errors)
722
+ not_expected_errors = synonyms[:non_expected_errors] if synonyms.keys.include?(:non_expected_errors)
723
+ #:length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
724
+ if (expected_errors.include?(:min_length) or expected_errors.include?(:max_length)) and !expected_errors.include?(:length)
725
+ expected_errors.push(:length)
726
+ end
727
+ if (not_expected_errors.include?(:min_length) or not_expected_errors.include?(:max_length)) and !not_expected_errors.include?(:length)
728
+ not_expected_errors.push(:length)
729
+ end
730
+ if pattern.kind_of?(Array) and pattern.size == 1
731
+ pattern = pattern[0]
732
+ elsif pattern.kind_of?(Array) and pattern.size > 1 then
733
+ total_min_length = 0
734
+ total_max_length = 0
735
+ all_errors_collected = Array.new
736
+ result = true
737
+ num_patt = 0
738
+ patterns = Array.new
739
+ pattern.each {|pat|
740
+ if (pat.kind_of?(String) and (!StringPattern.optimistic or
741
+ (StringPattern.optimistic and pat.to_s.scan(/(\d+)-(\d+):(.+)/).size == 0 and pat.to_s.scan(/^!?(\d+):(.+)/).size == 0))) #fixed text
742
+ symbol_type = ''
743
+ min_length = max_length = pat.length
744
+ elsif pat.kind_of?(Symbol) or (pat.kind_of?(String) and StringPattern.optimistic and
745
+ (pat.to_s.scan(/(\d+)-(\d+):(.+)/).size > 0 or pat.to_s.scan(/^!?(\d+):(.+)/).size > 0))
746
+ patt = StringPattern.analyze(pat)
747
+ min_length = patt.min_length
748
+ max_length = patt.max_length
749
+ symbol_type = patt.symbol_type
750
+ else
751
+ puts "String pattern class not supported (#{pat.class} for #{pat})"
752
+ end
753
+
754
+ patterns.push({pattern: pat, min_length: min_length, max_length: max_length, symbol_type: symbol_type})
755
+
756
+ total_min_length += min_length
757
+ total_max_length += max_length
758
+
759
+ if num_patt == (pattern.size - 1) # i am in the last one
760
+ if text_to_validate.length < total_min_length
761
+ all_errors_collected.push(:length)
762
+ all_errors_collected.push(:min_length)
763
+ end
764
+
765
+ if text_to_validate.length > total_max_length
766
+ all_errors_collected.push(:length)
767
+ all_errors_collected.push(:max_length)
768
+ end
769
+
770
+ end
771
+ num_patt += 1
772
+
773
+
774
+ }
775
+
776
+ num_patt = 0
777
+ patterns.each {|patt|
778
+
779
+ tmp_result = false
780
+ (patt[:min_length]..patt[:max_length]).each {|n|
781
+ res = StringPattern.validate(text: text_to_validate[0..n - 1], pattern: patt[:pattern], not_expected_errors: not_expected_errors)
782
+ if res.kind_of?(Array)
783
+ all_errors_collected += res
784
+ end
785
+
786
+ if res.kind_of?(TrueClass) or (res.kind_of?(Array) and res.size == 0) #valid
787
+ #we pass in the next one the rest of the pattern array list: pattern: pattern[num_patt+1..pattern.size]
788
+ res = StringPattern.validate(text: text_to_validate[n..text_to_validate.length], pattern: pattern[num_patt + 1..pattern.size], expected_errors: expected_errors, not_expected_errors: not_expected_errors)
789
+
790
+ if res.kind_of?(Array)
791
+ if ((all_errors_collected + res) - expected_errors).size > 0
792
+ tmp_result = false
793
+ else
794
+ all_errors_collected += res
795
+ tmp_result = true
796
+ end
797
+ elsif res.kind_of?(TrueClass) then
798
+ tmp_result = true
799
+ end
800
+ return true if tmp_result
801
+ end
802
+ }
803
+
804
+ unless tmp_result
805
+ return false
806
+ end
807
+ num_patt += 1
808
+ }
809
+ return result
810
+ end
811
+
812
+ if (pattern.kind_of?(String) and (!StringPattern.optimistic or
813
+ (StringPattern.optimistic and pattern.to_s.scan(/(\d+)-(\d+):(.+)/).size == 0 and pattern.to_s.scan(/^!?(\d+):(.+)/).size == 0))) #fixed text
814
+ symbol_type = ''
815
+ min_length = max_length = pattern.length
816
+ else #symbol
817
+ patt = StringPattern.analyze(pattern)
818
+ min_length = patt.min_length
819
+ max_length = patt.max_length
820
+ symbol_type = patt.symbol_type
821
+
822
+ required_data = patt.required_data
823
+ excluded_data = patt.excluded_data
824
+ string_set = patt.string_set
825
+ all_characters_set = patt.all_characters_set
826
+
827
+ required_chars = Array.new
828
+ required_data.each {|rd|
829
+ required_chars << rd if rd.size == 1
830
+ }
831
+ if (required_chars.flatten & excluded_data.flatten).size > 0
832
+ puts "pattern argument not valid on StringPattern.validate, a character cannot be required and excluded at the same time: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
833
+ return ''
834
+ end
835
+
836
+ end
837
+
838
+ if text_to_validate.nil?
839
+ return false
840
+ end
841
+ detected_errors = Array.new
842
+
843
+ if text_to_validate.length < min_length
844
+ detected_errors.push(:min_length)
845
+ detected_errors.push(:length)
846
+ end
847
+ if text_to_validate.length > max_length
848
+ detected_errors.push(:max_length)
849
+ detected_errors.push(:length)
850
+ end
851
+
852
+ if symbol_type == '' #fixed text
853
+ if pattern.to_s != text.to_s #not equal
854
+ detected_errors.push(:value)
855
+ detected_errors.push(:required_data)
856
+ end
857
+ else # pattern supplied
858
+ if symbol_type != '@'
859
+ if required_data.size > 0
860
+ required_data.each {|rd|
861
+ if (text_to_validate.chars & rd).size == 0
862
+ detected_errors.push(:value)
863
+ detected_errors.push(:required_data)
864
+ break
865
+ end
866
+ }
867
+ end
868
+ if excluded_data.size > 0
869
+ if (excluded_data & text_to_validate.chars).size > 0
870
+ detected_errors.push(:value)
871
+ detected_errors.push(:excluded_data)
872
+ end
873
+ end
874
+ string_set_not_allowed = all_characters_set - string_set
875
+ text_to_validate.chars.each {|st|
876
+ if string_set_not_allowed.include?(st)
877
+ detected_errors.push(:value)
878
+ detected_errors.push(:string_set_not_allowed)
879
+ break
880
+ end
881
+ }
882
+ else #symbol_type=="@"
883
+ string = text_to_validate
884
+ wrong = %w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@)
885
+ if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings
886
+ if string.index('@').to_i > 0 and
887
+ string[0..(string.index('@') - 1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join == string[0..(string.index('@') - 1)] and
888
+ string[(string.index('@') + 1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join == string[string[(string.index('@') + 1)..-1]]
889
+ error_regular_expression = false
890
+ else
891
+ error_regular_expression = true
892
+ end
893
+ else
894
+ error_regular_expression = true
895
+ end
896
+
897
+ if error_regular_expression
898
+ detected_errors.push(:value)
899
+ end
900
+
901
+ end
902
+ end
903
+
904
+ if expected_errors.size == 0 and not_expected_errors.size == 0
905
+ return detected_errors
906
+ else
907
+ if expected_errors & detected_errors == expected_errors
908
+ if (not_expected_errors & detected_errors).size > 0
909
+ return false
910
+ else
911
+ return true
912
+ end
913
+ else
914
+ return false
915
+ end
916
+ end
917
+ end
918
+
919
+ end
920
+