FactoryGirl giving me an error - ruby-on-rails

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.

Related

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.

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

Cannot create new Activerecord with belongs_to relationship

I am a beginner with Ruby on Rails and I am trying to create a seed.rb file to populate my database.
However I am having problems creating a new Request record.
This is my seed file:
if Rails.env == "development" || Rails.env == "test"
avaya = Company.create!(name: "Avaya")
microsoft = Company.create!(name: "Microsoft")
bradesco = Company.create!(name: "Bradesco")
customer = Customer.create!(name: "Leon Schmidt", email: "leon.schmidt#avaya.com", company_id: avaya.id)
Customer.create!(name: "Teri Wright", email: "teri.wright#avaya.com", company_id: avaya.id)
Customer.create!(name: "Jasmine Bush", email: "jasmine.bush#microsoft.com", company_id: microsoft.id)
Customer.create!(name: "Jake Blair", email: "jake.blair#bradesco.com.br", company_id: bradesco.id)
rate = Rate.create!(rate: 0.0165, days: 10, default_rate: true)
Request.create!(title_value: 1500, term: 28, monthly_revenue: 75000, receivable_value: 1425.75, rate_id: rate.id, customer_id: customer.id)
elsif Rails.env == "production"
Rate.create(rate: 0.0165, days: 10, default_rate: true)
end
Whenever I run rake db:reset to repopulate the development database I get this error:
rake aborted!
ActiveRecord::RecordInvalid: Validation failed: Customers must exist
c:/Desenvolvimento/Coding Tests/FactoringRateCalculator/db/seeds.rb:23:in `<top (required)>'
Tasks: TOP => db:reset => db:setup => db:seed
Please note that the line 23 is: Request.create!(title_value: 1500, term: 28, monthly_revenue: 75000, receivable_value: 1425.75, rate_id: rate.id, customer_id: customer.id)
My models are below:
class Customer < ApplicationRecord
belongs_to :company
has_many :requests
validates_associated :requests
validates :name, presence: true
validates :email, presence: true, email: true
end
class Request < ApplicationRecord
belongs_to :customers, required: true
belongs_to :rate, required: true
validates :term, presence: true, numericality: { greater_than_or_equal_to: 10 }
validates :title_value, presence: true, numericality: { greater_than_or_equal_to: 500 }
validates :monthly_revenue, :receivable_value, presence: true, numericality: true
end
class Rate < ApplicationRecord
has_many :requests
validates_associated :requests
validates :rate, numericality: true, presence: true
validates :days, numericality: { greater_than_or_equal_to: 10 }, presence: true
validates :rate, :days, :default_rate, presence: true
end
class Company < ApplicationRecord
has_many :customers
validates_associated :customers
validates :name, presence: true
end
I am using Ruby 2.3 and Rails 5.1.
Your required validation is specified with the plural version of your model:
belongs_to :customers, required: true
Try using the singular way:
belongs_to :customer, required: true
That's why the error says "Customers": "ActiveRecord::RecordInvalid: Validation failed: Customers must exist", because your seeds have been made following the relationship you specify.

Custom validation causing other validations to fail

Title pretty much explains the problem.
Here were my validations for my model:
class Lesson < ActiveRecord::Base
belongs_to :school
has_many :users, through: :goals
has_many :goals, dependent: :destroy
validates :school_id, presence: true
validates :date, presence: true, uniqueness: { scope: :school_id }
validates :attendance, numericality: { only_integer: true,
greater_than: 0 },
presence: true, if: :finished?
validates :lesson_plan_week_number, numericality: { only_integer: true,
greater_than: 0 },
presence: true, if: :finished?
end
All of my model tests (aside from the ones dealing with the custom validation) pass with the above code
Then I added validate :motion_presence and the following private method:
def motion_validation
if debate?
errors.add(:motion, "must be present if a debate was held")
else
errors.add(:motion, "cannot be present if a debate was held")
end
end
and now the tests that once passed are failing. What's happening?
Here's the relevant code in the model if I'm organizing it incorrectly
class Lesson < ActiveRecord::Base
belongs_to :school
has_many :users, through: :goals
has_many :goals, dependent: :destroy
validates :school_id, presence: true
validates :date, presence: true, uniqueness: { scope: :school_id }
validates :attendance, numericality: { only_integer: true,
greater_than: 0 },
presence: true, if: :finished?
validates :lesson_plan_week_number, numericality: { only_integer: true,
greater_than: 0 },
presence: true, if: :finished?
validate :motion_presence
private
# Validates the motion depending on debate?
def motion_presence
if debate?
errors.add(:motion, "must be present if a debate was held")
else
errors.add(:motion, "cannot be present if a debate was held")
end
end
end
def motion_presence
if debate?
errors.add(:motion, "must be present if a debate was held")
else
errors.add(:motion, "cannot be present if a debate was held")
end
end
This will always fail because you added to errors in either case.
If you wanted to fail it for multiple conditions, then try doing it in elsif. else means EVERYTHING ELSE and its failing always.
Do it like this:
def motion_presence
if debate?
errors.add(:motion, "must be present if a debate was held")
elsif some_condition
errors.add(:motion, "cannot be present if a debate was held")
end
end
That is because in any case whether debate? returns true or false, it is adding errors to the motion so it will never be successful which results in not saving the lesson object as errors are already added for motion field.
I think you need to add a case where it should just return without adding any errors. Besides just add errors for specific cases

Default value along with uniqueness validation

I have a Path model with name attribute as unique. I want to set default value as '/'
to the same.
I have done in the following manner.
class Path < ActiveRecord::Base
attr_accessible :name
validates :name, presence: true, uniqueness: true
before_validation :set_default_path
private
def set_default_path
self.name = name.presence || '/'
end
end
Domain model is designed as:
class Domain < ActiveRecord::Base
attr_accessible :name, path_id
validates :name, :path_id, presence: true
validates :name, uniqueness: {scope: :path_id}
end
But this doesn't work for consecutive inserts with a blank name for path.
path = Path.find_or_create_by_name('')
domain = Domain.new(name: 'stackoverflow.com')
domain.path = path
domain.save! # Fails with validation error
ActiveRecord::RecordInvalid:
Validation failed: Path can't be blank
Is there a robust way to achieve this ?
You should remove following callback
before_validation :set_default_path
and use validation for name as following:--
validates :name, presence: true, uniqueness: true, :if => 'name.present?'
and write a migration file to add default value to name attribute of paths table as either of followings:--
change_column :paths, :name, :string, :default => '/'
or
change_column_default :paths, :name, '/'
add condition on validation:
validates :name, presence: true
validates :name, uniqueness: true, unless: proc { |e| e.name == "/" }

Resources