Rails validation with if - ruby-on-rails

I am having trouble getting my validation working properly. I am trying to check if the user has checked :noship then :weight must equal 0.
With the code below I get an "Undefined local variable noship" error
snippet from models/package.rb
class Package < ActiveRecord::Base
belongs_to :campaign
validates_presence_of :weight, :campaign
validates :weight, numericality: { equal_to: 0 }, :if => :noship_test?
def noship_test?
noship == true
end
rails_admin do
object_label_method do
:custom_name
end
end
The :noship and :weight values are working and being saved correctly to my database

Since noship belongs to campaign model, you'll need to do something like:
def noship_test?
campaign && campaign.noship
end
However having such a method seems redundant, just pass a lambda to if key:
validates :weight, numericality: { equal_to: 0 }, :if => ->(r) { r.campaign && r.campaign.noship }

Related

Lifecycle of with_options in Rails. Why not working?

Im with a very tricky problem with a validation code on my app.
I have a user and a talent. A User has_one Talent. And, depending on the steps of the application, I need to validate some cases.
Those are my classes.
class User < ApplicationRecord
has_one :talent, dependent: :destroy
has_one :client, dependent: :destroy
has_one :agency, dependent: :destroy
before_create :set_initial_step
after_create :build_type
def build_type
logger.debug("Im inside build_type ***************** with #{type}")
if type.nil?
t = Talent.create(user: self)
Rails.logger.debug("checking errors ******#{type}*********** with #{t.errors.full_messages}")
else
o = Object.const_get(type.capitalize).create(user: self)
Rails.logger.debug("Im inside build_type ****#{type}************* with #{o.errors.full_messages}")
end
end
class Talent < ApplicationRecord
with_options if: Proc.new { |t| t.user.approval_submission_step >= 2 } do |t|
byebug
t.validates :talent_type_id,
:rate,
presence: true
t.validates :rate, numericality: { greater_than_or_equal_to: 25 }
end
with_options if: Proc.new { |t| t.user.approval_submission_step >= 3 && t.talent_type.is_model } do |t|
t.validates :gender,
:ethnicity,
:height_feet,
:height_inches,
:weight,
:eye_color,
:hair_color,
presence: true
t.validates :men_pant_size_waist,
:men_pant_size_length,
:men_shirt,
:men_dress_shirt_size,
:men_dress_shirt_neck,
:men_dress_shirt_sleeve,
:men_jacket,
:men_shoe_size,
presence: true, if: Proc.new { |t| t.gender == 'male' }
t.validates :ethnicity,
:inclusion => {in: VALID_ETHNICITY_TYPES}
t.validates :womens_dress_size,
:women_shoe_size,
:women_shirt,
:womens_dress_size,
:women_pant,
:women_bra_cup,
:women_bra_size,
presence: true, if: Proc.new { |t| t.gender == 'female' }
end
First weird thing. The byebug in the middle of the with_options its called when the server starts or when I do a rails console.
When I create a new User and save it (using the callback after_create of the user)
t = Talent.create(user: self)
The byebug its not called.
What Im doing wrong? Why the with_options on Talents are being called on the class-loading process but not when creating a new one?
First weird thing. The byebug in the middle of the with_options its called when the server starts or when I do a rails console.
with_options is evaluated when the class is loaded, that's why byebug only stops the excecution on those moments. That's how it works.
I'm not really sure what you want to do with those byebug calls, but I think you want to add them inside the Proc's, that code is actually evaluated at the moment with the current instance.
with_options if: Proc.new { |t| byebug; t.user.approval_submission_step >= 2 } do |t|

How to validates Rails column with self other column not blank?

I have a mode named Exam.
There are some columns in exames:
:title
:subject_id
:exam_type
I want to know how to implement this:
class Exam < ApplicationRecord
validates :title, presence: true
validates :subject_id, presence: true, if: :no_exam_type?
def no_exam_type?
self.exam_type == ""
end
end
That is to say, I want to create a exam:
Exam.create(title: "first exam", exam_type: "something")
The subject_id must be exist, when exam_type is blank, such as exam_type="" or just do:
Exam.create(title: "first exam", subject_id: 3)
because exam_type has a default blank value.
But the subject_id doesn't necessary provide, when exam_type not blank, such as exam_type="something".
Exam.create(title: "first exam", exam_type: "something", subject_id: 3)
I test it, but no lucky.
How to do that? Thanks appreciate.
In Rails 5 belongs_to associations default to optional: false. Which means that the model will automatically validate the presence of the association.
class Thing < ApplicationRecord
belongs_to :other_thing
end
Thing.create!
# => ActiveRecord::RecordInvalid: Validation failed: other_thing can't be blank
So you need to set the association as optional and make sure the column is nullable.
class Exam < ApplicationRecord
belongs_to :subject, optional: true
validates :title, presence: true
validates :subject_id, presence: true, if: :no_exam_type?
def no_exam_type?
!self.exam_type.present?
end
end
Have you tried like this.
validates :subject_id, presence: true, :if => exam_type.blank?
you can refer the doc here to suite your requirement
use validates_presence_of instead.
validates_presence_of :subject_id, if: :no_exam_type?
def no_exam_type?
self.exam_type.nil?
end

FactoryGirl giving me an error

