Rails NoMethodError: undefined method `valid?' - ruby-on-rails

I am relatively new to rails and I can't figure out how to fix this bug.
So here are my models:
User
class User < ActiveRecord::Base
before_save { email.downcase! }
has_many :tickets
has_many :events, through: :tickets
validates :first_name, presence: true
validates :last_name, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255},
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
end
Event:
class Event < ActiveRecord::Base
has_many :tickets
has_many :users, through: :tickets
validates :event_title, presence: true
validates :event_place, presence: true
validates :event_description, presence: true
validates :event_date, presence: true
validates_numericality_of :event_number_tickets, presence: true, numericality: { only_integer: true }, greater_than: 0
end
Ticket:
class Ticket < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
I have a test file for tickets which is
require 'test_helper'
class TicketTest < ActiveSupport::TestCase
def setup
#user = User.new(first_name: "Example", last_name: "User")
#event = Event.new(event_title: "Example Event",
event_place: "123 Example Ave.",
event_description: "Our example event for children to get introduced to technology",
event_date: DateTime.now,
event_number_tickets: 30)
#ticket = Ticket.new(event: #event, ticket_type: "regular", number_of_children: 1)
#user.tickets << #ticket
end
test "should be valid" do
assert #user.tickets.valid?
end
end
Whenever I run this test I get the following error:
NoMethodError: undefined method `valid?' for #<Ticket::ActiveRecord_Associations_CollectionProxy:0x007fae767617c8>
test/models/ticket_test.rb:18:in `block in <class:TicketTest>'
Do I have to create a custom valid method for this particular example.

There's not a valid? method for an ActiveRecord collection. If you're trying to test that the last Ticket you created is valid, you can do something like assert #ticket.valid?.
I think you might be doing a bit too much work in your setup method. You could try to just setup your User and Event model in the setup, and break the rest of what you described into different test blocks.

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.

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.

Unit testing a polymorphic association with fixtures in rails 4

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.

Creating a has_and_belongs_to_many relationship in Rails

I have a User model which is designed after the Michael Hartl RoR tutorial and I am trying to create a new Teacher model. I would like the teacher to have many users but each user to have only one teacher. I created the teacher model with
class CreateTeachers < ActiveRecord::Migration
def change
create_table :teachers do |t|
t.string :name
t.string :email
t.string :phone
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
and added has_one :teacher to user.rb. Here is the teachers.rb model
class Teacher < ActiveRecord::Base
has_and_belongs_to_many :users
validates :user_id, presence: true
before_save :downcase_email
validates :name, presence: true,
length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
end
However in my teacher_test.rb test file, things get a little fuzzy. I try this
def setup
#user = users(:michael)
#user2 = users(:archer)
#user3 = users(:lana)
#user4 = users(:mallory)
#teacher = Teacher.new(name: "Phred Willard",
email: "pwillard#test.com",
phone: "1234567890",
user_id: [#user.id,
#user2.id,
#user3.id,
#user4.id])
end
test "should be valid" do
assert #uac.valid?
end
but that fails outright. Did I set my relationship up correctly? I obviously am not adding users correctly since the model fails a validity test. How would I add more users to that teacher? Thanks in advance.
I would like the teacher to have many users but each user to have only one teacher
You only need has_many / belongs_to...
#app/models/user.rb
class User < ActiveRecord::Base
belongs_to :teacher
end
#app/models/teacher.rb
class Teacher < ActiveRecord::Base
has_many :users
end
You'll need to add a teacher_id column in your users table (the opposite of what you have now):
class UpdateUsers < ActiveRecord::Migration
def change
change_table :users do |t|
t.references :teacher, index: true, foreign_key: true #-> teacher_id
end
end
end
--
The error you have is that you're calling user_id on teacher; it should be teacher_id on user:
#teacher = Teacher.new(name: "Phred Willard",
email: "pwillard#test.com",
phone: "1234567890",
user_ids: [#user.id,
#user2.id,
#user3.id,
#user4.id])
This should associate #teacher with the defined #users you've listed.
You'll also want to look at collection_singular_ids for the has_many association, which is why your test is failing.
Your teacher.rb should be
class Teacher < ActiveRecord::Base
has_many :users
before_save :downcase_email
validates :name, presence: true,
length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true,
length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
end
and user.rb
class User < ActiveRecord::Base
belongs_to :teacher
# rest of your code here ....
end
You need teacher_id column in users table.

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

Resources