I'm trying to make a form that accepts only a valid email, or a blank email. I think it will be something along these lines:
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i OR ""
validates :email, format: { with: EMAIL_REGEX }
or maybe
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
BLANK_REGEX =
validates :email, format: { with: EMAIL_REGEX OR BLANK_REGEX }
but I can't figure out the proper syntax. Does anyone know the right way to do this?
The approach pointed by #avinash-raj is perfect. However you can use allow_blank: true in your validates. Your code should be like this:
validates :email, format: { with: /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i}, allow_blank: true
Just make your regex optional to make your regex to match blank email also.
EMAIL_REGEX = /\A(?:[\w+\-.]+#[a-z\d\-.]+\.[a-z]+)?\z/i
OR
EMAIL_REGEX = /^(?:[\w+\-.]+#[a-z\d\-.]+\.[a-z]+)?$/i
TO make a regex optional, enclose the whole regex inside a non-capturing group (?:...) and then add a ? next to that group.
Related
I have a few fields with a length validation, which can be bypassed by entering n number of white spaces. I'm trying to write a method that validates the number of alpha numeric characters only (not whitespace or special characters).
I've gotten as far as the following:
validates :title,
presence: true,
length: { minimum: 4, maximum: 140 },
format: { with: /([A-z0-9])/ }
What I can't get is how to validate the length of the title that matches the format. For example I want to allow a title of 'The Beast', but only count 'TheBeast' in the character count. This will allow 'The Beast' and include the space in the length validation
Is there something built into rails that allows me to do this? Or if not what's the best way go about writing a custom method?
Thanks in advance
if you would have aux column like 'filtered_title' you could do:
before_save :filter_title
def filter_title
self.filtered_title = title.gsub(/[^0-9a-zA-Z]/, '') // strip unneeded chars
end
and your validator, but on a the new column
validates :filtered_title,
presence: true,
length: { minimum: 4, maximum: 140 },
format: { with: /([A-z0-9])/ }
To expand on #NeverBe's answer, I went with:
class AlphanumericLengthValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
minimum_length = options.fetch(:length, 100)
stripped_value = value ? value.gsub(/[^0-9a-zA-Z]/, '') : nil
message = "must be at least #{minimum_length} alphanumeric characters in length"
return if stripped_value&.length && stripped_value.length >= minimum_length
record.errors.add(attribute, message) if !stripped_value || stripped_value.length < minimum_length
end
end
Which allowed me to do:
validates :title, alphanumeric_length: { length: 8 }
I'm wanting to validate that my height attribute is within a bunch of different ranges. So my attempt was something like what I did below... however this is incorrect. How should this be done? Thanks!
validates :height, :numericality => { in: { 5020..5028, 5030..5038, 5040..5048, 5050..5058, 5060..5068, 5070..5078, 5080..5088, 5090..5098, 5100..5108, 5110..5118,
6000..6008, 6010..6018, 6020..6028, 6030..6038, 6040..6048, 6050..6058, 6060..6068, 6070..6078, 6080..6088, 6090..6098, 6100..6108, 6110..6118,
7000..7008, 7010..7018, 7020..7028, 7030..7038, 7040..7048, 7050..7058, 7060..7068, 7070..7078, 7080..7088, 7090..7098, 7100..7108, 7110..7118 } }
You can put that in a custom validate method:
class YourModel < ActiveRecord::Base
VALID_HEIGHT_RANGES = [5020..5028, 5030..5038, 5040..5048, 5050..5058, 5060..5068, 5070..5078, 5080..5088, 5090..5098, 5100..5108, 5110..5118, 6000..6008, 6010..6018, 6020..6028, 6030..6038, 6040..6048, 6050..6058, 6060..6068, 6070..6078, 6080..6088, 6090..6098, 6100..6108, 6110..6118, 7000..7008, 7010..7018, 7020..7028, 7030..7038, 7040..7048, 7050..7058, 7060..7068, 7070..7078, 7080..7088, 7090..7098, 7100..7108, 7110..7118]
validate :height_in_valid_range
private
def height_in_valid_range
VALID_HEIGHT_RANGES.each do |range|
unless range.include? height
errors.add :height, "not in valid range"
break
end
end
end
end
I'm trying to validate redemption codes
#redemption_codes = Account.where(:redeemed == false).map(&:redemption_code)
validates :code, inclusion: { in: #redemption_codes }
before_create :remove_code
def remove_code
#redeemed = Account.where(:redeemed == true).map(&:redemption_code)
#redemption_codes.delete_if{|code|#redeemed.include?(code)}
end
If a code has already been redeemed, I want to remove it from the array, so it's no longer included.
Here I get
Undefined method delete_if for nil:NilClass
The value of #redemption_codes is nil
If I type in a value that is not included in the array, I get an error flash message.
When I type in a value included in the array, it works. In other words, it works if I comment out the remove_code method.
Problem is #redemption_codes is set in stone. If a value is updated from :redeemed = false to :redeemed = true, it won't disappear from the array.
That's why I tried to create a custom validation message, but it didn't work.
Help is appreciated.
That is the incorrect syntax.
Account.where(:redeemed == false)
should be
Account.where(redeemed: false)
You should try something like this
def validate_code
#redemption_codes = Account.where(redeemed: false).map(&:redemption_code)
errors[:base] << "Invalid Redemption Code" unless #redemption_codes.include?(self.code)
end
You could also use the uniqueness validator
validates :code, inclusion: { in: #redemption_codes }, uniqueness: true
This ensures that a redeemed account will not be valid
I'm trying to write a regular expression in Ruby (Rails) so that a username's characters only contains numbers and letters (also no spaces).
I have this regex, /^[a-zA-Z0-9]+$/, but it doesn't seem to be working and I get an error in Rails that says "The provided regular expression is using multiline anchors (^ or $), which may present a security risk. Did you mean to use \A and \z, or forgot to add the :multiline => true option?"
My full code for this implementation in my user.rb model is:
class User < ActiveRecord::Base
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_USERNAME_REGEX = /^[a-zA-Z0-9]+$/
validates :username, presence: true, length: { maximum: 20 },
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
end
What am I doing wrong and how I can fix this regex so that it only is valid for numbers and letters and no spaces? Thanks.
Short answer: use /\A[a-zA-Z0-9]+\z/ instead (as VALID_EMAIL_REGEX is using).
Long answer: the ^ and $ anchors will match the beginning and end of a line in a string. That means that if your string consists of multiple lines of alphanumeric characters they will match:
/^[a-zA-Z0-9]+$/ =~ "Ana\nBob\nClara\nDaniel" #=> 0 (matches)
The \A and \z on the other hand will match the beginning and end of a string, hence it will prevent a possible attack by a user sending a multiline string (like in the previous example).
/\A[a-zA-Z0-9]+\z/ =~ "Ana\nBob\nClara\nDaniel" #=> nil (doesn't match)
/\A[a-zA-Z0-9]+\z/ =~ "Erika" #=> 0 (matches)
All you have to do is follow the error message. Replace ^ (start of line anchor) with \A (start of string anchor), and $ (end of line anchor) with \z (end of string anchor). Other than that, your regex works as is.
\A[a-zA-Z0-9]+\z
Rails has this security concern because unlike some languages, ^ and $ only match the beginning/end of a single line, rather than the entire string.
This illustrates an example of this possible exploit:
str = "malicious_code()\naValidUsername"
str.match(/^[a-zA-Z0-9]+$/) # => #<MatchData "aValidUsername">
I am working on ruby on rails here is the code for regex
string_regex = /[a-z]+\z/i
validates :name , :format => { :with => string_regex , :message => "should not contain special character" }
Attempts:
dbcda-> true
abjdkbcak-> true
jgh1213 -> false
1314134##$ -> false
jbh31$ -> false
,,,,,,, -> false
kjgh,g,, -> TRUE(Which should be false).
You need to use /\A[a-z]+\z/i since you don't want any special characters from the beginning of your string (^) to the end of it (\z)
For help, try http://rubular.com/
[Edit] Changed as mentioned by #2called-chaos