update_attributes(:a => [" "]) should not handle by validation - ruby-on-rails

I have the code as below, as I understand, it only handles when a.blank? is true. But [" "].blank? is false. Why it cannot pass validation
class Demo < CouchRest::Model::Base
collection_of :subdemos
validate :ensure_subdemos
def ensure_subdemos
errors.add(:demo, "must include subdemos.") if subdemos.blank?
end
end
if I do demo.update_attributes(:subdemo_ids => [" "]), why it cannot pass validation?? Can anyone go through the process for me??

You need to use actual IDs. White space is blank:
-> % rails c
Loading development environment (Rails 4.2.3)
Frame number: 0/5
[1] pry(main)> " ".blank?
true
[2] pry(main)>
If you add data there it should pass validation:
demo.update_attributes(:subdemo_ids => [1,2,3])
If you want to allow white space or an empty array, try:
def ensure_subdemos
errors.add(:demo, "must include subdemos.") if subdemos.nil?
end

Related

How to limit the number of lines / text rows with Rails validation?

Is there a way to limit the number of lines / text rows in a type text attribute in Rails?
I know that I can limit the number of characters like so:
validates :message, :length => { :maximum => 100 }
But how about the number of lines?
Thanks for any help.
You could create a custom validator to implement the required logic. Something along the following idea should do the trick:
class LinesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
lines = value.split("\n")
if lines.size > options[:maximum]
record.errors[attribute] << "too many lines"
end
if lines.any? { |l| l.size > options[:length]}
record.errors[attribute] << "line longer than allowed length"
end
end
end
And the usage could be as follows:
validates :message, lines: { maximum: 5, length: 10}
For more information regarding validations and custom validators, you can read the rails docs
Writing a custom validation to do this is pretty easy. You use validate (not validates) to do this:
validate :not_too_many_lines
private
def not_too_many_lines
if self.message.split("\n").length > 10
self.errors.add :message, "has too many lines"
end
end
Under the hood this runs before valid?, which checks to see whether errors has anything in it. If you then ran <record>.errors.full_messages after trying to save an invalid record you'd see "Message has too many lines". If you just want it to say "too many lines" you can use self.errors.add :base, "too many lines"
I can't comment yet.
So to add top the answer, split('\n') is not always safe.
Better just use String#lines
Here is the example with the lines method instead.
validate :not_too_many_lines
private
def not_too_many_lines
if self.message.lines.count > 10
errors.add :message, "has too many lines"
end
end

Custom numericality validation in rails

I needed to add a custom validation option for numericality that accepts , and . as separators and provides localized error messages, while the field itself is a string.
I create a custom validation inside of my model, which means that I have access to the field names (average and grade_scale in this case)
validate :average_valid_number
def average_valid_number
avg = average.gsub("," , ".")
case avg
when /\A0[xX]/
errors.add(:average, :not_a_number)
else
begin
Kernel.Float(avg)
rescue
errors.add(:average, :not_a_number)
end
if grade_scale == "CH"
errors.add(:average, :greater_than_or_equal_to, options = {:count => 4}) if avg.to_f < 4
errors.add(:average, :less_than_or_equal_to, options = {:count => 6}) if avg.to_f > 6
end
end
end

Rails 3.2 Prevent Object from being Saved using Errors

I have an ActiveRecord object and I would like to prevent it from being saved, without having permanent validations on the model. You used to be able to do something like this using errors.add but it doesn't look like it works anymore.
user = User.last
user.errors.add :name, "name doesn't rhyme with orange"
user.valid? # => true
user.save # => true
or
user = User.last
user.errors.add :base, "my unique error"
user.valid? # => true
user.save # => true
How can I prevent the user object from getting saved in Rails 3.2 without modifying it's model?
You can set errors, but do it within a validate method, e.g.:
validate :must_rhyme_with_orange
def must_rhyme_with_orange
unless rhymes_with_orange?
errors.add(:name, "doesn't rhyme with orange")
end
end
If you want to conditionally run the validation, one trick is to use attr_accessor and a guard condition:
attr_accessor :needs_rhyming
validate :must_rhyme_with_orange, :if => Proc.new {|o| o.needs_rhyming}
> u = User.last
> u.needs_rhyming = true
> u.valid? # false
Your issue is running valid? will rerun the validations.. resetting your errors.
pry(main)> u.errors[:base] << "This is some custom error message"
=> ["This is some custom error message"]
pry(main)> u.errors
=> {:base=>["This is some custom error message"]}
pry(main)> u.valid?
=> true
pry(main)> u.errors
=> {}
pry(main)>
Instead, just check if u.errors.blank?
This is a slight deviation from the original question, but I found this post after trying a few things. Rails has built in functionality to reject an object from saving if it has the _destroy attribute assigned as true. Quite helpful if you're handling model creation on the controller level.

