What is wrong with this Regex in Ruby on Rails? - ruby-on-rails

I've written a regex to help validate a String for game character names. It's somehow passing seemingly invalid strings and not passing seemingly valid strings.
Requirements:
Starts with a capital letter
Has any number of alphanumeric characters after that (this includes spaces)
This is the rails code that does the validation in the Character Model:
validates :name, format: { with: %r{[A-Z][a-zA-Z0-9\s]*} }
Here's the unit test I'm using
test "character name should be properly formatted and does not contain any special characters" do
character = get_valid_character
assert character.valid?
character.name = "aBcd"
assert character.invalid?, "#{character.name} should be invalid"
character.name = "Number 1"
assert character.valid?, "#{character.name} should be valid"
character.name = "McDonalds"
assert character.valid?, "#{character.name} should be valid"
character.name = "Abcd."
assert character.invalid?, "#{character.name} should be invalid"
character.name = "Abcd%"
assert character.invalid?, "#{character.name} should be invalid"
end
The problems:
The regex passes "aBcd", "Abcd.", and "Abcd%" when it shouldn't. Now, I know this works because I tested this out in Python and it works just as you would expect.
What gives?
Thank you for your help!

Regular expressions look for matches anywhere in the given string unless told otherwise.
So the test string 'aBcd' is invalid, but it contains a valid substring: 'Bcd'. Same with 'Abcd%', where the valid substring is 'Abcd'.
If you want to match the entire string, use this as your regex:
# \A matches string beginning, \z matches string end
%r{\A[A-Z][a-zA-Z0-9\s]*\z}
PS: Some people will say to match the beginning of a string with ^ and the end with $. In Ruby, those symbols match the beginning and end of a line, not a string. So "ABCD\n%" would still match if you used ^ and $, but won't match if you use \A and \z. See the Rails security guide for more on this.

If you only want to match the capital letter at the beginning of the string, you need to put in the "start of line" marker ^ so it would look like:
validates :name, format: { with: %r{^[A-Z][a-zA-Z0-9\s]*} }
Check out Rubular to play around with your regex

Related

How to check if string include symbols?

