string_pattern 2.2.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5ea5dcf2857bafae800bd597d980c72aacb0c84b774ba698fc1107e1b9f29cf
4
- data.tar.gz: a69ccae4e72b74692c61cbcf58d987a07c5a320e8d82c14165cba389df49615b
3
+ metadata.gz: 1f63b3742ade3404358eaed22e93f1b2649d8226e51be4c8439c05debba83817
4
+ data.tar.gz: af26e1372da4d040e4e7e4e0a952a965b0b5e156d6d1734f3524e4189709a151
5
5
  SHA512:
6
- metadata.gz: 1d8a2fe7b5923924787234724b7f50a8f3cc860cbd7c01066e7ff7239a611ae3c64f1155e07ffe974344a1a8f91c4b1655230f7d0f92d54607c1e0289334bbab
7
- data.tar.gz: bc3abd91a3885901f6c4e265ef21f4e65fc0053482328d864dd3ac7b31214129bd2c198c78f94976e160a55af6e07631cd32b8478a9e0decd87f04851d265b8d
6
+ metadata.gz: 8c4e0dca5cca256df28d64e4ffde50e7b93267ba8332ce17bb668a3dadf6f60f00a2ab9e6b394023946b935f8aa719ad373ec8fe38c13226a6bf5d5b26650e97
7
+ data.tar.gz: 0ca1ea14d4127961835c6b64e28daf63e61982b0a8a15865532e9e360f343a04790889d7f0ce0cfc653af032515e89a773d921d86d23c3c13912195b01efc085
@@ -0,0 +1,195 @@
1
+ class StringPattern
2
+ ###############################################
3
+ # Analyze the pattern supplied and returns an object of Pattern structure including:
4
+ # min_length, max_length, symbol_type, required_data, excluded_data, data_provided, string_set, all_characters_set
5
+ ###############################################
6
+ def StringPattern.analyze(pattern, silent: false)
7
+ #unless @cache[pattern.to_s].nil?
8
+ # return Pattern.new(@cache[pattern.to_s].min_length.clone, @cache[pattern.to_s].max_length.clone,
9
+ # @cache[pattern.to_s].symbol_type.clone, @cache[pattern.to_s].required_data.clone,
10
+ # @cache[pattern.to_s].excluded_data.clone, @cache[pattern.to_s].data_provided.clone,
11
+ # @cache[pattern.to_s].string_set.clone, @cache[pattern.to_s].all_characters_set.clone, @cache[pattern.to_s].unique.clone)
12
+ #end
13
+ return @cache[pattern.to_s].clone unless @cache[pattern.to_s].nil?
14
+ min_length, max_length, symbol_type = pattern.to_s.scan(/(\d+)-(\d+):(.+)/)[0]
15
+ if min_length.nil?
16
+ min_length, symbol_type = pattern.to_s.scan(/^!?(\d+):(.+)/)[0]
17
+ max_length = min_length
18
+ if min_length.nil?
19
+ puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}" unless silent
20
+ return pattern.to_s
21
+ end
22
+ end
23
+ if symbol_type[-1] == "&"
24
+ symbol_type.chop!
25
+ unique = true
26
+ else
27
+ unique = false
28
+ end
29
+
30
+ symbol_type = "!" + symbol_type if pattern.to_s[0] == "!"
31
+ min_length = min_length.to_i
32
+ max_length = max_length.to_i
33
+
34
+ required_data = Array.new
35
+ excluded_data = Array.new
36
+ required = false
37
+ excluded = false
38
+ data_provided = Array.new
39
+ a = symbol_type
40
+ begin_provided = a.index("[")
41
+ excluded_end_tag = false
42
+ unless begin_provided.nil?
43
+ c = begin_provided + 1
44
+ until c == a.size or (a[c..c] == "]" and a[c..c + 1] != "]]")
45
+ if a[c..c + 1] == "]]"
46
+ data_provided.push("]")
47
+ c = c + 2
48
+ elsif a[c..c + 1] == "%%" and !excluded
49
+ data_provided.push("%")
50
+ c = c + 2
51
+ else
52
+ if a[c..c] == "/" and !excluded
53
+ if a[c..c + 1] == "//"
54
+ data_provided.push(a[c..c])
55
+ if required
56
+ required_data.push([a[c..c]])
57
+ end
58
+ c = c + 1
59
+ else
60
+ if !required
61
+ required = true
62
+ else
63
+ required = false
64
+ end
65
+ end
66
+ else
67
+ if required
68
+ required_data.push([a[c..c]])
69
+ else
70
+ if a[c..c] == "%"
71
+ if a[c..c + 1] == "%%" and excluded
72
+ excluded_data.push([a[c..c]])
73
+ c = c + 1
74
+ else
75
+ if !excluded
76
+ excluded = true
77
+ else
78
+ excluded = false
79
+ excluded_end_tag = true
80
+ end
81
+ end
82
+ else
83
+ if excluded
84
+ excluded_data.push([a[c..c]])
85
+ end
86
+ end
87
+ end
88
+ if excluded == false and excluded_end_tag == false
89
+ data_provided.push(a[c..c])
90
+ end
91
+ excluded_end_tag = false
92
+ end
93
+ c = c + 1
94
+ end
95
+ end
96
+ symbol_type = symbol_type[0..begin_provided].to_s + symbol_type[c..symbol_type.size].to_s
97
+ end
98
+
99
+ required = false
100
+ required_symbol = ""
101
+ if symbol_type.include?("/")
102
+ symbol_type.chars.each { |stc|
103
+ if stc == "/"
104
+ if !required
105
+ required = true
106
+ else
107
+ required = false
108
+ end
109
+ else
110
+ if required
111
+ required_symbol += stc
112
+ end
113
+ end
114
+ }
115
+ end
116
+
117
+ national_set = @national_chars.chars
118
+
119
+ if symbol_type.include?("L")
120
+ alpha_set = ALPHA_SET_LOWER.clone + ALPHA_SET_CAPITAL.clone
121
+ elsif symbol_type.include?("x")
122
+ alpha_set = ALPHA_SET_LOWER.clone
123
+ if symbol_type.include?("X")
124
+ alpha_set = alpha_set + ALPHA_SET_CAPITAL.clone
125
+ end
126
+ elsif symbol_type.include?("X")
127
+ alpha_set = ALPHA_SET_CAPITAL.clone
128
+ else
129
+ alpha_set = []
130
+ end
131
+ if symbol_type.include?("T")
132
+ alpha_set = alpha_set + national_set
133
+ end
134
+
135
+ unless required_symbol.nil?
136
+ if required_symbol.include?("x")
137
+ required_data.push ALPHA_SET_LOWER.clone
138
+ end
139
+ if required_symbol.include?("X")
140
+ required_data.push ALPHA_SET_CAPITAL.clone
141
+ end
142
+ if required_symbol.include?("L")
143
+ required_data.push(ALPHA_SET_CAPITAL.clone + ALPHA_SET_LOWER.clone)
144
+ end
145
+ if required_symbol.include?("T")
146
+ required_data.push national_set
147
+ end
148
+ required_symbol = required_symbol.downcase
149
+ end
150
+ string_set = Array.new
151
+
152
+ all_characters_set = ALPHA_SET_CAPITAL.clone + ALPHA_SET_LOWER.clone + NUMBER_SET.clone + SPECIAL_SET.clone + data_provided + national_set
153
+ if symbol_type.include?("_")
154
+ unless symbol_type.include?("$")
155
+ string_set.push(" ")
156
+ end
157
+ if required_symbol.include?("_")
158
+ required_data.push([" "])
159
+ end
160
+ end
161
+
162
+ #symbol_type = symbol_type.downcase
163
+
164
+ if symbol_type.downcase.include?("x") or symbol_type.downcase.include?("l") or symbol_type.downcase.include?("t")
165
+ string_set = string_set + alpha_set
166
+ end
167
+ if symbol_type.downcase.include?("n")
168
+ string_set = string_set + NUMBER_SET
169
+ end
170
+ if symbol_type.include?("$")
171
+ string_set = string_set + SPECIAL_SET
172
+ end
173
+ if symbol_type.include?("*")
174
+ string_set = string_set + all_characters_set
175
+ end
176
+ if data_provided.size != 0
177
+ string_set = string_set + data_provided
178
+ end
179
+ unless required_symbol.empty?
180
+ if required_symbol.include?("n")
181
+ required_data.push NUMBER_SET.clone
182
+ end
183
+ if required_symbol.include?("$")
184
+ required_data.push SPECIAL_SET.clone
185
+ end
186
+ end
187
+ unless excluded_data.empty?
188
+ string_set = string_set - excluded_data.flatten
189
+ end
190
+ string_set.uniq!
191
+ @cache[pattern.to_s] = Pattern.new(min_length, max_length, symbol_type, required_data, excluded_data, data_provided,
192
+ string_set, all_characters_set, unique)
193
+ return @cache[pattern.to_s].clone
194
+ end
195
+ end
@@ -0,0 +1,579 @@
1
+ class StringPattern
2
+ ###############################################
3
+ # Generate a random string based on the pattern supplied
4
+ # (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:
5
+ # "10-15:Ln/x/".generate #generate method on String class (alias: gen)
6
+ # ['(', :'3:N', ')', :'6-8:N'].generate #generate method on Array class (alias: gen)
7
+ # generate("10-15:Ln/x/") #generate Ruby Kernel method
8
+ # generate(['(', :'3:N', ')', :'6-8:N']) #generate Ruby Kernel method
9
+ # "(,3:N,) ,3:N,-,2:N,-,2:N".split(",").generate #>(937) #generate method on Array class (alias: gen)
10
+ # %w{( 3:N ) 1:_ 3:N - 2:N - 2:N}.gen #generate method on Array class, using alias gen method
11
+ # Input:
12
+ # pattern: array or string of different patterns. A pattern is a string with this info:
13
+ # "length:symbol_type" or "min_length-max_length:symbol_type"
14
+ # In case an array supplied, the positions using a string pattern should be supplied as symbols if StringPattern.optimistic==false
15
+ #
16
+ # These are the possible string patterns you will be able to supply:
17
+ # 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.
18
+ # min_length -- minimum length of the string
19
+ # max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided
20
+ # symbol_type -- the type of the string we want.
21
+ # you can use a combination of any ot these:
22
+ # x for alpha in lowercase
23
+ # X for alpha in capital letters
24
+ # L for all kind of alpha in capital and lower letters
25
+ # T for the national characters defined on StringPattern.national_chars
26
+ # n for number
27
+ # $ for special characters (includes space)
28
+ # _ for space
29
+ # * all characters
30
+ # [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: %%
31
+ # %characters% the characters we don't want on the resulting string. %% to exclude the character %
32
+ # /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: //
33
+ # We can supply 0 to allow empty strings, this character need to be at the beginning
34
+ # If you want to include the character " use \"
35
+ # If you want to include the character \ use \\
36
+ # If you want to include the character [ use \[
37
+ # Other uses:
38
+ # @ for email
39
+ # W for English words, capital and lower
40
+ # w for English words only lower and words separated by underscore
41
+ # P for Spanish words, capital and lower
42
+ # p for Spanish words only lower and words separated by underscore
43
+ # Examples:
44
+ # [:"6:X", :"3-8:_N"]
45
+ # # 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"
46
+ # [:"6-15:L_N", "fixed text", :"3:N"]
47
+ # # 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"
48
+ # "10-20:LN[=#]"
49
+ # # it will return a string of 10-20 characters containing Letters and/or numbers and/or the characters = and #, for example: eiyweQFWeL#do4Vl
50
+ # "30:TN_[#=]/x/"
51
+ # # 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
52
+ # "10:N[%0%]"
53
+ # # 10 characters length containing numbers and excluding the character 0, for example: 3523497757
54
+ # "10:N[%0%/AB/]"
55
+ # # 10 characters length containing numbers and excluding the character 0 and necessary to contain the characters A B, for example: 3AA4AA57BB
56
+ # "!10:N[%0%/AB/]"
57
+ # # it will generate a string that doesn't fulfill the pattern supplied, examples:
58
+ # # a6oMQ4JK9g
59
+ # # /Y<N6Aa[ae
60
+ # # 3444439A34B32
61
+ # "10:N[%0%/AB/]", errors: [:length]
62
+ # # it will generate a string following the pattern and with the errors supplied, in this case, length, example: AB44
63
+ # Output:
64
+ # the generated string
65
+ ###############################################
66
+ def StringPattern.generate(pattern, expected_errors: [], **synonyms)
67
+ tries = 0
68
+ begin
69
+ good_result = true
70
+ tries += 1
71
+ string = ""
72
+
73
+ expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors)
74
+
75
+ if expected_errors.kind_of?(Symbol)
76
+ expected_errors = [expected_errors]
77
+ end
78
+
79
+ if pattern.kind_of?(Array)
80
+ pattern.each { |pat|
81
+ if pat.kind_of?(Array) # for the case it is one of the values
82
+ pat = pat.sample
83
+ end
84
+
85
+ if pat.kind_of?(Symbol)
86
+ if pat.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
87
+ string << StringPattern.generate(pat.to_s, expected_errors: expected_errors)
88
+ else
89
+ string << pat.to_s
90
+ end
91
+ elsif pat.kind_of?(String)
92
+ if @optimistic and pat.to_s.scan(/^!?\d+-?\d*:.+/).size > 0
93
+ string << StringPattern.generate(pat.to_s, expected_errors: expected_errors)
94
+ else
95
+ string << pat
96
+ end
97
+ else
98
+ puts "StringPattern.generate: it seems you supplied wrong array of patterns: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
99
+ return ""
100
+ end
101
+ }
102
+ return string
103
+ elsif pattern.kind_of?(String) or pattern.kind_of?(Symbol)
104
+ patt = StringPattern.analyze(pattern).clone
105
+ return "" unless patt.kind_of?(Struct)
106
+
107
+ min_length = patt.min_length.clone
108
+ max_length = patt.max_length.clone
109
+ symbol_type = patt.symbol_type.clone
110
+
111
+ required_data = patt.required_data.clone
112
+ excluded_data = patt.excluded_data.clone
113
+ string_set = patt.string_set.clone
114
+ all_characters_set = patt.all_characters_set.clone
115
+
116
+ required_chars = Array.new
117
+ unless required_data.size == 0
118
+ required_data.each { |rd|
119
+ required_chars << rd if rd.size == 1
120
+ }
121
+ unless excluded_data.size == 0
122
+ if (required_chars.flatten & excluded_data.flatten).size > 0
123
+ 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}"
124
+ return ""
125
+ end
126
+ end
127
+ end
128
+
129
+ string_set_not_allowed = Array.new
130
+ elsif pattern.kind_of?(Regexp)
131
+ return generate(pattern.to_sp, expected_errors: expected_errors)
132
+ else
133
+ puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
134
+ return pattern.to_s
135
+ end
136
+
137
+ allow_empty = false
138
+ deny_pattern = false
139
+ if symbol_type[0..0] == "!"
140
+ deny_pattern = true
141
+ possible_errors = [:length, :value, :string_set_not_allowed]
142
+ (rand(possible_errors.size) + 1).times {
143
+ expected_errors << possible_errors.sample
144
+ }
145
+ expected_errors.uniq!
146
+ if symbol_type[1..1] == "0"
147
+ allow_empty = true
148
+ end
149
+ elsif symbol_type[0..0] == "0"
150
+ allow_empty = true
151
+ end
152
+
153
+ if expected_errors.include?(:min_length) or expected_errors.include?(:length) or
154
+ expected_errors.include?(:max_length)
155
+ allow_empty = !allow_empty
156
+ elsif expected_errors.include?(:value) or
157
+ expected_errors.include?(:excluded_data) or
158
+ expected_errors.include?(:required_data) or
159
+ expected_errors.include?(:string_set_not_allowed) and allow_empty
160
+ allow_empty = false
161
+ end
162
+
163
+ length = min_length
164
+ symbol_type_orig = symbol_type
165
+
166
+ expected_errors_left = expected_errors.dup
167
+
168
+ symbol_type = symbol_type_orig
169
+
170
+ unless deny_pattern
171
+ if required_data.size == 0 and expected_errors_left.include?(:required_data)
172
+ 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}"
173
+ return ""
174
+ end
175
+
176
+ if excluded_data.size == 0 and expected_errors_left.include?(:excluded_data)
177
+ 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}"
178
+ return ""
179
+ end
180
+
181
+ if expected_errors_left.include?(:string_set_not_allowed)
182
+ string_set_not_allowed = all_characters_set - string_set
183
+
184
+ if string_set_not_allowed.size == 0
185
+ 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}"
186
+ return ""
187
+ end
188
+ end
189
+ end
190
+
191
+ if expected_errors_left.include?(:min_length) or
192
+ expected_errors_left.include?(:max_length) or
193
+ expected_errors_left.include?(:length)
194
+ if expected_errors_left.include?(:min_length) or
195
+ (min_length > 0 and expected_errors_left.include?(:length) and rand(2) == 0)
196
+ if min_length > 0
197
+ if allow_empty
198
+ length = rand(min_length).to_i
199
+ else
200
+ length = rand(min_length - 1).to_i + 1
201
+ end
202
+ if required_data.size > length and required_data.size < min_length
203
+ length = required_data.size
204
+ end
205
+ expected_errors_left.delete(:length)
206
+ expected_errors_left.delete(:min_length)
207
+ else
208
+ 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}"
209
+ return ""
210
+ end
211
+ elsif expected_errors_left.include?(:max_length) or expected_errors_left.include?(:length)
212
+ length = max_length + 1 + rand(max_length).to_i
213
+ expected_errors_left.delete(:length)
214
+ expected_errors_left.delete(:max_length)
215
+ end
216
+ else
217
+ if allow_empty and rand(7) == 1
218
+ length = 0
219
+ else
220
+ if max_length == min_length
221
+ length = min_length
222
+ else
223
+ length = min_length + rand(max_length - min_length + 1)
224
+ end
225
+ end
226
+ end
227
+
228
+ if deny_pattern
229
+ if required_data.size == 0 and expected_errors_left.include?(:required_data)
230
+ expected_errors_left.delete(:required_data)
231
+ end
232
+
233
+ if excluded_data.size == 0 and expected_errors_left.include?(:excluded_data)
234
+ expected_errors_left.delete(:excluded_data)
235
+ end
236
+
237
+ if expected_errors_left.include?(:string_set_not_allowed)
238
+ string_set_not_allowed = all_characters_set - string_set
239
+ if string_set_not_allowed.size == 0
240
+ expected_errors_left.delete(:string_set_not_allowed)
241
+ end
242
+ end
243
+
244
+ if symbol_type == "!@" and expected_errors_left.size == 0 and !expected_errors.include?(:length) and
245
+ (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data))
246
+ expected_errors_left.push(:value)
247
+ end
248
+ end
249
+
250
+ string = ""
251
+ if symbol_type != "@" and symbol_type != "!@" and length != 0 and string_set.size != 0
252
+ if string_set.size != 0
253
+ 1.upto(length) { |i|
254
+ string << string_set.sample.to_s
255
+ }
256
+ end
257
+ if required_data.size > 0
258
+ positions_to_set = (0..(string.size - 1)).to_a
259
+ required_data.each { |rd|
260
+ if (string.chars & rd).size > 0
261
+ rd_to_set = (string.chars & rd).sample
262
+ else
263
+ rd_to_set = rd.sample
264
+ end
265
+ if ((0...string.length).find_all { |i| string[i, 1] == rd_to_set }).size == 0
266
+ if positions_to_set.size == 0
267
+ puts "pattern not valid on StringPattern.generate, not possible to generate a valid string: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
268
+ return ""
269
+ else
270
+ k = positions_to_set.sample
271
+ string[k] = rd_to_set
272
+ positions_to_set.delete(k)
273
+ end
274
+ else
275
+ k = ((0...string.length).find_all { |i| string[i, 1] == rd_to_set }).sample
276
+ positions_to_set.delete(k)
277
+ end
278
+ }
279
+ end
280
+ excluded_data.each { |ed|
281
+ if (string.chars & ed).size > 0
282
+ (string.chars & ed).each { |s|
283
+ string.gsub!(s, string_set.sample)
284
+ }
285
+ end
286
+ }
287
+
288
+ if expected_errors_left.include?(:value)
289
+ string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0
290
+
291
+ if string_set_not_allowed.size == 0
292
+ puts "Not possible to generate a non valid string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
293
+ return ""
294
+ end
295
+ (rand(string.size) + 1).times {
296
+ string[rand(string.size)] = (all_characters_set - string_set).sample
297
+ }
298
+ expected_errors_left.delete(:value)
299
+ end
300
+
301
+ if expected_errors_left.include?(:required_data) and required_data.size > 0
302
+ (rand(required_data.size) + 1).times {
303
+ chars_to_remove = required_data.sample
304
+ chars_to_remove.each { |char_to_remove|
305
+ string.gsub!(char_to_remove, (string_set - chars_to_remove).sample)
306
+ }
307
+ }
308
+ expected_errors_left.delete(:required_data)
309
+ end
310
+
311
+ if expected_errors_left.include?(:excluded_data) and excluded_data.size > 0
312
+ (rand(string.size) + 1).times {
313
+ string[rand(string.size)] = excluded_data.sample.sample
314
+ }
315
+ expected_errors_left.delete(:excluded_data)
316
+ end
317
+
318
+ if expected_errors_left.include?(:string_set_not_allowed)
319
+ string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0
320
+ if string_set_not_allowed.size > 0
321
+ (rand(string.size) + 1).times {
322
+ string[rand(string.size)] = string_set_not_allowed.sample
323
+ }
324
+ expected_errors_left.delete(:string_set_not_allowed)
325
+ end
326
+ end
327
+ elsif (symbol_type == "W" or symbol_type == "P" or symbol_type == "w" or symbol_type == "p") and length > 0
328
+ words = []
329
+ words_short = []
330
+ if symbol_type == "W"
331
+ if @words_camel.empty?
332
+ require "pathname"
333
+ require "json"
334
+ filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/nouns.json"
335
+ nouns = JSON.parse(File.read(filename))
336
+ filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/adjs.json"
337
+ adjs = JSON.parse(File.read(filename))
338
+ nouns = nouns.map(&:to_camel_case)
339
+ adjs = adjs.map(&:to_camel_case)
340
+ @words_camel = adjs + nouns
341
+ @words_camel_short = @words_camel.sample(2000)
342
+ end
343
+ words = @words_camel
344
+ words_short = @words_camel_short
345
+ elsif symbol_type == "w"
346
+ if @words.empty?
347
+ require "pathname"
348
+ require "json"
349
+ filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/nouns.json"
350
+ nouns = JSON.parse(File.read(filename))
351
+ filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/adjs.json"
352
+ adjs = JSON.parse(File.read(filename))
353
+ @words = adjs + nouns
354
+ @words_short = @words.sample(2000)
355
+ end
356
+ words = @words
357
+ words_short = @words_short
358
+ elsif symbol_type == "P"
359
+ if @palabras_camel.empty?
360
+ require "pathname"
361
+ require "json"
362
+ filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "spanish/palabras#{rand(12)}.json"
363
+ palabras = JSON.parse(File.read(filename))
364
+ palabras = palabras.map(&:to_camel_case)
365
+ @palabras_camel = palabras
366
+ @palabras_camel_short = @palabras_camel.sample(2000)
367
+ end
368
+ words = @palabras_camel
369
+ words_short = @palabras_camel_short
370
+ elsif symbol_type == "p"
371
+ if @palabras.empty?
372
+ require "pathname"
373
+ require "json"
374
+ filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "spanish/palabras#{rand(12)}.json"
375
+ palabras = JSON.parse(File.read(filename))
376
+ @palabras = palabras
377
+ @palabras_short = @palabras.sample(2000)
378
+ end
379
+ words = @palabras
380
+ words_short = @palabras_short
381
+ end
382
+
383
+ wordr = ""
384
+ wordr_array = []
385
+ tries = 0
386
+ while wordr.length < min_length
387
+ tries += 1
388
+ length = max_length - wordr.length
389
+ if tries > 1000
390
+ wordr += "A" * length
391
+ break
392
+ end
393
+ if symbol_type == "w" or symbol_type == "p"
394
+ length = length - 1 if wordr_array.size > 0
395
+ res = (words_short.select { |word| word.length <= length && word.length != length - 1 && word.length != length - 2 && word.length != length - 3 }).sample.to_s
396
+ unless res.to_s == ""
397
+ wordr_array << res
398
+ wordr = wordr_array.join(@word_separator)
399
+ end
400
+ else
401
+ wordr += (words_short.select { |word| word.length <= length && word.length != length - 1 && word.length != length - 2 && word.length != length - 3 }).sample.to_s
402
+ end
403
+ if (tries % 100) == 0
404
+ words_short = words.sample(2000)
405
+ end
406
+ end
407
+ good_result = true
408
+ string = wordr
409
+ elsif (symbol_type == "@" or symbol_type == "!@") and length > 0
410
+ if min_length > 6 and length < 6
411
+ length = 6
412
+ end
413
+ if deny_pattern and
414
+ (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data) or
415
+ expected_errors.include?(:string_set_not_allowed))
416
+ expected_errors_left.push(:value)
417
+ expected_errors.push(:value)
418
+ expected_errors.uniq!
419
+ expected_errors_left.uniq!
420
+ end
421
+
422
+ expected_errors_left_orig = expected_errors_left.dup
423
+ tries = 0
424
+
425
+ begin
426
+ expected_errors_left = expected_errors_left_orig.dup
427
+ tries += 1
428
+ string = ""
429
+ alpha_set = ALPHA_SET_LOWER.clone + ALPHA_SET_CAPITAL.clone
430
+ string_set = alpha_set + NUMBER_SET.clone + ["."] + ["_"] + ["-"]
431
+ string_set_not_allowed = all_characters_set - string_set
432
+
433
+ extension = "."
434
+ at_sign = "@"
435
+
436
+ if expected_errors_left.include?(:value)
437
+ if rand(2) == 1
438
+ extension = (all_characters_set - ["."]).sample.dup
439
+ expected_errors_left.delete(:value)
440
+ expected_errors_left.delete(:required_data)
441
+ end
442
+
443
+ if rand(2) == 1
444
+ 1.upto(rand(7)) { |i|
445
+ extension << alpha_set.sample.downcase
446
+ }
447
+
448
+ (rand(extension.size) + 1).times {
449
+ extension[rand(extension.size)] = (string_set - alpha_set - ["."]).sample
450
+ }
451
+
452
+ expected_errors_left.delete(:value)
453
+ else
454
+ 1.upto(rand(3) + 2) { |i|
455
+ extension << alpha_set.sample.downcase
456
+ }
457
+ end
458
+
459
+ if rand(2) == 1
460
+ at_sign = (string_set - ["@"]).sample.dup
461
+ expected_errors_left.delete(:value)
462
+ expected_errors_left.delete(:required_data)
463
+ end
464
+ else
465
+ if length > 6
466
+ 1.upto(rand(3) + 2) { |i|
467
+ extension << alpha_set.sample.downcase
468
+ }
469
+ else
470
+ 1.upto(2) { |i|
471
+ extension << alpha_set.sample.downcase
472
+ }
473
+ end
474
+ end
475
+ length_e = length - extension.size - 1
476
+ length1 = rand(length_e - 1) + 1
477
+ length2 = length_e - length1
478
+ 1.upto(length1) { |i| string << string_set.sample }
479
+
480
+ string << at_sign
481
+
482
+ domain = ""
483
+ domain_set = alpha_set + NUMBER_SET.clone + ["."] + ["-"]
484
+ 1.upto(length2) { |i|
485
+ domain << domain_set.sample.downcase
486
+ }
487
+
488
+ if expected_errors.include?(:value) and rand(2) == 1 and domain.size > 0
489
+ (rand(domain.size) + 1).times {
490
+ domain[rand(domain.size)] = (all_characters_set - domain_set).sample
491
+ }
492
+ expected_errors_left.delete(:value)
493
+ end
494
+
495
+ string << domain << extension
496
+
497
+ if expected_errors_left.include?(:value) or expected_errors_left.include?(:string_set_not_allowed)
498
+ (rand(string.size) + 1).times {
499
+ string[rand(string.size)] = string_set_not_allowed.sample
500
+ }
501
+ expected_errors_left.delete(:value)
502
+ expected_errors_left.delete(:string_set_not_allowed)
503
+ end
504
+
505
+ error_regular_expression = false
506
+
507
+ if deny_pattern and expected_errors.include?(:length)
508
+ good_result = true #it is already with wrong length
509
+ else
510
+ # I'm doing this because many times the regular expression checking hangs with these characters
511
+ wrong = %w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@)
512
+ if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings
513
+ if string.index("@").to_i > 0 and
514
+ string[0..(string.index("@") - 1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join == string[0..(string.index("@") - 1)] and
515
+ string[(string.index("@") + 1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join == string[string[(string.index("@") + 1)..-1]]
516
+ error_regular_expression = false
517
+ else
518
+ error_regular_expression = true
519
+ end
520
+ else
521
+ error_regular_expression = true
522
+ end
523
+
524
+ if expected_errors.size == 0
525
+ if error_regular_expression
526
+ good_result = false
527
+ else
528
+ good_result = true
529
+ end
530
+ elsif expected_errors_left.size == 0 and
531
+ (expected_errors - [:length, :min_length, :max_length]).size == 0
532
+ good_result = true
533
+ elsif expected_errors != [:length]
534
+ if !error_regular_expression
535
+ good_result = false
536
+ elsif expected_errors.include?(:value)
537
+ good_result = true
538
+ end
539
+ end
540
+ end
541
+ end until good_result or tries > 100
542
+ unless good_result
543
+ puts "Not possible to generate an email on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
544
+ return ""
545
+ end
546
+ end
547
+ if @dont_repeat
548
+ if @cache_values[pattern.to_s].nil?
549
+ @cache_values[pattern.to_s] = Array.new()
550
+ @cache_values[pattern.to_s].push(string)
551
+ good_result = true
552
+ elsif @cache_values[pattern.to_s].include?(string)
553
+ good_result = false
554
+ else
555
+ @cache_values[pattern.to_s].push(string)
556
+ good_result = true
557
+ end
558
+ end
559
+ if pattern.kind_of?(Symbol) and patt.unique
560
+ if @cache_values[pattern.__id__].nil?
561
+ @cache_values[pattern.__id__] = Array.new()
562
+ @cache_values[pattern.__id__].push(string)
563
+ good_result = true
564
+ elsif @cache_values[pattern.__id__].include?(string)
565
+ good_result = false
566
+ else
567
+ @cache_values[pattern.__id__].push(string)
568
+ good_result = true
569
+ end
570
+ end
571
+ end until good_result or tries > 10000
572
+ unless good_result
573
+ puts "Not possible to generate the string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}"
574
+ 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"
575
+ return ""
576
+ end
577
+ return string
578
+ end
579
+ end
@@ -0,0 +1,220 @@
1
+ class StringPattern
2
+ ##############################################
3
+ # This method is defined to validate if the text_to_validate supplied follows the pattern
4
+ # It works also with array of patterns but in that case will return only true or false
5
+ # input:
6
+ # text (String) (synonyms: text_to_validate, validate) -- The text to validate
7
+ # pattern -- symbol with this info: "length:symbol_type" or "min_length-max_length:symbol_type"
8
+ # min_length -- minimum length of the string
9
+ # max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided
10
+ # symbol_type -- the type of the string we want.
11
+ # expected_errors (Array of symbols) (optional) (synonyms: errors) -- :length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
12
+ # 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
13
+ # example:
14
+ # validate(text: "This text will be validated", pattern: :"10-20:Xn", expected_errors: [:value, :max_length])
15
+ #
16
+ # Output:
17
+ # if expected_errors and not_expected_errors are not supplied: an array with all detected errors
18
+ # if expected_errors or not_expected_errors supplied: true or false
19
+ # if array of patterns supplied, it will return true or false
20
+ ###############################################
21
+ def StringPattern.validate(text: "", pattern: "", expected_errors: [], not_expected_errors: [], **synonyms)
22
+ text_to_validate = text
23
+ text_to_validate = synonyms[:text_to_validate] if synonyms.keys.include?(:text_to_validate)
24
+ text_to_validate = synonyms[:validate] if synonyms.keys.include?(:validate)
25
+ expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors)
26
+ not_expected_errors = synonyms[:not_errors] if synonyms.keys.include?(:not_errors)
27
+ not_expected_errors = synonyms[:non_expected_errors] if synonyms.keys.include?(:non_expected_errors)
28
+ #:length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed
29
+ if (expected_errors.include?(:min_length) or expected_errors.include?(:max_length)) and !expected_errors.include?(:length)
30
+ expected_errors.push(:length)
31
+ end
32
+ if (not_expected_errors.include?(:min_length) or not_expected_errors.include?(:max_length)) and !not_expected_errors.include?(:length)
33
+ not_expected_errors.push(:length)
34
+ end
35
+ if pattern.kind_of?(Array) and pattern.size == 1
36
+ pattern = pattern[0]
37
+ elsif pattern.kind_of?(Array) and pattern.size > 1
38
+ total_min_length = 0
39
+ total_max_length = 0
40
+ all_errors_collected = Array.new
41
+ result = true
42
+ num_patt = 0
43
+ patterns = Array.new
44
+ pattern.each { |pat|
45
+ if (pat.kind_of?(String) and (!StringPattern.optimistic or
46
+ (StringPattern.optimistic and pat.to_s.scan(/(\d+)-(\d+):(.+)/).size == 0 and pat.to_s.scan(/^!?(\d+):(.+)/).size == 0))) #fixed text
47
+ symbol_type = ""
48
+ min_length = max_length = pat.length
49
+ elsif pat.kind_of?(Symbol) or (pat.kind_of?(String) and StringPattern.optimistic and
50
+ (pat.to_s.scan(/(\d+)-(\d+):(.+)/).size > 0 or pat.to_s.scan(/^!?(\d+):(.+)/).size > 0))
51
+ #patt = Marshal.load(Marshal.dump(StringPattern.analyze(pat))) #deep copy
52
+ patt = StringPattern.analyze(pat).clone
53
+ min_length = patt.min_length.clone
54
+ max_length = patt.max_length.clone
55
+ symbol_type = patt.symbol_type.clone
56
+ else
57
+ puts "String pattern class not supported (#{pat.class} for #{pat})"
58
+ return false
59
+ end
60
+
61
+ patterns.push({ pattern: pat, min_length: min_length, max_length: max_length, symbol_type: symbol_type })
62
+
63
+ total_min_length += min_length
64
+ total_max_length += max_length
65
+
66
+ if num_patt == (pattern.size - 1) # i am in the last one
67
+ if text_to_validate.length < total_min_length
68
+ all_errors_collected.push(:length)
69
+ all_errors_collected.push(:min_length)
70
+ end
71
+
72
+ if text_to_validate.length > total_max_length
73
+ all_errors_collected.push(:length)
74
+ all_errors_collected.push(:max_length)
75
+ end
76
+ end
77
+ num_patt += 1
78
+ }
79
+
80
+ num_patt = 0
81
+ patterns.each { |patt|
82
+ tmp_result = false
83
+ (patt[:min_length]..patt[:max_length]).each { |n|
84
+ res = StringPattern.validate(text: text_to_validate[0..n - 1], pattern: patt[:pattern], not_expected_errors: not_expected_errors)
85
+ if res.kind_of?(Array)
86
+ all_errors_collected += res
87
+ end
88
+
89
+ if res.kind_of?(TrueClass) or (res.kind_of?(Array) and res.size == 0) #valid
90
+ #we pass in the next one the rest of the pattern array list: pattern: pattern[num_patt+1..pattern.size]
91
+ 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)
92
+
93
+ if res.kind_of?(Array)
94
+ if ((all_errors_collected + res) - expected_errors).size > 0
95
+ tmp_result = false
96
+ else
97
+ all_errors_collected += res
98
+ tmp_result = true
99
+ end
100
+ elsif res.kind_of?(TrueClass)
101
+ tmp_result = true
102
+ end
103
+ return true if tmp_result
104
+ end
105
+ }
106
+
107
+ unless tmp_result
108
+ return false
109
+ end
110
+ num_patt += 1
111
+ }
112
+ return result
113
+ end
114
+
115
+ if (pattern.kind_of?(String) and (!StringPattern.optimistic or
116
+ (StringPattern.optimistic and pattern.to_s.scan(/(\d+)-(\d+):(.+)/).size == 0 and pattern.to_s.scan(/^!?(\d+):(.+)/).size == 0))) #fixed text
117
+ symbol_type = ""
118
+ min_length = max_length = pattern.length
119
+ else #symbol
120
+ #patt = Marshal.load(Marshal.dump(StringPattern.analyze(pattern))) #deep copy
121
+ patt = StringPattern.analyze(pattern).clone
122
+ min_length = patt.min_length.clone
123
+ max_length = patt.max_length.clone
124
+ symbol_type = patt.symbol_type.clone
125
+
126
+ required_data = patt.required_data.clone
127
+ excluded_data = patt.excluded_data.clone
128
+ string_set = patt.string_set.clone
129
+ all_characters_set = patt.all_characters_set.clone
130
+
131
+ required_chars = Array.new
132
+ required_data.each { |rd|
133
+ required_chars << rd if rd.size == 1
134
+ }
135
+ if (required_chars.flatten & excluded_data.flatten).size > 0
136
+ 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}"
137
+ return ""
138
+ end
139
+ end
140
+
141
+ if text_to_validate.nil?
142
+ return false
143
+ end
144
+ detected_errors = Array.new
145
+
146
+ if text_to_validate.length < min_length
147
+ detected_errors.push(:min_length)
148
+ detected_errors.push(:length)
149
+ end
150
+ if text_to_validate.length > max_length
151
+ detected_errors.push(:max_length)
152
+ detected_errors.push(:length)
153
+ end
154
+
155
+ if symbol_type == "" #fixed text
156
+ if pattern.to_s != text.to_s #not equal
157
+ detected_errors.push(:value)
158
+ detected_errors.push(:required_data)
159
+ end
160
+ else # pattern supplied
161
+ if symbol_type != "@"
162
+ if required_data.size > 0
163
+ required_data.each { |rd|
164
+ if (text_to_validate.chars & rd).size == 0
165
+ detected_errors.push(:value)
166
+ detected_errors.push(:required_data)
167
+ break
168
+ end
169
+ }
170
+ end
171
+ if excluded_data.size > 0
172
+ if (excluded_data.flatten & text_to_validate.chars).size > 0
173
+ detected_errors.push(:value)
174
+ detected_errors.push(:excluded_data)
175
+ end
176
+ end
177
+ string_set_not_allowed = all_characters_set - string_set
178
+ text_to_validate.chars.each { |st|
179
+ if string_set_not_allowed.include?(st)
180
+ detected_errors.push(:value)
181
+ detected_errors.push(:string_set_not_allowed)
182
+ break
183
+ end
184
+ }
185
+ else #symbol_type=="@"
186
+ string = text_to_validate
187
+ wrong = %w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@)
188
+ if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings
189
+ if string.index("@").to_i > 0 and
190
+ string[0..(string.index("@") - 1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join == string[0..(string.index("@") - 1)] and
191
+ string[(string.index("@") + 1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join == string[string[(string.index("@") + 1)..-1]]
192
+ error_regular_expression = false
193
+ else
194
+ error_regular_expression = true
195
+ end
196
+ else
197
+ error_regular_expression = true
198
+ end
199
+
200
+ if error_regular_expression
201
+ detected_errors.push(:value)
202
+ end
203
+ end
204
+ end
205
+
206
+ if expected_errors.size == 0 and not_expected_errors.size == 0
207
+ return detected_errors.uniq
208
+ else
209
+ if expected_errors & detected_errors == expected_errors
210
+ if (not_expected_errors & detected_errors).size > 0
211
+ return false
212
+ else
213
+ return true
214
+ end
215
+ else
216
+ return false
217
+ end
218
+ end
219
+ end
220
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: string_pattern
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Ruiz
@@ -60,6 +60,9 @@ files:
60
60
  - data/spanish/palabras8.json
61
61
  - data/spanish/palabras9.json
62
62
  - lib/string/pattern/add_to_ruby.rb
63
+ - lib/string/pattern/analyze.rb
64
+ - lib/string/pattern/generate.rb
65
+ - lib/string/pattern/validate.rb
63
66
  - lib/string_pattern.rb
64
67
  homepage: https://github.com/MarioRuiz/string_pattern
65
68
  licenses: