Ruby Validation When Adding to Database - ruby-on-rails

I am really new in Ruby and I am on the last step to finish my project, when I'm trying to add appointment I have to change if doctor works in that time. I don't know how to do this :(
It is how my db works:
In appointment I have data_wizyty (visit_date), doctor_id and godzina_wizyty(visit_time) - it is in my adding form.
In schedules I have:
dzien_tygodnia(day_of_the_week), poczatek_pracy(start_working), koniec_pracy(end_working) and doctors_workplace_id
In doctors_workplace:
doctor_id, schedule_id, clinic_id
I want to check if doctor is available in any of the clinic in choosen date and time :)
Please help me with this :)
I have already validated if date and time is unique with:
class Appointment < ActiveRecord::Base
validates :doctor_id, uniqueness: { scope: [:data_wizyty, :godzina_wizyty], message: 'Ten termin jest juz zajety!' }
end
I need to check if it is unique and if doctor works.
Appointment:
class Appointment < ActiveRecord::Base
validates :doctor_id, uniqueness: { scope: [:data_wizyty, :godzina_wizyty], message: 'Ten termin jest juz zajety!' }
after_initialize :aInit
after_save :aSave
belongs_to :patient
belongs_to :doctor
belongs_to :schedule
belongs_to :refferal
belongs_to :clinic
has_many :employees
include MultiStepModel
def self.total_steps
3
end
def aInit
#wymaga_Potwierdzenia = true
end
def aSave
if self.refferal_id == nil
#potwierdzona = false
else
#potwierdzona = true
end
if self.wymaga_Potwierdzenia == false
#potwierdzona = true
end
end
end
Schedule:
class Schedule < ActiveRecord::Base
has_many :appointments
belongs_to :clinic
belongs_to :doctors_workplace
def full_schedule
"#{dzien_tygodnia} : #{poczatek_pracy} - #{koniec_pracy}"
end
end
Doctors_workplace:
class DoctorsWorkplace < ActiveRecord::Base
has_many :schedules
belongs_to :doctor
belongs_to :clinic_surgery
end
Now I have something like this :
def check_doctor_available
if Schedule.where(doctor: doctor, dzien_tygodnia: data_wizyty.wday)
.where('poczatek_pracy < ? and koniec_pracy > ?', godzina_wizyty, godzina_wizyty).empty?
self.errors.add(:doctor, message: 'nie pracuje w tym terminie!')
end
It's what I have now:
def check_doctor_available
if DoctorsWorkplace.where(doctor_id: doctor_id) and
Schedule.where(doctors_workplace_id: ????, dzien_tygodnia: data_wizyty.wday)
.where('poczatek_pracy < ? and koniec_pracy > ?', godzina_wizyty, godzina_wizyty).empty?
self.errors.add(:doctor, message: 'nie pracuje w tym terminie!')
end

You can use a custom validation. Create a private method in appointment that checks if the doctor is available at the given date/time.
validate :check_doctor_available
private
def check_doctor_available
#your implementation
end
Take a look at this if you have any doubts what to write in your custom validation method.

Related

Adding an additional model relationships using build() on Rails

Currently I have the following four models:
users <-- agency_memberships --> agencies
|
|
agency_roles
agency_memberships is a join table, and agency_roles is a small lookup table with the roles :
ID NAME
-----------
1 admin
2 editor
...
Before adding the AgencyRole model, when a user was created, if a param create_agency = true, then this was enough to create a new Agency (along the join table AgencyMembership).
# user_controller.rb
def create
#user = User.new(user_params)
#user.agencies.build(name: "Demo Agency") if params[:user][:create_agency]
if #user.save!
...
end
However, now I need to add a valid AgencyRole before saving.
Is it possible to do so with .build() or what is the Rails best practice to do so?
Right now, I'm creating all relationships manually before saving, which works but isn't as compact:
def create
#user = User.new(user_params)
if (params[:user][:create_agency])
agency_name = params[:user][:agency_name]
agency_role = AgencyRole.find_by(name: 'admin')
#agency_membership = AgencyMembership.new(agency_role: #agency_role)
#agency = Agency.new(name: agency_name)
#agency_membership.agency = #agency
#agency_membership.user = #user
#agency_membership.agency_role = agency_role
#user.agency_memberships << #agency_membership
end
if #user.save!
...
end
EdIT: My model relationships are as follows:
class AgencyMembership < ApplicationRecord
belongs_to :agency
belongs_to :user
belongs_to :agency_role
end
class AgencyRole < ApplicationRecord
validates :name, uniqueness: { case_sensitive: false }, presence: true
end
class Agency < ApplicationRecord
has_many :agency_memberships
has_many :projects
has_many :users, through: :agency_memberships
end
class User < ApplicationRecord
has_many :agency_memberships, inverse_of: :user
has_many :agencies, through: :agency_memberships
end
You can encapsulate and separate it from the controller, keep thin controller fat model, beside that you can use autosave in order to auto save association.
class AgencyMembership < ApplicationRecord
belongs_to :agency_role, autosave: true
end
class Agency < ApplicationRecord
has_one :agency_membership, autosave: true
end
module BuildAgency
def build_with_role(attributes = {}, &block)
agency_role_name = attributes.delete(:role)
agency = build(attributes, &block) # association: user - membership - agency
# add role
agency_role = AgencyRole.find_by(name: agency_role_name)
agency.agency_membership.agency_role = agency_role # autosave
agency
end
end
class User < ApplicationRecord
has_many :agencies, autosave: true, :extend => BuildAgency
def build_agency(attributes)
new_agency = agencies.build_with_role(attributes)
# ...
new_agency
end
end
# controller
def create
if (params[:user][:create_agency])
#user.build_agency(name: params[:user][:agency_name], role: params[:user][:agency_role])
end
if #user.save! # it'll save agencies since we set `autosave`
end

