Ruby on Rails | How to apply validation on rails form - ruby-on-rails

I am adding the promo code option for my application. I want to apply validation on the form that will match the entered promo code by the user to the promo code that has been already there in the admin dashboard (Activeadmin is used for admin dashboard).
I have tried a few options but nothing works. Examples that I have tried -
def validate_promo_code
if promo_code.present? and (!promo_code.match(EngineName::PromoCode.promo_code))
errors.add :promo_code, "must be a valid promo code"
end
end
def validate_promo_code
if promo_code.present? and (promo_code != EngineName::PromoCode.where(promo_code: promo_code))
errors.add :promo_code, "must be valid promo code"
return;
end
end
Does anyone has any idea how to achieve this? Please Help!

You have to make your activerecord model aware of the validation using the validate dsl statement. Does this work?
validate :ensure_valid_promo_code
def ensure_valid_promo_code
if promo_code.present? && (!promo_code.match(EngineName::PromoCode.promo_code))
errors.add :promo_code, "must be a valid promo code"
end
end

In case if someone is still looking for the solution, try this one -
def validate_promo_code
existing_code = EngineName::PromoCode.where(promo_code: promo_code)
if promo_code.present? and !(existing_code.present?)
errors.add :promo_code, "must be valid promo code"
end
end
This solution works for me.

Related

Custom validation Rails for boolean attributes

I need to add custom validation to my AR model: there is Order model with 'approved' attribute - this attribute cannot be approved twice. It's boolean attribute. I don't understand how can I check if this attribute has been approved already.
validate :cannot_be_approved_twice
def cannot_be_approved_twice
errors[:base] << ERROR_MSG if ...
end
How can I check it? Thanks!
If it is approved already then the value of that field would be something and not nil so you can do it like this:
def cannot_be_approved_twice
errors[:base] << ERROR_MSG unless approved.nil?
end
And if suppose it would have true value if it is approved then you can do:
def cannot_be_approved_twice
errors[:base] << ERROR_MSG if approved
end
Hope this helps.
I'd prefer attaching the error to the approved attribute, so the error would be something like this
validate :prevent_double_approval, if: :some_check
def prevent_double_approval
errors[:approved] << "can't be approved more than once" if approved?
end
This some_check method should be true when you detect that the user is trying to change the value, then the inner check if approved? checks if the value is already true before that second approval.
You can do it by checking previous value in your validation. Something like this:
def cannot_be_approved_twice
errors[:approved] << "can't be approved more than once" unless self.approved_was.nil?
end
The "_was" postfix gives the value before the update. So, if it was not nil then someone has set it to true or false (meaning approved or disapproved).
Update
I read through your comments and realized that what when order is created it is set to false (initially, I thought that it is set to nil, so you could approve or disapprove it). Thus, if the field initially set to false, then the method above becomes:
def cannot_be_approved_twice
errors[:approved] << "can't be approved more than once" if self.approved_was # if approved field was true (approved)
end
One more thing, with the way you are doing order update (order.update!(), with the bung), and your order has been approved then your application will fail with validation error because bung(!) explicitly tells application to fail in case of validation errors. You probably want to use just the "update" method and not "update!".

Stopping creation of posts unless approved is true

Afternoon All,
I have a model called snippet.rb and I would like only one user to post at a time until approved.
Would I run this as a custom validation or as an after_create in the snippet.rb.
The step process is below:
User creates snippet
Snippet submitted for approval
No other snippets can be created until the previous one has been approved.
Could someone help me or point me in the direction of some documentation on how to do this.
Always appreciate the help. I'm trying to work through this in my head but cannot find anything to help.
in your snippets_controller.rb
before_filter :check_last_snippet, :only => [:create]
private
def check_last_snippet
redirect_to root_path unless Snippet.last.approved?
end
I believe that you should use custom validation.
for example
validate :verify_for_not_approved_snippets
def verify_for_not_aproved_posts
errors.add(:base, "error message") if "your condition here"
end
more detaily you can read at http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#performing-custom-validations
create a method in your user model and call this method before creating snippet
def check_snippet
return true if user.snippets.blank? || (user.snippets.present? && user.snippets.where(approved: true).size > 1)
end
If this method returns true then only user can post snippet again. It may help you.
You can use validate on create,
validate :check_last_snippet_approved, :on => :create
def check_last_snippet_approved
errors.add(:base, "could not add due to last snippet not approved") if !self.last.nil? && self.last.approved == false
end

