Ruby on Rails 4.0 length validation - ruby-on-rails

I would like to validate my code's length.
It suppose to take a number from a range in between 13 and 16 but it should exclude 14.
I cannot find any clever way to do it in Model.
class Card < ActiveRecord::Base
validates :number, :length => { in: 13..16 }
end

Here it is:
validates :number, :length => { in: [13,15,16] }
However, it smells like you probably should be using a regular expression.

Related

Rails validate uniqueness only if conditional

I have a Question class:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
A given user can only create a single question per day, so I want to force uniqueness in the database via a unique index and the Question class via validates_uniqueness_of.
The trouble I'm running into is that I only want that constraint for non-admin users. So admins can create as many questions per day as they want. Any ideas for how to achieve that elegantly?
You can make a validation conditional by passing either a simple string of Ruby to be executed, a Proc, or a method name as a symbol as a value to either :if or :unless in the options for your validation. Here are some examples:
Prior to Rails version 5.2 you could pass a string:
# using a string:
validates :name, uniqueness: true, if: 'name.present?'
From 5.2 onwards, strings are no longer supported, leaving you the following options:
# using a Proc:
validates :email, presence: true, if: Proc.new { |user| user.approved? }
# using a Lambda (a type of proc ... and a good replacement for deprecated strings):
validates :email, presence: true, if: -> { name.present? }
# using a symbol to call a method:
validates :address, presence: true, if: :some_complex_condition
def some_complex_condition
true # do your checking and return true or false
end
In your case, you could do something like this:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id, unless: Proc.new { |question| question.user.is_admin? }
end
Have a look at the conditional validation section on the rails guides for more details: http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
The only way I know of to guarantee uniqueness is through the database (e.g. a unique index). All Rails-only based approaches involve race conditions. Given your constraints, I would think the easiest thing would be to establish a separate, uniquely indexed column containing a combination of the day and user id which you'd leave null for admins.
As for validates_uniqueness_of, you can restrict validation to non-admins through use of an if or unless option, as discussed in http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Just add a condition to the validates_uniqueness_of call.
validates_uniqueness_of :created_on, scope: :user_id, unless: :has_posted?
def has_posted
exists.where(user_id: user_id).where("created_at >= ?", Time.zone.now.beginning_of_day)
end
But even better, just create a custom validation:
validate :has_not_posted
def has_not_posted
posted = exists.where(user: user).where("DATE(created_at) = DATE(?)", Time.now)
errors.add(:base, "Error message") if posted
end

validates format with regexp in ruby on rails

My Requirement :
To Add validation to make sure that SITEMAP_URL keyword is always included in search_engines.url and it should be of format http://www.example.com/
class SearchEngine < ActiveRecord::Base
validates :name, :ping_url, :uniqueness => true, :presence => true
validates :ping_url, :format => {:with => /\=SITEMAP_URL$/}, :presence => true
end
Is the validation i have written is correct .
The validation looks fine.
Except if you expect /\=SITEMAP_URL$/ to interpolate it won't.
It will only match strings like;
"http://some.com?q=SITEMAP_URL"
Is that really what you mean?
If you have
SITEMAP_URL = "/sitemap.xml"
Then you can say
SITEMAP_REGEXP = /\=#{SITEMAP_URL}/
But this will interpret the "." as any character.
So you need to use Regexp.escape
Pretty much
Regexp.new(Regexp.escape("=#{SITEMAP_URL}")+"$")
Should do what you want.

What is the "equals greater than" operator => in Ruby?

In a Ruby on Rails tutorial, I am asked to type:
class Post < ActiveRecord::Base
validates :name, :presence => true
validates :title, :presence => true, :length => { :minimum => 5 }
end
I understand what this does, but I would like to know what the => operator is. In PHP-land, it links a key and a value in an associative array. Is it the same thing here? Is it a Ruby operator or a Rails operator?
It is mainly a ruby operator that sets the value of a key inside a hash. Thus :
{ :minimum => 5 }
Is a ruby hash that has the symbol :minimum as a key that maps to the value of 5. A hash with one entry, in this example. Same for :
:presence => true
Still a hash. However, in ruby, when you have a method, you can omit the {} that surround a hash. That is what happens with the validates method. It's a method and thus the passed hash does not explicitly need {}.

Rails - force field uppercase and validate uniquely

Airports have four-letter ICAO codes. By convention, these are always uppercase. I'm creating a form for receiving user input, but this form needs to be able to accept user input in mixed case, and prevent them from creating dupes.
The default :uniqueness is case-sensitive, of course. I figured out how to transform the user's input to uppercase before it gets saved, but the problem is that this appears to be post-validation, instead of pre-validation.
For example, if there is already an Airport with ICAO of KLAX, a user can enter klax, it will get validated as unique, and then transformed to uppercase and stored, resulting in duplicates.
Here's my model code at present.
class Airport < ActiveRecord::Base
validates :icao, :name, :lat, :lon, :presence => true
validates :icao, :uniqueness => true
before_save :uppercase_icao
def uppercase_icao
icao.upcase!
end
end
Or a slightly different take: Write a setter for icao that converts anything thrown at it to uppercase:
def icao=(val)
self[:icao] = val.upcase
end
And then you can use regular uniqueness validation (back it up with a unique index in your DB). Might even make things a little easier for the DB during finds, 'cause it doesn't have to worry about case-insensitive comparisons any more.
Hope this helps!
try this:
validates :icao, :uniqueness => { :case_sensitive => false }
Updated answer for Rails 4.
class Airport < ActiveRecord::Base
validates :icao, :name, :lat, :lon, :presence => true
validates :icao, :uniqueness => { case_sensitive: false }
def icao=(val)
write_attribute :icao, val.upcase
end
end
Simply fixed (as many problems with Rails are) - as Danny pointed out above, although not in his own answer so I can't accept it :), changing before_save to before_validation fixes it perfectly.

Rails: Why "format" (regex) validation fails?

I have the following validation of product's price:
class Product < ActiveRecord::Base
...
PRICE_REGEX = /^([1-9]\d{0,5}|0)(\.\d{1,2})?$/
validates :price, :presence => true, :format => PRICE_REGEX
...
end
It supposed to allow prices from 0 to 999999.99.
However, if I enter hello, the validation passes, and 0.00 is saved in the database.
The :presence validation works fine, though.
What am I missing here ?
The price column is a float, and so Rails will automatically convert the string "hello" to float, as per "hello".to_f # => 0.0. This is then converted back to the string "0.0", which obviously matches the regular expression.
In general, it's a bad idea to use regular expressions on non-string columns. Instead, use validates_numericality_of. If you wanted the same restriction as with the regex, do it like this:
class Product < ActiveRecord::Base
validates_numericality_of :price, :greater_than => 0, :less_than => 1000000
end
Not only is it safer, but it's easier to read and follow as well. Note that it'll automatically reject the price if blank as well.
I haven't tested this, but every description I saw from validates uses :format in this way:
validates :price, :presence => true, :format => { :with => PRICE_REGEX }
Maybe the problem is there

Resources