Change priority of Custom validations in rails model - ruby

I have implemented validations in a dependent manner, like if start_date format is invalid so i don't want to run other validation on start_date.
validates_format_of :available_start_date, :with => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}((((\-|\+){1}\d{2}:\d{2}){1})|(z{1}|Z{1}))$/, :message => "must be in the following format: 2011-08-25T00:00:00-04:00"
This checks for a specific format and then i have custom validation methods called from which should run later.
def validate
super
check_offer_dates
end
I have used self.errors["start_date"] to check if the error object contains errors, it should skip the other validations on same parameter if it's not empty.
But the problem is def validate is called first and then the validates_format_of. How can i change this so that the flow can be achieved.

I just ran into a similar problem; this is how I fixed it using a before_save callout:
Not working (validates in wrong order - I want custom validation last):
class Entry < ActiveRecord::Base
validates_uniqueness_of :event_id, :within => :student_id
validate :validate_max_entries_for_discipline
def validate_max_entries_for_discipline
# set validation_failed based on my criteria - you'd have your regex test here
if validation_failed
errors.add(:maximum_entries, "Too many entries here")
end
end
end
Working (using before_save callout):
class Entry < ActiveRecord::Base
before_save :validate_max_entries_for_discipline!
validates_uniqueness_of :event_id, :within => :student_id
def validate_max_entries_for_discipline!
# set validation_failed based on my criteria - you'd have your regex test here
if validation_failed
errors.add(:maximum_entries, "Too many entries here")
return false
end
end
end
Note the changes:
validate_max_entries_for_discipline becomes validate_max_entries_for_discipline!
validation method now returns false on failure
validate validate_max_entries_for_discipline becomes before_save validate_max_entries_for_discipline!

Related

How to conduct Rails model validation witih or condition?

