I have this simplified model:
class Contract < ActiveRecord::Base
belongs_to :user belongs_to :plan
before_validation :set_default_is_voided
before_validation :set_default_expiration
validates :user, presence: true
validates :plan, presence: true
validates :contract_date, presence: true
validates :is_voided, presence: true
validates :expiration, presence: true
protected
def set_default_is_voided
if self.is_voided.nil?
self.is_voided = false
ap self.is_voided.present?
ap self.is_voided
end
end
def set_default_expiration
if self.contract_date.present?
self.expiration = self.contract_date+1.month
end
end
end
And this rspec simplified test:
context "Should create default values" do
it "Have to create is_voided" do
user = FactoryGirl.create(:user)
plan = FactoryGirl.create(:planContract)
ap "HERE"
contractDefault = FactoryGirl.create(:contractDefault, plan: plan, user: user)
ap contractDefault
expect(contractDefault.is_voided).to eq(false)
end
it "Have to create expiration" do
#expect(contract.expiration).should eq(Date.today()+1.month)
end
end
FactoryGirl:
FactoryGirl.define do
factory :contractVoid, class:Contract do
end
factory :contractDefault, class:Contract do
contract_date Date.today
end
end
This test fail with an 'is_voided can't be blank'.
And the question is:
Why the method "set_default_is_voided" in before_validation don't pass the presence true validation? Moreover, the self.is_voided.present? return false, why is it happing?
You answered your own question as to why set_default_is_voided doesn't pass the the presence: true validation, namely that self.is_voided.present? returns false, which is how presence: true is determined.
self.is_voided.present? returns false because false.present? == false per A concise explanation of nil v. empty v. blank in Ruby on Rails
See Rails: how do I validate that something is a boolean? for one way to validate that a boolean field is not nil.
See http://www.quora.com/Why-does-Rails-make-false-blank-true for a Q&A on the motivation behind the definition of blank?.
Related
I'm new to rails, I have a trip class with three foreign key. Two of these associate it with the same class: Place.
This is my model:
class Trip < ApplicationRecord
belongs_to :from, class_name: "Place", foreign_key: "from_id"
belongs_to :to, class_name: "Place", foreign_key: "to_id"
belongs_to :vehicle, class_name: "Vehicle", foreign_key: "vehicle_id"
validates :price, presence: true
validates :time, presence: true
validates :from_id, presence: true
validates :to_id, presence: true, if: :from_different_to?
def from_different_to?
to_id != from_id
end
end
All model tests pass except for the last one:
class TripTest < ActiveSupport::TestCase
def setup
#place1 = Place.create(name:"NewYork",cap:"11111",lat:"1234",long:"1478")
#place2 = Place.create(name:"Los Angeles", cap:"22222", lat:"1234",long:"1478")
#vehicle = Vehicle.create(targa: "ab123cd",modello:"500",marca:"Fiat", posti:5,alimentazione:"benzina")
#trip = Trip.new(price: 10, time: Time.new(2021, 10, 14, 12,03), from_id: #place1.id, to_id: #place2.id,vehicle_id: #vehicle.id)
end
...
test "Departure id and arrival id should be different" do
#trip.to_id = #place1.id
assert_not #trip.valid?
end
that result in a failure:
Failure:
TripTest#test_Departure_id_and_arrival_id_should_be_different [/media/alessandro/DATA/Universita/Magistrale/1_anno/Programmazione_concorrente/hitchhiker/test/models/trip_test.rb:45]:
Expected true to be nil or false
I'm not able to understand why.
Can someone help me?
It seems like you think validates ... if: works differently as it actually does. This line
validates :to_id, presence: true, if: :from_different_to?
translates to validate that the to_id is present if the from_different_to method returns true. When from_different_to evaluates to false then do not validate. See Rails Guides.
That means when you define
#trip.to_id = #place1.id
assert_not #trip.valid?
in your test then the first line disables the check for the presence of the to_id. No validation, no error...
I suppose what you really try to achieve is to validate that to to_id is present and from_id and to_id are not equal. This can be done with a custom validation like this:
validates :to_id, presence: true
validate :validates_places_are_different
private
def validates_places_are_different
errors.add(:to_id, "must be different to from_id") if to_id == from_id
end
I'm not able to understand why. Can someone help me?
That if conditionally enables a validation. Your to_id is the same as from_id and so to_id is not validated at all. But even if it was, to_id has a value, so there wouldn't be an error from this field.
Overall, I'm not quite sure why are you expecting a validation error here or what that error should be. In my experience, assertions like assert_not #model.valid? are virtually useless. The record might not be valid because of unrelated reasons and you'll have no idea. Personally, I assert the exact error message I'm expecting. Something along these lines (rspec syntax)
it "requires first_name" do
expected_messages = {
first_name: [:blank],
}
#model.valid?
expect(#model.errors.full_messages).to eq expected_messages
end
An alternative to that of #spickermann is that:
class Trip < ApplicationRecord
belongs_to :from, class_name: "Place", foreign_key: "from_id"
belongs_to :to, class_name: "Place", foreign_key: "to_id"
belongs_to :vehicle, class_name: "Vehicle", foreign_key: "vehicle_id"
validates :price, presence: true
validates :time, presence: true
validates :from_id, presence: true
validates :to_id, numericality: {other_than: :from_id}, if: :from_place_id?
def from_place_id
from_id
end
def from_place_id?
!from_id.nil?
end
end
Note that we have to put a control to execute the last validates only if from_id is not null, because if we doesn't do that, we vanificate the control validates :from_id, presence:true on the superior line.
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
I need to unit test a promotions model where every campaign has a URL. There is a polymorphic association between promotions and reference_link. the reference link fixture:
reference_link:
linkable:
fix_1 (PromoCode)
How do I convince rails that the promotions fixture does indeed have a URL belonging to it?
In promotions test helper:
test "should have a URL associated with the promo code" do
promo_code = promo_codes(:fix_1)
promo_code.reference_link.url = nil
assert_not promo_code.valid?
promo_code2 = promo_codes(:fix_2)
assert promo_code2.valid?
end
promocode.rb
class PromoCode < ActiveRecord::Base
belongs_to :reward
has_one :reference_link, as: :linkable, dependent: :destroy
validates :email, presence: true
validates :code, presence: true
end
reference_link.rb
class ReferenceLink < ActiveRecord::Base
belongs_to :linkable, polymorphic: true,touch: true
validates :link_name, presence: true
validates :link_url, presence: true
validates_format_of :link_url, :with => /\A#{URI::regexp(['http', 'https'])}\z/
validates_length_of :link_url,:link_name, :maximum => 255
end
It's simple, you almost got it right. First, ensure the fixtures are correctly set:
# promo_codes.yml
promo_1:
email: 'foobar#gmail.com'
name: 'Foobar'
# reference_links.yml
reference_1:
linkable: promo_1
link_name: 'lorem'
link_url: 'http://loremipsum.com'
The tests:
# promotion_test.rb
test "should have a URL associated with the promo code" do
promo_code = promo_codes(:promo_1)
assert_eq true, promo_code.reference_link.present?
end
Do not forget that file naming is an important part of convention that Rails use.
I want to validate 1 params in model method, but i can't found any fit answers , please show me the right way.
class User < ActiveRecord::Base
validate :username, presence: true, length: 4..5, unique: true
validate :email, presence: true, unique: true, format: {with: /\A[a-z0-9\.]+#([a-z]{1,10}\.){1,2}[a-z]{2,4}\z/}
def self.get_post(id)
# how to call validate id ???
validates :id, numericality: true
if id.valid?
# true code
else
# false code
end
end
def change_profile
# How to check validate user and email
username.valid?
email.valid?
# some_code....
end
end
Thanks all.
You cannot use validates there, you can do this instead
def self.get_post(id)
if id.is_a? Numeric
# true code
else
# false code
end
end
You can use active model for your customization, you can not check validation on field to filed, but you can perform with active model with number of fields as per your requirement
http://railscasts.com/episodes/219-active-model
class User
include ActiveModel::Validations
validates_with UserProfile
end
class UserProfile < ActiveModel::Validator
def validate(record)
if some_complex_logic
record.errors[:base] = "This record is invalid"
end
end
private
def some_complex_logic
# ...
end
end
I have the following in my models/user.rb:
validates :company, presence: true
validates :title, presence: true
I have a secondary view where I want to create a user but not require this user to enter a company and a title. How would I do that without modifying the main user.rb?
This is for Rails 3.2
You can do by declaring custom validations the way #BroiSatse has answered or when saving the user you can pass validate: false as argument, do this way
#user.save(:validate => false)
I usually do sth like:
class User < AR::Base
validates :company, :title, presence: true, if: :validate_company_and_title?
def validate_company_and_title?
#validate_company_and_title.nil? || #validate_company_and_title
end
def skip_company_and_title_validation!
#validate_company_and_title = false
end
end
Then in your controller create action for given view you can do:
#user.skip_company_and_title_validation!