class StringPattern
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.
If true it will add 'generate' and 'validate' methods to the classes: Array, String and Symbol. Also it will add 'generate' method to Kernel aliases: 'gen' for 'generate' and 'val' for 'validate' Examples of use: "(,3:N,) ,3:N,-,2:N,-,2:N".split(",").generate #>(937) 980-65-05 %w{( 3:N ) 1:_ 3:N - 2:N - 2:N}.gen #>(045) 448-63-09 ["1:L", "5-10:LN", "-", "3:N"].gen #>zqWihV-746 gen("10:N") #>3433409877 "20-30:@".gen #>dkj34MljjJD-df@jfdluul.dfu "10:L/N/[/-./%d%]".validate("12ds6f--.s") #>[:value, :string_set_not_allowed] "20-40:@".validate(my_email)
national_chars
: (Array
, default: english alphabet)
Set of characters that will be used when using T pattern
optimistic: (TrueFalse, default: true)
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.
dont_repeat
: (TrueFalse, default: false)
If you want to generate for example 1000 strings and be sure all those strings are different you can set it to true
default_infinite
: (Integer, default: 10)
In case using regular expressions the maximum when using * or + for repetitions
word_separator
: (String
, default: '_')
When generating words using symbol types 'w' or 'p' the character to separate the english or spanish words.
Constants
- ALPHA_SET_CAPITAL
- ALPHA_SET_LOWER
- NUMBER_SET
- Pattern
- SPECIAL_SET
Attributes
Public Class Methods
Analyze the pattern supplied and returns an object of Pattern
structure including: min_length, max_length, symbol_type, required_data, excluded_data, data_provided, string_set, all_characters_set
# File lib/string/pattern/analyze.rb, line 6 def StringPattern.analyze(pattern, silent: false) #unless @cache[pattern.to_s].nil? # return Pattern.new(@cache[pattern.to_s].min_length.clone, @cache[pattern.to_s].max_length.clone, # @cache[pattern.to_s].symbol_type.clone, @cache[pattern.to_s].required_data.clone, # @cache[pattern.to_s].excluded_data.clone, @cache[pattern.to_s].data_provided.clone, # @cache[pattern.to_s].string_set.clone, @cache[pattern.to_s].all_characters_set.clone, @cache[pattern.to_s].unique.clone) #end return @cache[pattern.to_s].clone unless @cache[pattern.to_s].nil? min_length, max_length, symbol_type = pattern.to_s.scan(/(\d+)-(\d+):(.+)/)[0] if min_length.nil? min_length, symbol_type = pattern.to_s.scan(/^!?(\d+):(.+)/)[0] max_length = min_length if min_length.nil? puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}" unless silent return pattern.to_s end end if symbol_type[-1] == "&" symbol_type.chop! unique = true else unique = false end symbol_type = "!" + symbol_type if pattern.to_s[0] == "!" min_length = min_length.to_i max_length = max_length.to_i required_data = Array.new excluded_data = Array.new required = false excluded = false data_provided = Array.new a = symbol_type begin_provided = a.index("[") excluded_end_tag = false unless begin_provided.nil? c = begin_provided + 1 until c == a.size or (a[c..c] == "]" and a[c..c + 1] != "]]") if a[c..c + 1] == "]]" data_provided.push("]") c = c + 2 elsif a[c..c + 1] == "%%" and !excluded data_provided.push("%") c = c + 2 else if a[c..c] == "/" and !excluded if a[c..c + 1] == "//" data_provided.push(a[c..c]) if required required_data.push([a[c..c]]) end c = c + 1 else if !required required = true else required = false end end else if required required_data.push([a[c..c]]) else if a[c..c] == "%" if a[c..c + 1] == "%%" and excluded excluded_data.push([a[c..c]]) c = c + 1 else if !excluded excluded = true else excluded = false excluded_end_tag = true end end else if excluded excluded_data.push([a[c..c]]) end end end if excluded == false and excluded_end_tag == false data_provided.push(a[c..c]) end excluded_end_tag = false end c = c + 1 end end symbol_type = symbol_type[0..begin_provided].to_s + symbol_type[c..symbol_type.size].to_s end required = false required_symbol = "" if symbol_type.include?("/") symbol_type.chars.each { |stc| if stc == "/" if !required required = true else required = false end else if required required_symbol += stc end end } end national_set = @national_chars.chars if symbol_type.include?("L") alpha_set = ALPHA_SET_LOWER.clone + ALPHA_SET_CAPITAL.clone elsif symbol_type.include?("x") alpha_set = ALPHA_SET_LOWER.clone if symbol_type.include?("X") alpha_set = alpha_set + ALPHA_SET_CAPITAL.clone end elsif symbol_type.include?("X") alpha_set = ALPHA_SET_CAPITAL.clone else alpha_set = [] end if symbol_type.include?("T") alpha_set = alpha_set + national_set end unless required_symbol.nil? if required_symbol.include?("x") required_data.push ALPHA_SET_LOWER.clone end if required_symbol.include?("X") required_data.push ALPHA_SET_CAPITAL.clone end if required_symbol.include?("L") required_data.push(ALPHA_SET_CAPITAL.clone + ALPHA_SET_LOWER.clone) end if required_symbol.include?("T") required_data.push national_set end required_symbol = required_symbol.downcase end string_set = Array.new all_characters_set = ALPHA_SET_CAPITAL.clone + ALPHA_SET_LOWER.clone + NUMBER_SET.clone + SPECIAL_SET.clone + data_provided + national_set if symbol_type.include?("_") unless symbol_type.include?("$") string_set.push(" ") end if required_symbol.include?("_") required_data.push([" "]) end end #symbol_type = symbol_type.downcase if symbol_type.downcase.include?("x") or symbol_type.downcase.include?("l") or symbol_type.downcase.include?("t") string_set = string_set + alpha_set end if symbol_type.downcase.include?("n") string_set = string_set + NUMBER_SET end if symbol_type.include?("$") string_set = string_set + SPECIAL_SET end if symbol_type.include?("*") string_set = string_set + all_characters_set end if data_provided.size != 0 string_set = string_set + data_provided end unless required_symbol.empty? if required_symbol.include?("n") required_data.push NUMBER_SET.clone end if required_symbol.include?("$") required_data.push SPECIAL_SET.clone end end unless excluded_data.empty? string_set = string_set - excluded_data.flatten end string_set.uniq! @cache[pattern.to_s] = Pattern.new(min_length, max_length, symbol_type, required_data, excluded_data, data_provided, string_set, all_characters_set, unique) return @cache[pattern.to_s].clone end
Generate a random string based on the pattern supplied (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:
"10-15:Ln/x/".generate #generate method on String class (alias: gen) ['(', :'3:N', ')', :'6-8:N'].generate #generate method on Array class (alias: gen) generate("10-15:Ln/x/") #generate Ruby Kernel method generate(['(', :'3:N', ')', :'6-8:N']) #generate Ruby Kernel method "(,3:N,) ,3:N,-,2:N,-,2:N".split(",").generate #>(937) #generate method on Array class (alias: gen) %w{( 3:N ) 1:_ 3:N - 2:N - 2:N}.gen #generate method on Array class, using alias gen method
Input:
pattern: array or string of different patterns. A pattern is a string with this info: "length:symbol_type" or "min_length-max_length:symbol_type" In case an array supplied, the positions using a string pattern should be supplied as symbols if StringPattern.optimistic==false
These are the possible string patterns you will be able to supply:
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. min_length -- minimum length of the string max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided symbol_type -- the type of the string we want. you can use a combination of any ot these: x for alpha in lowercase X for alpha in capital letters L for all kind of alpha in capital and lower letters T for the national characters defined on StringPattern.national_chars n for number $ for special characters (includes space) _ for space * all characters [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: %% %characters% the characters we don't want on the resulting string. %% to exclude the character % /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: // We can supply 0 to allow empty strings, this character need to be at the beginning If you want to include the character " use \" If you want to include the character \ use \\ If you want to include the character [ use \[ Other uses: @ for email W for English words, capital and lower w for English words only lower and words separated by underscore P for Spanish words, capital and lower p for Spanish words only lower and words separated by underscore
Examples:
[:"6:X", :"3-8:_N"] # 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" [:"6-15:L_N", "fixed text", :"3:N"] # 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" "10-20:LN[=#]" # it will return a string of 10-20 characters containing Letters and/or numbers and/or the characters = and #, for example: eiyweQFWeL#do4Vl "30:TN_[#=]/x/" # 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 "10:N[%0%]" # 10 characters length containing numbers and excluding the character 0, for example: 3523497757 "10:N[%0%/AB/]" # 10 characters length containing numbers and excluding the character 0 and necessary to contain the characters A B, for example: 3AA4AA57BB "!10:N[%0%/AB/]" # it will generate a string that doesn't fulfill the pattern supplied, examples: # a6oMQ4JK9g # /Y<N6Aa[ae # 3444439A34B32 "10:N[%0%/AB/]", errors: [:length] # it will generate a string following the pattern and with the errors supplied, in this case, length, example: AB44
Output:
the generated string
# File lib/string/pattern/generate.rb, line 66 def StringPattern.generate(pattern, expected_errors: [], **synonyms) tries = 0 begin good_result = true tries += 1 string = "" expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors) if expected_errors.kind_of?(Symbol) expected_errors = [expected_errors] end if pattern.kind_of?(Array) pattern.each { |pat| if pat.kind_of?(Array) # for the case it is one of the values pat = pat.sample end if pat.kind_of?(Symbol) if pat.to_s.scan(/^!?\d+-?\d*:.+/).size > 0 string << StringPattern.generate(pat.to_s, expected_errors: expected_errors) else string << pat.to_s end elsif pat.kind_of?(String) if @optimistic and pat.to_s.scan(/^!?\d+-?\d*:.+/).size > 0 string << StringPattern.generate(pat.to_s, expected_errors: expected_errors) else string << pat end else puts "StringPattern.generate: it seems you supplied wrong array of patterns: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}" return "" end } return string elsif pattern.kind_of?(String) or pattern.kind_of?(Symbol) patt = StringPattern.analyze(pattern).clone return "" unless patt.kind_of?(Struct) min_length = patt.min_length.clone max_length = patt.max_length.clone symbol_type = patt.symbol_type.clone required_data = patt.required_data.clone excluded_data = patt.excluded_data.clone string_set = patt.string_set.clone all_characters_set = patt.all_characters_set.clone required_chars = Array.new unless required_data.size == 0 required_data.each { |rd| required_chars << rd if rd.size == 1 } unless excluded_data.size == 0 if (required_chars.flatten & excluded_data.flatten).size > 0 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}" return "" end end end string_set_not_allowed = Array.new elsif pattern.kind_of?(Regexp) return generate(pattern.to_sp, expected_errors: expected_errors) else puts "pattern argument not valid on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}" return pattern.to_s end allow_empty = false deny_pattern = false if symbol_type[0..0] == "!" deny_pattern = true possible_errors = [:length, :value, :string_set_not_allowed] (rand(possible_errors.size) + 1).times { expected_errors << possible_errors.sample } expected_errors.uniq! if symbol_type[1..1] == "0" allow_empty = true end elsif symbol_type[0..0] == "0" allow_empty = true end if expected_errors.include?(:min_length) or expected_errors.include?(:length) or expected_errors.include?(:max_length) allow_empty = !allow_empty elsif expected_errors.include?(:value) or expected_errors.include?(:excluded_data) or expected_errors.include?(:required_data) or expected_errors.include?(:string_set_not_allowed) and allow_empty allow_empty = false end length = min_length symbol_type_orig = symbol_type expected_errors_left = expected_errors.dup symbol_type = symbol_type_orig unless deny_pattern if required_data.size == 0 and expected_errors_left.include?(:required_data) 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}" return "" end if excluded_data.size == 0 and expected_errors_left.include?(:excluded_data) 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}" return "" end if expected_errors_left.include?(:string_set_not_allowed) string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0 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}" return "" end end end if expected_errors_left.include?(:min_length) or expected_errors_left.include?(:max_length) or expected_errors_left.include?(:length) if expected_errors_left.include?(:min_length) or (min_length > 0 and expected_errors_left.include?(:length) and rand(2) == 0) if min_length > 0 if allow_empty length = rand(min_length).to_i else length = rand(min_length - 1).to_i + 1 end if required_data.size > length and required_data.size < min_length length = required_data.size end expected_errors_left.delete(:length) expected_errors_left.delete(:min_length) else 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}" return "" end elsif expected_errors_left.include?(:max_length) or expected_errors_left.include?(:length) length = max_length + 1 + rand(max_length).to_i expected_errors_left.delete(:length) expected_errors_left.delete(:max_length) end else if allow_empty and rand(7) == 1 length = 0 else if max_length == min_length length = min_length else length = min_length + rand(max_length - min_length + 1) end end end if deny_pattern if required_data.size == 0 and expected_errors_left.include?(:required_data) expected_errors_left.delete(:required_data) end if excluded_data.size == 0 and expected_errors_left.include?(:excluded_data) expected_errors_left.delete(:excluded_data) end if expected_errors_left.include?(:string_set_not_allowed) string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0 expected_errors_left.delete(:string_set_not_allowed) end end if symbol_type == "!@" and expected_errors_left.size == 0 and !expected_errors.include?(:length) and (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data)) expected_errors_left.push(:value) end end string = "" if symbol_type != "@" and symbol_type != "!@" and length != 0 and string_set.size != 0 if string_set.size != 0 1.upto(length) { |i| string << string_set.sample.to_s } end if required_data.size > 0 positions_to_set = (0..(string.size - 1)).to_a required_data.each { |rd| if (string.chars & rd).size > 0 rd_to_set = (string.chars & rd).sample else rd_to_set = rd.sample end if ((0...string.length).find_all { |i| string[i, 1] == rd_to_set }).size == 0 if positions_to_set.size == 0 puts "pattern not valid on StringPattern.generate, not possible to generate a valid string: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}" return "" else k = positions_to_set.sample string[k] = rd_to_set positions_to_set.delete(k) end else k = ((0...string.length).find_all { |i| string[i, 1] == rd_to_set }).sample positions_to_set.delete(k) end } end excluded_data.each { |ed| if (string.chars & ed).size > 0 (string.chars & ed).each { |s| string.gsub!(s, string_set.sample) } end } if expected_errors_left.include?(:value) string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0 if string_set_not_allowed.size == 0 puts "Not possible to generate a non valid string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}" return "" end (rand(string.size) + 1).times { string[rand(string.size)] = (all_characters_set - string_set).sample } expected_errors_left.delete(:value) end if expected_errors_left.include?(:required_data) and required_data.size > 0 (rand(required_data.size) + 1).times { chars_to_remove = required_data.sample chars_to_remove.each { |char_to_remove| string.gsub!(char_to_remove, (string_set - chars_to_remove).sample) } } expected_errors_left.delete(:required_data) end if expected_errors_left.include?(:excluded_data) and excluded_data.size > 0 (rand(string.size) + 1).times { string[rand(string.size)] = excluded_data.sample.sample } expected_errors_left.delete(:excluded_data) end if expected_errors_left.include?(:string_set_not_allowed) string_set_not_allowed = all_characters_set - string_set if string_set_not_allowed.size == 0 if string_set_not_allowed.size > 0 (rand(string.size) + 1).times { string[rand(string.size)] = string_set_not_allowed.sample } expected_errors_left.delete(:string_set_not_allowed) end end elsif (symbol_type == "W" or symbol_type == "P" or symbol_type == "w" or symbol_type == "p") and length > 0 words = [] words_short = [] if symbol_type == "W" if @words_camel.empty? require "pathname" require "json" filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/nouns.json" nouns = JSON.parse(File.read(filename)) filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/adjs.json" adjs = JSON.parse(File.read(filename)) nouns = nouns.map(&:to_camel_case) adjs = adjs.map(&:to_camel_case) @words_camel = adjs + nouns @words_camel_short = @words_camel.sample(2000) end words = @words_camel words_short = @words_camel_short elsif symbol_type == "w" if @words.empty? require "pathname" require "json" filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/nouns.json" nouns = JSON.parse(File.read(filename)) filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "english/adjs.json" adjs = JSON.parse(File.read(filename)) @words = adjs + nouns @words_short = @words.sample(2000) end words = @words words_short = @words_short elsif symbol_type == "P" if @palabras_camel.empty? require "pathname" require "json" filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "spanish/palabras#{rand(12)}.json" palabras = JSON.parse(File.read(filename)) palabras = palabras.map(&:to_camel_case) @palabras_camel = palabras @palabras_camel_short = @palabras_camel.sample(2000) end words = @palabras_camel words_short = @palabras_camel_short elsif symbol_type == "p" if @palabras.empty? require "pathname" require "json" filename = File.join Pathname(File.dirname(__FILE__)), "../../../data", "spanish/palabras#{rand(12)}.json" palabras = JSON.parse(File.read(filename)) @palabras = palabras @palabras_short = @palabras.sample(2000) end words = @palabras words_short = @palabras_short end wordr = "" wordr_array = [] tries = 0 while wordr.length < min_length tries += 1 length = max_length - wordr.length if tries > 1000 wordr += "A" * length break end if symbol_type == "w" or symbol_type == "p" length = length - 1 if wordr_array.size > 0 res = (words_short.select { |word| word.length <= length && word.length != length - 1 && word.length != length - 2 && word.length != length - 3 }).sample.to_s unless res.to_s == "" wordr_array << res wordr = wordr_array.join(@word_separator) end else wordr += (words_short.select { |word| word.length <= length && word.length != length - 1 && word.length != length - 2 && word.length != length - 3 }).sample.to_s end if (tries % 100) == 0 words_short = words.sample(2000) end end good_result = true string = wordr elsif (symbol_type == "@" or symbol_type == "!@") and length > 0 if min_length > 6 and length < 6 length = 6 end if deny_pattern and (expected_errors.include?(:required_data) or expected_errors.include?(:excluded_data) or expected_errors.include?(:string_set_not_allowed)) expected_errors_left.push(:value) expected_errors.push(:value) expected_errors.uniq! expected_errors_left.uniq! end expected_errors_left_orig = expected_errors_left.dup tries = 0 begin expected_errors_left = expected_errors_left_orig.dup tries += 1 string = "" alpha_set = ALPHA_SET_LOWER.clone + ALPHA_SET_CAPITAL.clone string_set = alpha_set + NUMBER_SET.clone + ["."] + ["_"] + ["-"] string_set_not_allowed = all_characters_set - string_set extension = "." at_sign = "@" if expected_errors_left.include?(:value) if rand(2) == 1 extension = (all_characters_set - ["."]).sample.dup expected_errors_left.delete(:value) expected_errors_left.delete(:required_data) end if rand(2) == 1 1.upto(rand(7)) { |i| extension << alpha_set.sample.downcase } (rand(extension.size) + 1).times { extension[rand(extension.size)] = (string_set - alpha_set - ["."]).sample } expected_errors_left.delete(:value) else 1.upto(rand(3) + 2) { |i| extension << alpha_set.sample.downcase } end if rand(2) == 1 at_sign = (string_set - ["@"]).sample.dup expected_errors_left.delete(:value) expected_errors_left.delete(:required_data) end else if length > 6 1.upto(rand(3) + 2) { |i| extension << alpha_set.sample.downcase } else 1.upto(2) { |i| extension << alpha_set.sample.downcase } end end length_e = length - extension.size - 1 length1 = rand(length_e - 1) + 1 length2 = length_e - length1 1.upto(length1) { |i| string << string_set.sample } string << at_sign domain = "" domain_set = alpha_set + NUMBER_SET.clone + ["."] + ["-"] 1.upto(length2) { |i| domain << domain_set.sample.downcase } if expected_errors.include?(:value) and rand(2) == 1 and domain.size > 0 (rand(domain.size) + 1).times { domain[rand(domain.size)] = (all_characters_set - domain_set).sample } expected_errors_left.delete(:value) end string << domain << extension if expected_errors_left.include?(:value) or expected_errors_left.include?(:string_set_not_allowed) (rand(string.size) + 1).times { string[rand(string.size)] = string_set_not_allowed.sample } expected_errors_left.delete(:value) expected_errors_left.delete(:string_set_not_allowed) end error_regular_expression = false if deny_pattern and expected_errors.include?(:length) good_result = true #it is already with wrong length else # I'm doing this because many times the regular expression checking hangs with these characters wrong = %w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@) if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings if string.index("@").to_i > 0 and string[0..(string.index("@") - 1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join == string[0..(string.index("@") - 1)] and string[(string.index("@") + 1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join == string[string[(string.index("@") + 1)..-1]] error_regular_expression = false else error_regular_expression = true end else error_regular_expression = true end if expected_errors.size == 0 if error_regular_expression good_result = false else good_result = true end elsif expected_errors_left.size == 0 and (expected_errors - [:length, :min_length, :max_length]).size == 0 good_result = true elsif expected_errors != [:length] if !error_regular_expression good_result = false elsif expected_errors.include?(:value) good_result = true end end end end until good_result or tries > 100 unless good_result puts "Not possible to generate an email on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}" return "" end end if @dont_repeat if @cache_values[pattern.to_s].nil? @cache_values[pattern.to_s] = Array.new() @cache_values[pattern.to_s].push(string) good_result = true elsif @cache_values[pattern.to_s].include?(string) good_result = false else @cache_values[pattern.to_s].push(string) good_result = true end end if pattern.kind_of?(Symbol) and patt.unique if @cache_values[pattern.__id__].nil? @cache_values[pattern.__id__] = Array.new() @cache_values[pattern.__id__].push(string) good_result = true elsif @cache_values[pattern.__id__].include?(string) good_result = false else @cache_values[pattern.__id__].push(string) good_result = true end end end until good_result or tries > 10000 unless good_result puts "Not possible to generate the string on StringPattern.generate: #{pattern.inspect}, expected_errors: #{expected_errors.inspect}" 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" return "" end return string end
# File lib/string_pattern.rb, line 55 def self.national_chars=(par) @cache = Hash.new() @national_chars = par end
This method is defined to validate if the text_to_validate supplied follows the pattern It works also with array of patterns but in that case will return only true or false
input: text (String) (synonyms: text_to_validate, validate) -- The text to validate pattern -- symbol with this info: "length:symbol_type" or "min_length-max_length:symbol_type" min_length -- minimum length of the string max_length (optional) -- maximum length of the string. If not provided the result will be with the min_length provided symbol_type -- the type of the string we want. expected_errors (Array of symbols) (optional) (synonyms: errors) -- :length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed 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 example: validate(text: "This text will be validated", pattern: :"10-20:Xn", expected_errors: [:value, :max_length]) Output: if expected_errors and not_expected_errors are not supplied: an array with all detected errors if expected_errors or not_expected_errors supplied: true or false if array of patterns supplied, it will return true or false
# File lib/string/pattern/validate.rb, line 21 def StringPattern.validate(text: "", pattern: "", expected_errors: [], not_expected_errors: [], **synonyms) text_to_validate = text text_to_validate = synonyms[:text_to_validate] if synonyms.keys.include?(:text_to_validate) text_to_validate = synonyms[:validate] if synonyms.keys.include?(:validate) expected_errors = synonyms[:errors] if synonyms.keys.include?(:errors) not_expected_errors = synonyms[:not_errors] if synonyms.keys.include?(:not_errors) not_expected_errors = synonyms[:non_expected_errors] if synonyms.keys.include?(:non_expected_errors) #:length, :min_length, :max_length, :value, :required_data, :excluded_data, :string_set_not_allowed if (expected_errors.include?(:min_length) or expected_errors.include?(:max_length)) and !expected_errors.include?(:length) expected_errors.push(:length) end if (not_expected_errors.include?(:min_length) or not_expected_errors.include?(:max_length)) and !not_expected_errors.include?(:length) not_expected_errors.push(:length) end if pattern.kind_of?(Array) and pattern.size == 1 pattern = pattern[0] elsif pattern.kind_of?(Array) and pattern.size > 1 total_min_length = 0 total_max_length = 0 all_errors_collected = Array.new result = true num_patt = 0 patterns = Array.new pattern.each { |pat| if (pat.kind_of?(String) and (!StringPattern.optimistic or (StringPattern.optimistic and pat.to_s.scan(/(\d+)-(\d+):(.+)/).size == 0 and pat.to_s.scan(/^!?(\d+):(.+)/).size == 0))) #fixed text symbol_type = "" min_length = max_length = pat.length elsif pat.kind_of?(Symbol) or (pat.kind_of?(String) and StringPattern.optimistic and (pat.to_s.scan(/(\d+)-(\d+):(.+)/).size > 0 or pat.to_s.scan(/^!?(\d+):(.+)/).size > 0)) #patt = Marshal.load(Marshal.dump(StringPattern.analyze(pat))) #deep copy patt = StringPattern.analyze(pat).clone min_length = patt.min_length.clone max_length = patt.max_length.clone symbol_type = patt.symbol_type.clone else puts "String pattern class not supported (#{pat.class} for #{pat})" return false end patterns.push({ pattern: pat, min_length: min_length, max_length: max_length, symbol_type: symbol_type }) total_min_length += min_length total_max_length += max_length if num_patt == (pattern.size - 1) # i am in the last one if text_to_validate.length < total_min_length all_errors_collected.push(:length) all_errors_collected.push(:min_length) end if text_to_validate.length > total_max_length all_errors_collected.push(:length) all_errors_collected.push(:max_length) end end num_patt += 1 } num_patt = 0 patterns.each { |patt| tmp_result = false (patt[:min_length]..patt[:max_length]).each { |n| res = StringPattern.validate(text: text_to_validate[0..n - 1], pattern: patt[:pattern], not_expected_errors: not_expected_errors) if res.kind_of?(Array) all_errors_collected += res end if res.kind_of?(TrueClass) or (res.kind_of?(Array) and res.size == 0) #valid #we pass in the next one the rest of the pattern array list: pattern: pattern[num_patt+1..pattern.size] 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) if res.kind_of?(Array) if ((all_errors_collected + res) - expected_errors).size > 0 tmp_result = false else all_errors_collected += res tmp_result = true end elsif res.kind_of?(TrueClass) tmp_result = true end return true if tmp_result end } unless tmp_result return false end num_patt += 1 } return result end if (pattern.kind_of?(String) and (!StringPattern.optimistic or (StringPattern.optimistic and pattern.to_s.scan(/(\d+)-(\d+):(.+)/).size == 0 and pattern.to_s.scan(/^!?(\d+):(.+)/).size == 0))) #fixed text symbol_type = "" min_length = max_length = pattern.length else #symbol #patt = Marshal.load(Marshal.dump(StringPattern.analyze(pattern))) #deep copy patt = StringPattern.analyze(pattern).clone min_length = patt.min_length.clone max_length = patt.max_length.clone symbol_type = patt.symbol_type.clone required_data = patt.required_data.clone excluded_data = patt.excluded_data.clone string_set = patt.string_set.clone all_characters_set = patt.all_characters_set.clone required_chars = Array.new required_data.each { |rd| required_chars << rd if rd.size == 1 } if (required_chars.flatten & excluded_data.flatten).size > 0 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}" return "" end end if text_to_validate.nil? return false end detected_errors = Array.new if text_to_validate.length < min_length detected_errors.push(:min_length) detected_errors.push(:length) end if text_to_validate.length > max_length detected_errors.push(:max_length) detected_errors.push(:length) end if symbol_type == "" #fixed text if pattern.to_s != text.to_s #not equal detected_errors.push(:value) detected_errors.push(:required_data) end else # pattern supplied if symbol_type != "@" if required_data.size > 0 required_data.each { |rd| if (text_to_validate.chars & rd).size == 0 detected_errors.push(:value) detected_errors.push(:required_data) break end } end if excluded_data.size > 0 if (excluded_data.flatten & text_to_validate.chars).size > 0 detected_errors.push(:value) detected_errors.push(:excluded_data) end end string_set_not_allowed = all_characters_set - string_set text_to_validate.chars.each { |st| if string_set_not_allowed.include?(st) detected_errors.push(:value) detected_errors.push(:string_set_not_allowed) break end } else #symbol_type=="@" string = text_to_validate wrong = %w(.. __ -- ._ _. .- -. _- -_ @. @_ @- .@ _@ -@ @@) if !(Regexp.union(*wrong) === string) #don't include any or the wrong strings if string.index("@").to_i > 0 and string[0..(string.index("@") - 1)].scan(/([a-z0-9]+([\+\._\-][a-z0-9]|)*)/i).join == string[0..(string.index("@") - 1)] and string[(string.index("@") + 1)..-1].scan(/([0-9a-z]+([\.-][a-z0-9]|)*)/i).join == string[string[(string.index("@") + 1)..-1]] error_regular_expression = false else error_regular_expression = true end else error_regular_expression = true end if error_regular_expression detected_errors.push(:value) end end end if expected_errors.size == 0 and not_expected_errors.size == 0 return detected_errors.uniq else if expected_errors & detected_errors == expected_errors if (not_expected_errors & detected_errors).size > 0 return false else return true end else return false end end end