I’m using Rails 4.2.3. I have three fields in my model — name, first_name, and last_name. would like to have a validation rule in my model that causes a save to fail if the “name” field is empty unless either the first_name or last_name field is not empty. SOoI tried
validates_presence_of :name, :unless => !:first_name.empty? or !:last_name.empty?
but this doesn’t work. I get the error below
undefined method `validate' for true:TrueClass
What is the proper way to write the validation rule above?
Everything you need to know is here.
You can write the rule by defining a separate method for it:
class Whatever < ActiveRecord::Base
validates :name, presence: true, unless: :firstname_or_surname?
def firstname_or_surname?
firstname.present? || surname.present?
end
end
Or you can use a Proc to define it inline:
class Whatever < ActiveRecord::Base
validates :name, presence: true,
unless: Proc.new { |a| a.firstname.present? || a.surname.present? }
end

How to verify if an embedded field changed on before_save?

I am running Ruby 2.1 and Mongoid 5.0 (no Rails).
I want to track on a before_save callback whether or not an embedded field has changed.
I can use the document.attribute_changed? or document.changed methods to check normal fields, but somehow these don't work on relations (embed_one, has_one, etc).
Is there a way of detecting these changes before saving the document?
My model is something like this
class Company
include Mongoid::Document
include Mongoid::Attributes::Dynamic
field :name, type: String
#...
embeds_one :address, class_name: 'Address', inverse_of: :address
#...
before_save :activate_flags
def activate_flags
if self.changes.include? 'address'
#self.changes never includes "address"
end
if self.address_changed?
#This throws an exception
end
end
One example of how I save my document is:
#...
company.address = AddressUtilities.parse address
company.save
#After this, the callback is triggered, but self.changes is empty...
#...
I have read the documentation and Google the hell out of it, but I can't find a solution?
I have found this gem, but it's old and doesn't work with the newer versions of Mongoid. I want to check if there is another way of doing it before considering on trying to fix/pull request the gem...
Adding these two methods to your Model and calling get_embedded_document_changes should provide you an hash with the changes to all its embedded documents:
def get_embedded_document_changes
data = {}
relations.each do |name, relation|
next unless [:embeds_one, :embeds_many].include? relation.macro.to_sym
# only if changes are present
child = send(name.to_sym)
next unless child
next if child.previous_changes.empty?
child_data = get_previous_changes_for_model(child)
data[name] = child_data
end
data
end
def get_previous_changes_for_model(model)
data = {}
model.previous_changes.each do |key, change|
data[key] = {:from => change[0], :to => change[1]}
end
data
end
[ source: https://gist.github.com/derickbailey/1049304 ]

Rails 4: validate a model only on specific action

I want to validate an email object only when I'm actually sending the email and not everytime I'm updating it:
class Email < ActiveRecord::Base
# Runs on every update/save method
validates :body, presence: true
def send_email
# Only run when this method is called
if body.blank?
errors.add :body, "can't be blank"
raise ActiveRecord::InvalidRecord.new(self)
end
update_column(:sent_at, Time.zone.now)
EmailMailer.new_email(self).deliver
end
end
Currently, this code will validate the body on every update, but I only want to validate when the send_email method is called. The reasoning is I want to allow users to save the email without actually sending, and this validation forces them to have a value.
I can manually check the validation in my method, but was wondering if there was a "cleaner" approach to this, something like:
validates :body, presence: true, action: :send_email # does not work, but what I want
Update 2015-08-13 15:30
I have some semi-working code, but would prefer a cleaner solution than what I have:
class Email < ActiveRecord::Base
# remove validate methods
def send_email
errors.add :body, "can't be blank" if body.blank?
# My application has some higher level controller code that handles exceptions.
raise ActiveRecord::InvalidRecord.new(self) if errors.any?
# code execution should be stopped by exception...
end
end

Rails 4: Active Record Validation error on: create

If I have some before create action like:
before_create :generate_token
private
def generate_token
self.auth_token = loop do
random_token = SecureRandom.urlsafe_base64(nil, false)
break random_token unless self.class.exists?(auth_token: random_token)
end
end
When writing validations I find validates :auth_token, presence: true, on: :create fails.
However, validates :auth_token, presence: true, on: :after_create works.
Why is this?
ref. Active Record Callback Order and Active Record Validations :on
edit: Curiously, on: :before_create passes validation, on: :create fails, and on: after_create succeeds. This is puzzling, could some please explain this?
Read the docs you linked, to:
(1) before_validation
(-) validate <======== This runs before before_create callbacks are run
(2) after_validation
(3) before_save
(4) before_create
I would consider generate the token when the object is initialized.
after_initialized :generate_token

Rails3: Nested model - child validates_with method results in "NameError - uninitialized constant [parent]::[child]"

Consider the following parent/child relationship where Parent is 1..n with Kids (only the relevant stuff here)...
class Parent < ActiveRecord::Base
# !EDIT! - was missing this require originally -- was the root cause!
require "Kid"
has_many :kids, :dependent => :destroy, :validate => true
accepts_nested_attributes_for :kids
validates_associated :kids
end
class Kid < ActiveRecord::Base
belongs_to :parent
# for simplicity, assume a single field: #item
validates_presence_of :item, :message => "is expected"
end
The validates_presence_of methods on the Kid model works as expected on validation failure, generating a final string of Item is expected per the custom message attribute supplied.
But if try validates_with, instead...
class Kid < ActiveRecord::Base
belongs_to :parent
validates_with TrivialValidator
end
class TrivialValidator
def validate
if record.item != "good"
record.errors[:base] << "Bad item!"
end
end
end
...Rails returns a NameError - uninitialized constant Parent::Kid error following not only an attempt to create (initial persist) user data, but also when even attempting to build the initial form. Relevant bits from the controller:
def new
#parent = Parent.new
#parent.kids.new # NameError, validates_* methods called within
end
def create
#parent = Parent.new(params[:parent])
#parent.save # NameError, validates_* methods called within
end
The error suggests that somewhere during model name (and perhaps field name?) resolution for error message construction, something has run afoul. But why would it happen for some validates_* methods and not others?
Anybody else hit a wall with this? Is there some ceremony needed here that I've left out in order to make this work, particularly regarding model names?
After a few hours away, and returning fresh -- Was missing require "Kid" in Parent class. Will edit.

Resources