How can I check if a string include symbol? It looks confusing to me:
class Beba
def initialize
while true
puts "Qfar emri deshironi ti vnoni bebes?"
##emri = gets.chomp.capitalize
if ##emri.scan(/\d+/).empty? && ##emri.scan(/\/./).empty?
puts "Ti e emertove beben me emrin: #{##emri}"
break
else
puts "Emri nuk mund te jete me numra/simbole, provoni perseri."
end
end
end
end
As you can see, at if##emri.scan(/\d+/).empty? && ##emri.scan(/\/./).empty?, I don't know what to do, like which method can I use for ##emri.scan(/\\.\).empty? to check if my string doesn't include any symbol?
For the specific characters you asked for, you can use this:
##emri.scan(/[!##$%^&*()_+{}\[\]:;'"\/\\?><.,]/).empty?
Will return true if no special character is found.
str !~ /[!##$%^&*()_+{}\[\]:;'"\/\\?><.,]/
returns true if and only if the string str contains none of the characters in the regex's character class (else false is returned).
Its seems you are looking for special characters.
Use something like
"Hel#lo".index( /[^[:alnum:]]/ )
It will return nil if no special charatcters.
[:alnum:] includes all 0-9, a-z, A-Z.
IF YOU WANT TO GO FOR SPECIFIC CHARATCERS
place all characters in a string & create regex like
characters = "!##$%^&*()_+{}[]:;'\"\/?><.,"
regex = /[#{characters.gsub(/./){|char| "\\#{char}"}}]/
& than use this regex to see if any of them exist in string like
if some_string =~ regex

How to test a model validation with a regex in RSpec?

After a bit of hacking I have come up with the following tests to make sure the regex pattern on my model validator is working correctly. I am wondering if there is a better way to test these conditions instead of building a bad string. I want to account for any and all characters outside the approved regex pattern. Different columns may have different validators too.
Model
validates :provider_unique_id,
presence: true,
length: { maximum: 50 },
format: { with: /\A[A-Za-z0-9]+\z/ }
Spec
describe 'provider unique id' do
let(:bad_string) { (0..255).map(&:chr).select { |x| x != /\A[A-Za-z0-9]+\z/ }.sample(20).join }
it 'should exist' do
shop.provider_unique_id = nil
expect(shop.valid?).to be_falsey
end
it 'passes regex rules' do
shop.provider_unique_id = bad_string
expect(shop.valid?).to be_falsey
end
end
Here's what I'd write if I were being extremely thorough. Imagine test-driving the validations, adding one test at a time and adding to the validations to make it pass.
describe '#provider_unique_id' do
%w(a z).each do |letter|
it "can be letter #{letter}" do
expect_to_be_valid letter
end
end
# after making this pass, I'd change the regex to use the i flag so I wouldn't need to test for Z
it "can be uppercase" do
expect_to_be_valid 'A'
end
[0, 9].each do |digit|
it "can be digit #{digit}" do
expect_to_be_valid digit
end
end
it "can be more than one character" do
expect_to_be_valid '00'
end
it "isn't nil" do
expect_to_be_invalid nil
end
it "isn't blank" do
expect_to_be_invalid ""
end
it "can be 50 characters long" do
expect_to_be_valid('0' * 50)
end
it "can't be longer than 50 characters" do
expect_to_be_invalid('0' * 51)
end
# I chose _ as a non-alphanumeric since it's the only non-alphanumeric word character.
# That is, it's as close to a valid character as it can be without be valid.
it "can't contain a non-alphanumeric character" do
expect_to_be_invalid '_'
end
# this example forces you to add \A
it "can't begin with a non-alphanumeric character" do
expect_to_be_invalid '_0'
end
# this example forces you to add \z
it "can't end with a non-alphanumeric character" do
expect_to_be_invalid '0_'
end
def expect_to_be_valid(provider_unique_id)
shop.provider_unique_id = provider_unique_id
expect(shop).to be_valid
end
def expect_to_be_invalid(provider_unique_id)
shop.provider_unique_id = provider_unique_id
expect(shop).to_not be_valid
end
end
I wouldn't randomly generate a bad string, because it wouldn't force you to write any additional code. I think the tests with _ are sufficient. Note that there are many more characters than ASCII 0-255, and it would be impractical to test them all.
You could imagine boundary-checking the ranges in the regex (a-z, A-Z, 0-9) by testing characters that come immediately before and after each range, but it's unlikely that someone would write code that would incorrectly include those characters, so I wouldn't go that far.

rspec model test on string match regex adjustment

I have the following test in rspec
it 'passes regex rules' do
job = create(:job)
job.valid?
expect(job.title).to match(/\A[\w\d .,:-#]+\z/)
end
This regex pattern matches the model pattern. What is the recommended way to test to make sure this pattern does not change in the model from future developers?
Basically I want to test for conditions that do not fall in the approved: can only have 0-9, A-Z, periods, colons, hypens, underscores, and spaces. No new lines (enter keys)
Update
Based on Generate random string based on Regex? I decided to go with (0..255).map(&:chr).select{|x| x != /\A[\w\d .,:-#]+\z/}.sample(5).join for now which appears to work, thoughts?
Based on the update, I went with the following:
describe 'title' do
let(:bad_string) { (0..255).map(&:chr).select{|x| x != /\A[\w\d .,:-#]+\z/}.sample(20).join }
it 'should exist' do
job = build(:job, title: nil)
job.valid?
expect(job.errors[:title].size).to eq(3)
end
it 'passes regex rules' do
job = build(:job, title: bad_string)
job.valid?
expect(job.errors[:title].size).to eq(1)
end
end

Regex to check if string is just made up of special characters

I have some strings for which i have to check whether it is made up of all special characters in regex i tried something but its not working the way i want any help ?
str = "##%^"
regex = /[\?\<\>\'\,\?\[\]\}\{\=\-\)\(\*\&\^\%\$\#\`\~\{\}\#]/
str.match(regex)
You can do it with
/\A\W*\z/
The \W* matches any non-word character from the beginning (\A) till the end (\z) of string.
See demo:
class String
def onlySpecialChars?
!!self.match(/\A\W*\z/)
end
end
puts "##%^".onlySpecialChars? # true
puts "w##%^".onlySpecialChars? # false
If you have your own set of special characters, just use instead of \W. Just also note you have overescaped your regex, [?<>',?\[\]}{=)(*&^%$#`~{}#-] will suffice.

Rails regex validation failing in rspec

I am trying to write and test a regex valiation which allows only for a sequence of paired integers, in the format
n,n n,n
where n is any integer not beginning with zero and pairs are space separated. There may be a single pair or the field may also be empty.
So with this data, it should give 2 errors
12,2 11,2 aa 111,11,11
error 1: the 'aa'
error 2: the triplet (111,11,11)
In my Rails model I have this
validates_format_of :sequence_excluded_region, :sequence_included_region,
with: /[0-9]*,[0-9] /, allow_blank: true
In my Rspec model test I have this
it 'is invalid with alphanumeric SEQUENCE_INCLUDED_REGION' do
expect(DesignSetting.create!(sequence_included_region: '12,2 11,2 aa 111,11,11')).to have(1).errors_on(:sequence_included_region)
end
The test fails, as the regex does not find the errors, or perhaps I am calling the test incorrectly.
Failures:
1) DesignSetting is invalid with alphanumeric SEQUENCE_INCLUDED_REGION
Failure/Error: expect(DesignSetting.create!(sequence_included_region: '12,2 11,2 aa 111,11,11')).to have(2).errors_on(:sequence_included_region)
expected 2 errors on :sequence_included_region, got 0
# ./spec/models/design_setting_spec.rb:5:in `block (2 levels) in <top (required)>'
Regex
Your regex matches a single pair followed by a space anywhere in the string.
'12,2 11,2 aa 111,11,11 13,3'.scan /[0-9]*,[0-9] /
=> ["12,2 ", "11,2 "]
So any string with one valid pair followed by a space will be valid. Also a single pair would fail 3,4 as there is no space.
A regex that would validate the entire string:
positive_int = /[1-9][0-9]*/
pair = /#{positive_int},#{positive_int}/
re_validate = /
\A # Start of string
#{pair} # Must have one number pair.
(?:\s#{pair})* # Can be followed by any number of pairs with a space delimiter
\z # End of string (no newline)
/x
Validators
I don't use rails much but it seems like you are expecting too much from a simple regex validator for it to parse out the individual error components from a string for you.
If you split the variable up by space and then validated each element of the array you could get that detail for each field.
'12,2 11,2 aa 111,11,11 13,3'.split(' ').reject{|f| f =~ /^[1-9][0-9]*,[1-9][0-9]*$/ }
You can put something like that into a custom validator class using validates_with which you can then have direct control of your errors with...
class RegionValidator < ActiveModel::Validator
def validate(record)
record.sequence_included_region.split(' ').reject{|f| f =~ /^[1-9][0-9]*,[1-9][0-9]*$/ }.each do |err|
record.errors[sequence_included_region] << "bad region field [#{err}]"
end
end
end
(?<=\s|^)\d+,\d+(?=\s|$)
Try this.Replace with empty string.The left string split by are your errors.
See demo.
http://regex101.com/r/rQ6mK9/22

Resources