Rails 4: Active Record Validation error on: create - validation

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

Related

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

Order of before_save callbacks in rails

I am trying to geocode an address and I am trying to geocode on a non-persistent attribute called full_address. Here is my code:
class Company < ActiveRecord::Base
include ActiveModel::Dirty
validates :name, :organization, :title, :state, :city, presence: true
validates :email, presence: true, length: { maximum: 255 },
format: { with: /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i, }
before_save :full_address
geocoded_by :full_address
before_save :geocode, if: ->(obj){obj.full_address.present? && (obj.city_changed? || obj.state_changed?)}
def full_address
"#{city}, #{state}"
end
end

I am not sure if my before_save callbacks are firing the in the right order. Basically, how do I make sure the
before_save :full_address
fires before the other?
I looked into the around_save documentation, but I am still unsure what it's doing.
Jwan --
AR callbacks are used for a lot of reasons, and they are particularly helpful for dealing with records in the DB, but you don't need one here for for 'full_address'. Why?
Because all it is it doing is returning a string, not performing an operation on the db or altering data in any way.
It's been awhile since I've worked with geocoder gem, but in your other before_save callback, you are passing a lambdha for conditional operation.
Couple things:
1.) You're checking for presence of an interpolated string, but validating presence of the two interpolated attributes, which fires before the callback, so obj.full_address.present? should always return true because it will blow up on the validations if both or even one of those attributes isn't present. Try
before_save :geocode, if: ->(obj){ obj.city_changed? || obj.state_changed? }
+1 though on ActiveModel::Dirty implementation!
2.) Unless you're using a much older version of Rails or explicitly configuring it otherwise, ActiveModel::Dirty is autoloaded; no need for line 2.
3.) I would also try using an after_validation callback instead of the before_save. Can't guarantee that will solve the problem, but it is earlier in the callback stack. Refer to http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Client side validation is not working while applying conditional validation in rails 4

I am using client side validation gem. Client side validation is not working while applying conditions.
Here is my Model Class.
validates_uniqueness_of :project_code
**validates :price, numericality: true, :if => :project_type_fixed?**
def project_type_fixed?
project_type == 'Fixed'
end
In this code validation for project_code is working fine, but for price it is not working.
Thanks in advance.
Client_side_validations does not validate conditionals out-of-the-box. What you are observing is the intended behavior.
In order to validate conditionals, you need to force them in your form:
f.number_field :price, :validate => { :numericality => true}
In addition, according to the documentation, the value needs to evaluate to true at the time the form is being generated. However, there is a hack for this: The method that determines whether the condition evaluates to true is called run_conditional (source), so you can override that method in your model:
def run_conditional(method_name_value_or_proc)
(:project_type_fixed? == method_name_value_or_proc) || super
end

Pass Validation on Form Fields Before Friendly ID Creation - Custom

I currently have two models
Location and Product
I have it configured that when a record is created in my production model, it creates a custom url based on the information it has gather from the location selected during the product creation
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
def should_generate_new_friendly_id?
name_changed? or location_id_changed?
end
def slug_candidates
[
[location.media_type, location.state, location.name, :name, :sku]
]
end
What I am currently testing is when a user decides NOT to fill out those very important fields, for it to throw an error message before creating
validates :name, presence: true
validates :sku, presence: true
validates :location_id, presence: true
What is happening in my case, is that it overlooks that validator and first tries to create the slug. If I remove the custom attributes to the url creation and list as
def slug_candidates
[
[:name, :sku]
]
end
it will work fine, running the field validators first. Assuming because those two are attributes on the given model directly.
Does anyone know why this is happening? I need for the validators to be picked up first since it contains all the relevant information for the url.
Solved
def slug_candidates
if self.location_id.nil?
self.errors.add(:location_id)
else
[
[location.media_type, location.state, location.name, :name, :sku]
]
end
end

Change priority of Custom validations in rails model

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!

Resources