belongs_to reject if condition not meets in Rails 5 ActiveRecord Conditional Associations

I have 2 models:
class PaymentRequest < ApplicationRecord
has_many :invoices, ->(request) { with_currency(request.amount_currency) }
end
and
class Invoice < ApplicationRecord
belongs_to :payment_request, optional: true
scope :with_currency, ->(currency) { where(amount_currency: currency) }
end
PaymentRequest may have only invoices of the same currency. And it meets the condition, while payment_request.invoices is called.
I have different currencies as following:
payment_request = PaymentRequest.create(id: 1, amount_currency: 'USD')
invoice = Invoice.create(amount_currency: 'GBP')
But, how to reject the following?
# no validation here
payment_request.invoices << invoice
# the payment_request_id is set to 1
invoice.payment_request_id #=> 1
One solution is adding the has_many :invoices, before_add: :check_currency and raise exception.
Is there a better solution to reject association?
I implemented the following solution for currency validation:
class PaymentRequest < ApplicationRecord
has_many :invoices, ->(request) { with_currency(request.amount_currency) },
before_add: :validate_invoice
monetize :amount_cents
private
def validate_invoice(invoice)
raise ActiveRecord::RecordInvalid if invoice.amount_currency != amount_currency
end
end

Saving Rails associations after creating User

I'm new to Rails and ActiveRecord and need some help. Basically, I have 4 models: User, Property, PropertyAccount, and AccountInvitation. Users and Properties have a many to many relationship via PropertyAccounts. AccountInvitations have a user's email and a property_id.
What I want to happen is that after a user registers on my app, his user account is automatically associated with some pre-created Properties. What I don't know how to do is write the query to get the Property objects from the AccountInvitations and save them to the User object. Please see def assign_properties for my pseudo code. Any help is welcome, thanks so much!
class User < ActiveRecord::Base
has_many :property_accounts
has_many :properties, through: :property_accounts
after_create :assign_properties
# Check to see if user has any pre-assigned properties, and if so assign them
def assign_properties
account_invitations = AccountInvitations.where(email: self.email)
if account_invitations.any?
account_invitations.each do |i|
properties += Property.find(i.property_id)
end
self.properties = properties
self.save
end
end
end
class AccountInvitation < ActiveRecord::Base
belongs_to :property
validates :property_id, presence: true
validates :email, uniqueness: {scope: :property_id}
end
class Property < ActiveRecord::Base
has_many :account_invitations
has_many :property_accounts
has_many :users, through: :property_accounts
end
class PropertyAccount < ActiveRecord::Base
belongs_to :property
belongs_to :user
end
Thanks to #wangthony , I looked at the includes method on http://apidock.com/rails/ActiveRecord/QueryMethods/includes and tweaked one of their examples in order to get this to work. Here's the solution:
def assign_property
self.properties = Property.includes(:account_invitations).where('account_invitations.email = ?', self.email).references(:account_invitations)
self.save
end
I believe you can do this:
user.properties = Property.includes(:account_invitations).where(email: user.email)
user.save

Ruby on Rails - Get polymorphic_id before_save

I have a two models:
Polymorphic
class Custo < ActiveRecord::Base
belongs_to :custavel, polymorphic: true
accepts_nested_attributes_for :custavel, allow_destroy: true
validates_presence_of :numero, :serie
validates_uniqueness_of :numero, scope: :serie
end
Another class
class Pagamento < ActiveRecord::Base
has_one :custo, as: :custavel
end
In general numero and serie are filled by user input, but only when the polymorphic is a Pagamento i need to fill the column numero with custavel_id.
Then, i tried it:
before_save :set_numero
def set_numero
return unless custavel_type == 'Pagamento'
write_attribute(:numero, custavel_id)
end
But it fails on validation because custavel_id doesn't exists yet. How to do it?
Modify the callback method
before_save :set_numero
def set_numero
write_attribute(:numero, custavel_id) if custavel_type == 'Pagamento'
end

Get Orders not Liked by logged in User Model in Rails 4

I have 3 Models: User, LikeOrder and Like. User has many LikeOrders. A User can like a LikeOrder only once. So I created Models as below:
class User < ActiveRecord::Base
has_many :like_orders
accepts_nested_attributes_for :like_orders
has_many :likes, dependent: :destroy
end
class LikeOrder < ActiveRecord::Base
belongs_to :user
has_many :likes, dependent: :destroy
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :like_order
end
And Migration for Like Model is:
class CreateLikes < ActiveRecord::Migration
def change
create_table :likes do |t|
t.references :user, index: true, foreign_key: true
t.references :like_order, index: true, foreign_key: true
t.timestamps null: false
end
end
end
So when User likes a LikeOrder I do it this way (using likes method directly) without any problem:
class User < ActiveRecord::Base
...
def like(order)
likes.create(like_order: order) if likes.where(like_order: order).count == 0
end
end
class UserController < ApplicationController
def like
#user = User.find(params[:user_id])
#order = LikeOrder.find(params[:order_id])
#user.like #order
end
end
There was no problem.
My Problem is I want to get Orders that:
their status are pending and the id is greater that from_id param and are not liked by logged in User.
class LikeOrder < ActiveRecord::Base
...
def self.not_likeds(user, from_id)
joins(:likes).where("like_orders.id > ? and like_orders.status = ?", from_id, 'pending')
end
end
I'm getting the greater than from_id and pending ones.
I made a Join(:likes) But don't know how to Get Not Liked ones from likes table? I've been trying for 6 hours with no luck.
UPDATED: (1 Oct 2015)
Finally I think this is working:
class LikeOrder < ActiveRecord::Base
...
def self.not_likeds(user, from_id)
not_liked = []
pending_form_id(from_id).each do |order|
not_liked << order if order.likes.where('user_id = ?', user.id).count == 0
end
not_liked
end
end
But there might be another way without any block, using where method. can anyone help?
UPDATED: (15 Dec 2015)
I found a better solution:
where("id > ? AND status = ?",from_id, 'pending').where("id not in (SELECT like_order_id from likes WHERE user_id = ?)",user.id).where("user_id != ?",user.id).limit(limit)
I want to get Orders that: their status are pending and the id is
greater that from_id param and are not liked by logged in User.
#app/models/order.rb
class Order < ActiveRecord::Base
def not_liked user, from_id
joins(:likes).where(status: "pending", id > from_id).not(likes: {user_id: user.id})
end
end
This would allow you to call:
#order = Order.not_liked current_user, "5"
Not tested.
Your structure really confused me; why don't you just have a Like model instead of LikeOrder...
#app/models/like.rb
class Like < ActiveRecord::Base
#you could include an "order" attribute here
belongs_to :user
belongs_to :order
validates :user, uniqueness: { scope: :order, message: "Only one Order like per user!" }
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :likes
has_many :favourites, through: :likes, class: "Order", foreign_key: "order_id"
end
#app/models/order.rb
class Order < ActiveRecord::Base
has_many :likes
has_many :fans, through: :likes, class: "User", foreign_key: "user_id"
end
This would allow you to call...
#user = User.find params[:id]
#user.favourites => all the orders they liked
Hey you can try in this way
def self.not_likeds(user, from_id)
joins(:likes).where("like_orders.id > ? and like_orders.status = ? and likes.user_id not in (?)", from_id, 'pending',current_user.id)
end
UPDATE
In this case, I'm guessing that user is current_user so try to do this:
def self.not_likeds(user, from_id)
joins(:user, :likes).where("like_orders.id > ? and like_orders.status = ? and likes.id NOT IN (?)", from_id, 'pending', user.like_ids)
end
the solution for me was:
class LikeOrder < ActiveRecord::Base
...
def self.not_liked(user, from_id=0, limit=20)
where("id > ? AND status = ?",from_id, 'pending').where("id not in (SELECT like_order_id from likes WHERE user_id = ?)",user.id).where("user_id != ?",user.id).limit(limit)
end
end

Resources