Rails model how to check that two objects are not equal - ruby-on-rails

I have 2 models, Bid & Package. How can I validate in the model that this can never be true?
bid.user == bid.item.user
so that this unit test can pass
describe "user cannot bid on their own package" do
#bid.user should_not equal #bid.item.user
end
I am trying to make sure that a user cannot bid on their own items.
Edit:
My model is
class Bid < ActiveRecord::Base
belongs_to :user
belongs_to :package
validates :user_id, presence: true
validates :package_id, presence: true
validates :amount, presence: true, numericality: { greater_than: 0 }
.....
end

class Bid < ActiveRecord::Base
validate :cannot_bid_on_self
def cannot_bid_on_self
if user.id == item.user.id
errors.add(:user, "can't bid on own item")
end
end
...

Related

Rails Model Custom Validation Won't Work On Update

I try to create some model validation on my Coupon Model
evrything work on create, but when i try to update data the validation won't validate
the first validation is to raise error if percentage more than 100
the second validation is to validate if date start bigger than date expired it will throw some error
my model in this code:
# frozen_string_literal: true
class Coupon < ApplicationRecord
has_many :transactions, dependent: :restrict_with_exception
has_one :battery, through: :transactions
enum :discount_type, %i[percentage nominal]
validates :code, format: { with: /\A[0-9A-Z]+\Z/ },
presence: true,
uniqueness: true,
length: { maximum: 50 },
on: :create,
allow_nil: false
validate :discount_value, :discount_type
validate :discount_value_percentage
validates :start_at, :expired_at, presence: true
validate :end_date_after_start_date
def discount_value_percentage
return unless discount_type == 'percentage' && discount_value > 100
errors.add(:discount_value, 'discount value percentage cannot over 100%')
end
def end_date_after_start_date
return if expired_at.blank? || start_at.blank?
return unless expired_at < start_at
errors.add(:expired_at, 'must be after the start date')
end
end
Please help me to resolve my problem
the validation can validate if date start bigger than date end it will throw error code

Rails conditional validation: if: doesn't working

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.

Why throwing abort in a nested association raises “Failed to destroy the record” exception?

I need to validate if any CameraVectors has been associated to any MonitoredPlace before I destroy a Camera.
Camera's Model
class Camera < ApplicationRecord
belongs_to :location
has_many :camera_vectors, inverse_of: :camera, dependent: :destroy
validates :description, :device_serial, :device_name,
:device_type, :device_api_url, :device_user, :device_password,
presence: true
accepts_nested_attributes_for :camera_vectors, allow_destroy: true
end
CameraVector's model
class CameraVector < ApplicationRecord
belongs_to :camera, inverse_of: :camera_vectors
belongs_to :monitored_place, optional: true
validates :description, presence: true
validates :position, numericality: { greater_than_or_equal_to: 0 }, presence: true
before_destroy :has_monitored_place?
private
def has_monitored_place?
if monitored_place.present?
errors.add(:base, "cannot delete")
throw :abort
end
end
end
MonitoredPlace's model
class MonitoredPlace < ApplicationRecord
belongs_to :location
belongs_to :place_type
has_many :camera_vectors
validates :place_name, presence: true
validates :place_type_id, uniqueness: { scope: :location_id }, presence: true
scope :enabled, -> { where.not(enabled_on: nil).where(disabled_on: nil) }
end
Because of the accepts_nested_attributes_for whenever I try to update or destroy a camera this nested fields are sent as params
"camera_vectors_attributes"=>{"0"=>{"description"=>"A", "position"=>"1", "_destroy"=>"1", "id"=>"47"}}
I thought If I wrote a callback before_destroy in the model CameraVector I could validate it, but if the validation occurs it raises ActiveRecord::RecordNotDestroyed in the controller.
if #camera.destroy(camera_params)
redirect_to(action: :index, notice: t(".success"))
else
render :index
end
as you can read in the api documentation
ActiveRecord::RecordNotDestroyed
Raised by ActiveRecord::Base#destroy! when a call to #destroy would return false.
It is result of
before_destroy :has_monitored_place?
that calls a method and returns false.
def has_monitored_place?
if monitored_place.present?
errors.add(:base, "cannot delete")
throw :abort
end
end
to change this behavior implement a logic similar to the one described in the api
begin
complex_operation_that_internally_calls_destroy!
rescue ActiveRecord::RecordNotDestroyed => invalid
puts invalid.record.errors
end
or read
How do I 'validate' on destroy in rails

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

Rails model default_value method conflict with presence true

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?.

Resources