How do I validate user input into a template?
In my Ruby on Rails application, I need to validate user input for patterns consisting of letters, numbers, and a few special characters (such as dashes and underscores), and I don't know how.
For example, if the template is relevant LL-NNNN
and the user submits a string AB-0001
, this will be valid.
If the template NN-LL
and the user submits 77-ABC
, it will be invalid.
I thought about converting each pattern to a regex, but since each user can define their own pattern (which means there will be many), I am concerned that this might lead to unexpected results.
Can anyone tell me what is the best way to solve this?
source to share
In the loop, check to see if each character from the input string matches the corresponding character in the pattern.
def match?(pattern, input)
return false if pattern.length != input.length
pattern.each_char.with_index.all? do |char, i|
case char
when 'L' then input[i] =~ /[A-Z]/
when 'N' then input[i] =~ /[0-9]/
when '-' then input[i] == '-'
else raise 'invalid pattern'
end
end
end
match?('LL-NNNN', 'AB-1234') #=> true
match?('NN-LL', '77-ABC') #=> false
match?('NN-LL', '77-99') #=> false
match?('NN-LL', '77-AB') #=> true
source to share
If the pattern will always be a combination L => Letter
, N => number
and -
, you can convert it to a regular expression withRegex.new(value)
It will all look like this for any occasion.
def match(pattern, value)
/\A#{pattern.gsub(/[LN]/, 'L' => '[a-zA-Z]', 'N' => '\d')}\z/.match(value)
end
and this is for uppercase only
def match(pattern, value)
/\A#{pattern.gsub(/[LN]/, 'L' => '[A-Z]', 'N' => '\d')}\z/.match(value)
end
You can even do the conversion to regex earlier and store the regex in the DB instead of a pattern to optimize processing time.
def regex_from_pattern(pattern)
"\\A#{pattern.gsub(/[LN]/, 'L' => '[a-zA-Z]', 'N' => '\d')}\\z"
end
def match(regex_string, value)
Regexp.new(regex_string).match(value)
end
source to share
Because we love Ruby's one-liner so much, here's a quick hack.
Replace the input characters with the appropriate wildcard character:
'AB-1234'.gsub(/[A-Z]/, 'N') #=> "NN-1234"
.gsub(/[0-9]/, 'L') #=> "NN-LLLL"
And compare the result with the pattern:
'AB-1234'.gsub(/[A-Z]/, 'N').gsub(/[0-9]/, 'L') == 'NN-LLLL'
#=> true
source to share
You can create a regexp builder and Regexp.escape
will help you. Let's say you've installed a custom template alphabet:
ALPHABET = 'LN-_:=.\\'
Then you can check if the user template is correct:
PATTERN_REGEXP = Regexp.new('\A[%s]+\z' % Regexp.escape(ALPHABET))
def is_pattern_ok?(pattern)
PATTERN_REGEXP =~ pattern
end
The next step is to create a custom regex input string from its template:
def regexp_str_from(pattern)
('\A%s\z' % Regexp.escape(pattern))
.gsub('N', '\d')
.gsub('L', '[a-zA-Z]'))
end
And save it somewhere in user preferences in the DB. This way you can validate user input with a custom template:
Regexp.new(regexp_str) =~ user_input
Edited: Note: Regexp.escape
Required to avoid "unexpected results".
source to share