string_pattern 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: