Get attribute of polymorphic parent before save, in Rails 3? - ruby-on-rails

I have Vehicles, each can have many bookings. Each Booking can have many Events. This question comes as I validate a new Booking and Event against an existing Vehicle.
When validating the Event model I need to traverse up to the Vehicle and find all the Bookings and any Events that may clash with the new one, before I've actually saved the new Booking and Event.
Class Event < ActiveRecord::Base
belongs_to :eventable, :polymorphic => true
end
Class Booking < ActiveRecord::Base
belongs_to :vehicle
has_many :events, :as => :eventable
end
Class Vehicle < ActiveRecord::Base
has_many :bookings
end
When creating the Booking it has vehicle_id. How can I get the vehicle_id inside the Event model?

You would normally use validates_uniqueness_of with a :scope, but the join association here won't work that way. Here's an example of a custom uniqueness validator:
class Booking
# Create a custom validation
validate :verify_unique_vehicle_date
private
def verify_unique_vehicle_date
if booking = Booking.includes(:events).where('vehicle_id = ? AND events.date IN (?)', self.vehicle_id, self.events.map(&:date)).first
errors.add(:vehicle, "already booked on #{booking.events.map(&:date).join(',')}")
end
end
end

Related

How to validate has_one relationship?

I have these two models
User.rb
has_one :bike
Bike.rb
belongs_to :user
If a user tries to create multiple bikes, then the has_one relation doesn't make sense.
How can I add validation for this condition in the model level?
How can I make sure that the user will be always having one bike?
Create a before_create callback in bike.rb file. Check if the current_user.bike has any record or not. If record exists then add an error and return.
class Bike < ApplicationRecord
# Associations
has_one :user
before_create :user_exists
private
def user_exists
if self.user.bike.present?
errors[:base] << "Add your validation message here"
return false
end
end
end
You can simply add a validates_uniqueness_of call on your Bike model.
class Bike < ApplicationRecord
belongs_to :user
validates_uniqueness_of :user_id
end

Get current object on has_many side when saving

I am creating an Event that has many Equipments. I want to validate the Equipment model. How can I access the equipment's event that I am currently saving? I need that to get the date when the equipment will be used(date is known from Event model). Here is the code:
class Event < ApplicationRecord
has_many :equipment_events, class_name: EquipmentEvent
has_many :equipment, through: :equipment_events
end
class Equipment < ApplicationRecord
has_many :equipment_events
has_many :events, through: :equipment_events
validate :equipment_already_used
def equipment_already_used
date = # <== HERE I need the date from this equipment's event that I am saving
end
end
You need to move the validation from Equipment to EquipmentEvent, since Equipment and Event are independent entities.
class EquipmentEvent < ApplicationRecord
belongs_to :equipment
belongs_to :event
validate :equipment_already_used
def equipment_already_used
event.date # <== HERE
end
end

RailsActive Record and Nested Resources

I would like for click action on a button to result in the addition of a resource to a join table. There are several ways to do this in the console but I can't for the life of me figure out how to implement this outside the console.
Here is an example that mimics my current model:
class Student < ActiveRecord::Base
has_many :reports
has_many :schools, through: :reports
end
class School < ActiveRecord::Base
has_many :reports
has_many :students, through: :reports
accepts_nested_attributes_for :reports
end
class Report < ActiveRecord::Base
belongs_to :student
belongs_to :school
accepts_nested_attributes_for :student
accepts_nested_attributes_for :school
validates :student_id, presence: true
validates :school_id, presence: true
end
This method returns all the report cards that belong to a student (student already exists in
my database):
#student.reports
This method returns all the schools that a student has attended:
#student.schools
I can add/associate an existing school to a student by doing this:
#school = School.find(params[:id])
if student.present?
#student = Student.find(params[:id])
#student.schools << #school
Please note that the association of a single report to many students is intentional. My question now is how do I enable a student to add a school to their report simply by clicking on a particualr school? My reports table (which basically is a join table) should be automatically updated as soon as this click_action takes place/happens (that is a new row that associates that particular student_id with that particular school id should be created).
Been trying to figure it out but not making progress for some reason. Thank you in advance!
Well, to start with, in your view you should have a javascript/DOM event for each school:
onclick(window.location.href = "<%= path_to_add_school_to_students(student,school) %>")
So there you have your one click.
In your controller
student=Student.find(params[:student])
if student.schools.find(params[:school]).nil? # if school not already assigned
student.reports.create(:school_id => params[:school])
end

Rails - Query with has_many association

I am currently trying to create a custom method on a model, where the conditions used are those of a has_many association. The method I have so far is:
class Dealer < ActiveRecord::Base
has_many :purchases
def inventory
inventory = Vehicle.where(:purchases => self.purchases)
return inventory
end
end
This is not working, due to the fact that Vehicle has_many :purchases (thus there is no column "purchases" on the vehicle model). How can I use the vehicle.purchases array as a condition in this kind of query?
To complicate matters, the has_many is also polymorphic, so I can not simply use a .join(:purchases) element on the query, as there is no VehiclePurchase model.
EDIT: For clarity, the relevant parts of my purchase model and vehicle models are below:
class Purchase < ActiveRecord::Base
attr_accessible :dealer_id, :purchase_type_id
belongs_to :purchase_item_type, :polymorphic => true
end
class Vehicle < ActiveRecord::Base
has_many :purchases, :as => :purchase_item_type
end
class Dealer < ActiveRecord::Base
def inventory
Vehicle.where(:id => purchases.where(:purchase_item_type_type => "Vehicle").map(&:purchase_item_type_id))
end
end
Or:
def inventory
purchases.includes(:purchase_item_type).where(:purchase_item_type_type => "Vehicle").map(&:purchase_item_type)
end
I was able to do this using the :source and :source_type options on the Vehicle model, which allows polymorphic parents to be associated.

How to delete nested objects in Rails3?

How can I delete nested objects in a form? I found out that I need to add :allow_destroy in the parent model at the accepts_nested_attributes_for directive.
Further, I want to restrict the deletion. A nested object only should be deleted, if the parent object is the only one that retains the association.
Example:
class Internship < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company, allow_destroy => true
end
class Company < ActiveRecord::Base
has_many :internships
end
Explanation: A company can host many internships. Therefore, I do not want to delete the company record as long as there is at least one other internship associated with it.
You could use dependent => :destroy
class Internship < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company, allow_destroy => true
end
class Company < ActiveRecord::Base
has_many :internships, :dependent => :destroy
end
If you return false in a before_destroy filter, then the destroy action will be blocked. So we can check to see if there are any internships associated to the company, and block it if so. This is done in the company model.
class Company < ActiveRecord::Base
has_many :internships
before_destroy :ensure_no_internships
private
def ensure_no_internships
return false if self.internships.count > 0
end
end

Resources