Rails: How to set different ":message" for each one of the possible errors?

I use this validation:
validates_numericality_of :price, :greater_than_or_equal_to => 0, :less_than => 1000000
How could I set a different :message for each one of the following cases ?
price < 0
price >= 1000000
Assuming you're using Rails 3, another option you have is to create a custom validator:
# You can put this in lib/better_numericality_validator.rb
class BetterNumericalityValidator < ActiveModel::EachValidator
def validate_each(record,attribute,value)
if value < 0
record.errors[attribute] << "must be greater than or equal to 0"
elsif value >= 1000000
record.errors[attribute] << "must be less than 1000000")
end
end
end
Then you can use your custom validator in your model:
# In your model.rb
validates :price, :better_numericality => true
This method is very similar to Anubhaw's answer. But pulling the logic out into the a custom validator makes it so that you can reuse the validation elsewhere easily, you can easily unit test the validator in isolation, and I personally think that validates :price, :better_numericality => true leaves your model looking cleaner than the alternative.
You can use following in model.rb:-
def validate
if self.price < 0
errors.add(:price, "custom message")
elsif self.price > 1000000
errors.add(:price, "custom message")
end
end
Thanks....
How about:
validates_numericality_of :price, :greater_than_or_equal_to => 0, :message => "Foo"
validates_numericality_of :price, :less_than => 1000000, :message => "Bar"
I've not tested it, but it should work?
Alternatively, Anubhaw's question is a good fallback.
At some point, you should probably ask yourself whether it isn't time to apply some convention over configuration.
In my opinion, an error message such as "Please enter a valid price greater than 0 and less than 1 million" (or similar) is a perfectly valid solution to the problem. It prevents you from adding unnecessary complexity to your application and allows you to move on to other (presumably more important) features.

How do you validate the presence of one field from many

I'm answering my own questions - just putting this up here for google-fu in case it helps someone else. This code allows you to validate the presence of one field in a list. See comments in code for usage. Just paste this into lib/custom_validations.rb and add require 'custom_validations' to your environment.rb
#good post on how to do stuff like this http://www.marklunds.com/articles/one/312
module ActiveRecord
module Validations
module ClassMethods
# Use to check for this, that or those was entered... example:
# :validates_presence_of_at_least_one_field :last_name, :company_name - would require either last_name or company_name to be filled in
# also works with arrays
# :validates_presence_of_at_least_one_field :email, [:name, :address, :city, :state] - would require email or a mailing type address
def validates_presence_of_at_least_one_field(*attr_names)
msg = attr_names.collect {|a| a.is_a?(Array) ? " ( #{a.join(", ")} ) " : a.to_s}.join(", ") +
"can't all be blank. At least one field (set) must be filled in."
configuration = {
:on => :save,
:message => msg }
configuration.update(attr_names.extract_options!)
send(validation_method(configuration[:on]), configuration) do |record|
found = false
attr_names.each do |a|
a = [a] unless a.is_a?(Array)
found = true
a.each do |attr|
value = record.respond_to?(attr.to_s) ? record.send(attr.to_s) : record[attr.to_s]
found = !value.blank?
end
break if found
end
record.errors.add_to_base(configuration[:message]) unless found
end
end
end
end
end
This works for me in Rails 3, although I'm only validating whether one or the other field is present:
validates :last_name, :presence => {unless => Proc.new { |a| a.company_name.present? }, :message => "You must enter a last name, company name, or both"}
That will only validate presence of last_name if company name is blank. You only need the one because both will be blank in the error condition, so to have a validator on company_name as well is redundant. The only annoying thing is that it spits out the column name before the message, and I used the answer from this question regarding Humanized Attributes to get around it (just setting the last_name humanized attribute to ""

Resources