I got this model:
rails g model Absence user:references company:references from:date to:date date:date category:integer hours:decimal remarks
This also generates:
FactoryGirl.define do
factory :absence do
user nil
company nil
from nil
to nil
date nil
category 0
hours "8.00"
remarks "MyString"
end
end
I set from, to and date to nil because it's either: from and to OR a certain date.
When I try this in my spec:
#absence = create(:absence, user: #company.owner, from: "2015-09-10", to: "2015-09-10", hours: 4)
I receive this error message:
NoMethodError:
undefined method `from=' for #<Absence:0x007f81f5494b88>
What could be wrong?
Edit:
When I remove the
from nil
from the factories/absences.rb I'm getting it on the next field (to) and after removing that I'm seeing the error message on category.
Edit2:
Model:
class Absence < ActiveRecord::Base
belongs_to :user
belongs_to :company
enum type: {holiday: 0, sick: 1}
validates :from, presence: true, if: '!user_id.nil?'
validates :to, presence: true, if: '!user_id.nil?'
validates :date, presence: true, if: '!company_id.nil?'
validates :hours, presence: true, if: '!user_id.nil?'
validates :hours, :numericality => { :greater_than_or_equal_to => 0 }, if: '!user_id.nil?'
validates :category, presence: true, if: '!user_id.nil?'
validates_numericality_of :company_id, allow_nil: true
validates_numericality_of :user_id, allow_nil: true
validate :company_xor_user
validate :to_date_after_from_date
validate :hours_smaller_than_workday
validate :non_overlapping
after_save :calculate_time_checks
after_destroy :calculate_time_checks_delete
DB:
https://www.evernote.com/shard/s29/sh/e8c1429d-9fa7-475b-87e8-3dc11a3f3978/08a7e7d6dfd80c6f407339cab97734c2
FINALLY found the real cause.
At first I had the Absence model created with an attribute named 'type'. This was migrated to both the development and test database. Afterwards I changed it to category and added 'from' and 'to' as well and did a rollback and migrated again (but not on test!).
By using pry
require 'pry'; binding.pry
in the test I did Absence.columns and noticed the difference.

NoMethodError - undefined method `by_email' for ActiveModel::MassAssignmentSecurity::BlackList:Class:

I have an email_address object that I am trying to check to see if it is on the blacklist for that particular domain. I'm calling it like this:
elsif #email.blacklisted?(#domain.id)
do something ...
end
I am getting the error:
NoMethodError - undefined method `by_email' for ActiveModel::MassAssignmentSecurity::BlackList:Class:
I have also tried doing a .find_all_by_id instead of using the blacklist scopes I created. Same error though. This is driving me crazy, any ideas would be amazing!
EmailAddress Class
class EmailAddress < ActiveRecord::Base
attr_accessible :email, :global_blacklist
has_many :transactions
has_many :black_lists
has_many :opt_outs
validates :email, :presence => true,
:uniqueness => true
validates :global_blacklist, :acceptance => true
def blacklisted?(domain_id)
black_lists = BlackList.by_email(self.id).by_domain(domain_id)
black_lists.count > 0
end
end
BlackList Class
class BlackList < ActiveRecord::Base
attr_accessible :domain_id, :email_address_id, :date_added
belongs_to :domain
belongs_to :email_address
validates :domain_id, :presence => true
validates :email_address_id, :presence => true
validates :date_added, :presence => true
GLOBAL_BLACK_LIST_THRESHOLD = 2
scope :by_domain, ->(domain_id) { where('domain_id = ?', domain_id) }
scope :by_email, ->(email_id) { where('email_address_id = ?', email_id) }
end
Just in case anyone else has this problem...
The class name BlackList (capitol L) seems to be an ActiveModel Class Name.
I changed the class name to Blacklist (one word instead of two) and the problem went away.

Rails Model Validation Conditional allow_nil?

So I was wondering if we can have a conditional allow_nil option for a validation on a rails model.
What I would like to do is to be able to allow_nil depending upon some logic(some other attribute)
So I have a product model which can be saved as draft. and when being saved as draft the price can be nil, but when not a draft save the price should be numerical. how can I achieve this. The following doesn't seem to work. It works for draft case but allows nil even when status isn't draft.
class Product<ActiveRecord::Base
attr_accessible :status, price
validates_numericality_of :price, allow_nil: :draft?
def draft?
self.status == "draft"
end
end
Looking at rails docs I looks like there isn't an option to pass a method to allow_nil?
One possible solution is to do separate validations for both cases
with_options :unless => :draft? do |normal|
normal.validates_numericality_of :price
end
with_options :if => :draft? do |draft|
draft.validates_numericality_of :price, allow_nil: true
end
Any other ways to get this working ?
Thanks
You can use if and unless to do the following
class Product<ActiveRecord::Base
attr_accessible :status, price
validates_numericality_of :price, allow_nil: true, if: :draft?
validates_numericality_of :price, allow_nil: false, unless: :draft?
def draft?
self.status == "draft"
end
end
With the above, you will have 2 validations set up, one that applies when draft? == true, which will allow nils, and one where draft? == false which will not allow nils
You don't need allow_nil then, just use if:
class Product < ActiveRecord::Base
attr_accessible :status, price
validates_numericality_of :price, if: -> { (draft? && !price.nil?) || !draft? }
def draft?
self.status == "draft"
end
end

Resources