Rails custom validation for specific value

I have a user signup form where I am trying to restrict the ability to create an account to users who have a specific access code. I'm running into a problem with being able to validate for that specific code. I've included the code that I've been able to piece together, but isn't working for me.
#new.html.erb
<%= f.text_field :access, placeholder: "Access Code", required: "required" %>
#user.rb
validate :check_code
def check_code
errors.add(:base, 'Try another code') unless User.where(:access == "theAccessCode")
end
Can someone suggest an alternative to this validation?
If there's a single correct access code, I think you want something like this instead:
ACCESS_CODE = 'secretCode'
def check_code
errors.add(:base, 'Try another code') unless self.access == ACCESS_CODE
end
Alternatively, your code would probably need to look like this to work (although querying for an existing user still seems wrong):
def check_code
errors.add(:base, 'Try another code') unless User.where(:access => "theAccessCode").exists?
end

Locking an attribute after it has a certain value

I have a model where if it is given a certain status, the status can never be changed again. I've tried to achieve this by putting in a before_save on the model to check what the status is and raise an exception if it is set to that certain status.
Problem is this -
def raise_if_exported
if self.exported?
raise Exception, "Can't change an exported invoice's status"
end
end
which works fine but when I initially set the status to exported by doing the following -
invoice.status = "Exported"
invoice.save
the exception is raised because the status is already set the exported on the model not the db (I think)
So is there a way to prevent that attribute from being changed once it has been set to "Exported"?
You can use an validator for your requirement
class Invoice < ActiveRecord::Base
validates_each :status do |record, attr, value|
if ( attr == :status and status_changed? and status_was == "Exported")
record.errors.add(:status, "you can't touch this")
end
end
end
Now
invoice.status= "Exported"
invoice.save # success
invoice.status= "New"
invoice.save # error
You can also use ActiveModel::Dirty to track the changes, instead of checking current status:
def raise_if_exported
if status_changed? && status_was == "Exported"
raise "Can't change an exported invoice's status"
end
end
Try this, only if you really want that exception to raise on save. If not, check it during the validation like #Trip suggested
See this page for more detail.
I'd go for a mix of #Trip and #Sikachu's answers:
validate :check_if_exported
def check_if_exported
if status_changed? && status_was.eql?("Exported")
errors.add(:status, " cannot be changed once exported.")
end
end
Invalidating the model is a better response than just throwing an error, unless you reeeally want to do that.
Try Rails built in validation in your model :
validate :check_if_exported
def check_if_exported
if self.exported?
errors.add(:exported_failure, "This has been exported already.")
end
end

How to add custom errors to the User errors collection?

How can I add errors to the User model manually?
Is it just #user.errors << "some text goes here" ?
#user.errors.add(:email, "Not valid")
If you don't want to use any attributes, then in your model
#user.errors[:base] << "This person is invalid because ..."
For details: link
WARNING
If you just add errors in a separate method (not in a validation), then by default when you call .valid? or .save those errors will be automatically cleared. So you may want to use validation contexts instead.
Use the errors.add method
Example:
#user.errors.add(:name, "wasn't filled in")
try this:
errors.add(:base, "#{user.full_name} has errors here!")
Expanding on Jason Axelson's answer:
Manually added errors (e.g. errors.add(:email, 'invalid')) will not affect .save or .valid?.
You can use validation contexts to run conditional validations:
class User < ActiveRecord::Base
validate :ensure_something, on: :special_context
private
def ensure_something
errors.add(:email, 'invalid') if special_condition
end
end
user.save!(context: :special_context)
user.valid?(context: :special_context)

Resources