class Reservation < ActiveRecord::Base
validates :table, presence: true
validates :start, presence: true
validates :finish, presence: true
validate :checking_the_time, :uniqueness_reservation
scope :tables, ->(table) { where("'reservations'.'table' = ? ", table) }
def checking_the_time
if start >= finish
errors.add(:finish, "Invalid time range")
end
end
def uniqueness_reservation
unless Reservation.diapazone(start, finish).tables(table).where.not(id: id).empty?
errors.add(:booked_from, 'Invalid period.')
end
end
private
def self.diapazone(start, finish)
where("(start >= :s AND finish <= :f) OR (start <= :s AND finish >= :s)
OR (start <= :f AND finish >= :f)",
{s: start, f: finish})
end
end
How to refactor the validation checks on entering the order in the order that has already been created?
I will be grateful for the advice, if you can with an example.
Related
I have the follow code that is working in rails 5. Updagrate to 6 I get the error undefined method `__metadata'.
Here's the problematic code
*
def nature
self.__metadata.key.to_s.singularize.to_sym #
end
*
Have try to use method but it doesn't return what it does in rails 5 / mongoid. Mongoid version is '~> 7.0'
Complete class code
# Participant model class definition
class Participant
include Mongoid::Document
include Mongoid::Timestamps
include DryValidation
field :address
field :identifier
field :name
field :birthdate, type: Date
field :sex
field :profession
field :phone
field :email
field :ownership_percentage
field :contribution_amount
field :category
field :group
field :registered_on, type: Date
field :retired, type: Boolean
field :retired_on, type: Date
field :committee
# Callbacks
before_save :generate_identifier
# Relations
embedded_in :book, inverse_of: :shareholders
embedded_in :book, inverse_of: :directors
embedded_in :book, inverse_of: :employees
embedded_in :book, inverse_of: :committee_members
embeds_many :participant_files
accepts_nested_attributes_for :participant_files, allow_destroy: true
#Validations
validates :name, presence: true
validates :email, allow_blank: true, format: { with: /\A\S+#\S+\.\S+\z/i }
validates :registered_on, presence: true, non_existent_date: true
validates :birthdate, non_existent_date: true
validates :retired_on, non_existent_date: true
validate :registered_on_date
def self.options_for(field_name)
case field_name.to_sym
when :category then [nil, :founders, :actives, :participants]
when :sex then [nil, :male, :female]
when :group then [nil, :legal, :accounting, :human_resources, :consumer, :employee,
:management_and_administration, :communication_and_marketing,
:ethic_and_gouvernance, :other]
else []
end
end
def self.ordered
# This should be as simple as .order_by(:retired_on.desc, :registered_on.asc)
# but the registered_on parameters is never ordered correctly so I had to do this ugly thing :(
self.all.sort_by{ |a| [ (a.retired_on ? a.retired_on.strftime('%Y%m%d') : 0), (a.registered_on ? a.registered_on.strftime('%Y%m%d') : 0) ].join }
end
def self.ordered_by_name
participants = self.active.sort_by{ |p| p.name.downcase }
participants += self.inactive.sort_by{ |p| p.name.downcase }
participants
end
def self.active
now = Time.now.strftime('%Y%m%d')
self.all.select do |a|
if a.registered_on
if a.retired_on
a.retired_on.strftime('%Y%m%d') >= now && a.registered_on.strftime('%Y%m%d') <= now
else
a.registered_on.strftime('%Y%m%d') <= now
end
end
end
end
def self.inactive
now = Time.now.strftime('%Y%m%d')
self.all.select do|a|
(a.retired_on && a.retired_on.strftime('%Y%m%d') < now) ||
(a.registered_on && a.registered_on.strftime('%Y%m%d') > now)
end
end
def book
self._parent
end
def committee_member?
self.nature == :committee_member
end
def director?
self.nature == :director
end
def employee?
self.nature == :employee
end
def nature
self.__metadata.key.to_s.singularize.to_sym #
end
def active?
!retired?
end
def retired?
self.retired_on && self.retired_on <= Time.zone.today
end
def shareholder?
self.nature == :shareholder
end
def securities
self.book.transactions.any_of({from: self.id}, {to: self.id}).asc(:transacted_on)
end
def save_files
self.participant_files.each do |pf|
pf.save
end
delete_objects_without_file
end
def has_shares?
book.share_categories.each do |sc|
return true if total_shares(sc) > 0
end
false
end
def total_shares(share_category)
total = 0
securities.each do |s|
if s.share_category == share_category
if s.nature == 'issued' or (s.nature == 'transfered' and self.id.to_s == s.to.to_s)
total += s.quantity if s.quantity
elsif s.nature == 'repurchased' or (s.nature == 'transfered' and self.id.to_s == s.from.to_s)
total -= s.quantity if s.quantity
end
end
end
total
end
def share_class_percentage(sc)
book.share_class_quantity(sc) > 0 ? self.total_shares(sc)/book.share_class_quantity(sc).to_f*100 : 0
end
def acceptance_documents
self.book.documents.select{|document| document.participant_id == self.id && document.nature == 'dir_accept'}
end
def resignation_documents
self.book.documents.select{|document| document.participant_id == self.id && document.nature == 'dir_resig'}
end
private
def existing_identifier?
participant_type = self.__metadata.key.to_sym
identifiers = book.send(participant_type).map{ |p| p.identifier if p.id != self.id }.compact
identifiers.include? self.identifier
end
def generate_identifier
self.identifier = self.name.parameterize if self.identifier.blank?
i = 1
while existing_identifier?
self.identifier = "#{self.identifier}-#{i}"
i += 1
end
end
def registered_on_date
unless registered_on.nil? || retired_on.nil?
if registered_on > retired_on
errors.add(:registered_on, I18n.t("mongoid.errors.models.participant.attributes.registered_on.greater_than_retired_on"))
end
end
end
def delete_objects_without_file
self.participant_files.each do |pf|
pf.delete if pf.pdf_file.file.nil?
end
end
end```
This is my model -
class Leave < ActiveRecord::Base
belongs_to :staff
validates :staff, :leave_type, :start_date, :end_date, :number_of_days, :approved_by, presence: true
enum leave_type: {Medical: 0, Annual: 1, Urgent: 3, "Birth Leave": 4}
validate :check_leave, :if => "self.number_of_days.present?"
protected
def check_leave
if self.leave_type = 0
if ( self.number_of_days + LeaveAllocation.last.medical_leave_counter ) > LeaveAllocation.last.medical_leave
self.errors.add(:number_of_days, "Days exceeded the limit")
end
end
if self.leave_type = 1
if ( self.number_of_days + LeaveAllocation.last.annual_leave_counter ) > LeaveAllocation.last.annual_leave
self.errors.add(:number_of_days, "Days exceeded the limit")
end
end
end
end
When I try to run the validation, it only seems checks the first one "0" even if i change the selection to "1". Any help would be appreciated! Thanks
change to == in if condition.
self.leave_type == 0 and
self.leave_type == 1
I want users not to be able to cancel a booking just 2 hours before departure time.
I don't know where can I write this restriction. Should I write it in the model or in the controller application?
This is the pseudo-code I wrote so far:
class CancelValidator < ActiveMOdel::Validator
def validate(record)
if record.date_trip.to_time < Date.now + 2
record.errors[:base] << 'error'
end
end
end
EDIT: This is all the code, but it still lets me destroy the booking.. why?
class CountValidator < ActiveModel::Validator
def validate(record)
if (record.second || record.first)
record.errors[:base]<< ' error '
end
end
end
class DepartureValidator < ActiveModel::Validator
def validate(record)
if record.date_trip.to_date < Date.today
record.errors[:base]<< ' error '
end
end
end
class Reservation < ActiveRecord::Base
validates_with DepartureValidator
validates_with CountValidator
before_destroy :ensure_deletable
belongs_to :dep ,:class_name => 'Stop', :foreign_key => 'dep_id'
belongs_to :arr ,:class_name => 'Stop',:foreign_key => 'arr_id'
belongs_to :route
belongs_to :user
delegate :CountStop, :to => :route, prefix: true, :allow_nil => false
delegate :city ,:to => :arr, :allow_nil => false
delegate :city ,:to => :dep, :allow_nil => false
def division
return Reservation.select{|r| r.route_id == route_id && r.date_trip == date_trip }
end
def second
if (class_point == 2)
y=division.select{ |l| l.class_point == 2 }.count
if(y+1 > route.train.second_class_seats)
return true
end
end
return false
end
def first
if (class_point == 1)
y=division.select{ |l| l.class_point == 1 }.count
if(y+1 > route.train.prima_classe_seats)
return true
end
end
return false
end
def ensure_deletable
self.date_trip.to_time < Time.now + 2
end
end
Since you delete the value, you're going to want to add a callback instead.
The benefit of this is that, before you go and delete the entity, you can decide to stop it outright if it fails your condition.
Here's an example below. Caution: this is untested, but this should give you the gist of things.
class Booking < ActiveRecord::Base
before_destroy :ensure_deletable
private
def ensure_deletable
self.date_trip.to_time < Date.now + 2
end
end
Remember, from the documentation:
The method reference callbacks work by specifying a protected or private method available in the object...
How exactly do you do arithmetic operations in the controller?
I've tried this
def choose
rand_id = rand(Gif.count)
#gif1 = Gif.first(:conditions => [ "id >= ?", rand_id])
#gif2 = Gif.first(:conditions => [ "id >= ?", rand_id])
if #gif1.id == #gif2.id
#gif2 = Gif.first(:order => 'Random()')
end
total = #gif1.votes+#gif2.votes
number_one = #gif1.votes/total*100
number_two = #gif2.votes/total*100
#gif1.update_attribute(:votes, number_one)
#gif2.update_attribute(:votes, number_two)
end
class Gif < ActiveRecord::Base
before_save :default_agree_count
def default_agree_count
self.agree = 1
self.votes = 1
end
VALID_REGEX = /http:\/\/[\S]*\.gif$/
attr_accessible :link, :votes, :agree
acts_as_votable
validates :link, presence: true, format: {with: VALID_REGEX}, uniqueness: {case_sensitive: false}
end
However, it says that +, /, * are all unknown operators. I've also tried doing them within like such #gif1.agree = '#gif1.votes+1' with and without '. Any ideas?
Thanks!
I suppose you are using Acts As Votable gem.
Basically it works as follows:
#post = Post.new(:name => 'my post!')
#post.save
#post.liked_by #user
#post.votes.size # => 1
So try replacing .votes with .votes.size in your code.
E.g.:
total = #gif1.votes.size + #gif2.votes.size
Further to #ilyai's answer (which I +1'd) (I don't have much experience with the Acts As Votable gem), you can perform any calculations you want in your controllers
Here's some refactoring for you:
.first
def choose
Gif.update_votes
end
class Gif < ActiveRecord::Base
before_save :default_agree_count
def default_agree_count
self.agree = 1
self.votes = 1
end
def self.update_votes
rand_id = rand count #-> self.count?
gif = where("id >= ?", rand_id)
gif1 = gif[0]
gif2 = gif[1]
if gif1.id == gif2.id
gif2 = where(order: 'Random()').first
end
total = (gif1.votes) + (gif2.votes)
number_one = ((gif1.votes /total) * 100)
number_two = ((gif2.votes / total) * 100)
gif1.update_attribute(:votes, number_one)
gif2.update_attribute(:votes, number_two)
end
VALID_REGEX = /http:\/\/[\S]*\.gif$/
attr_accessible :link, :votes, :agree
acts_as_votable
validates :link, presence: true, format: {with: VALID_REGEX}, uniqueness: {case_sensitive: false}
end
I'm creating a rails app to manage events and I want to add validation to check if an event overlaps with another.
Here's my custom validation code to check for overlapping events:
class Event < ActiveRecord::Base
attr_accessible :full_date, :start_hour, :end_hour, :address
validate :no_overlapping_events
def no_overlapping_events
overlap = false
same_date = Event.where(:full_date => self.full_date).where("id != ?", self.id).where(:address => self.address)
same_date.each do |t|
if self.start_hour <= t.start_hour
if self.end_hour >= t.start_hour
overlap = true
break
end
end
end
if overlap == true
errors.add(:base, "Can't have overlapping events!")
end
end
end
I can still create events that are overlapping and they're still being saved. What am I doing wrong here?
Your custom validation method "ends" too soon. Try:
class Event < ActiveRecord::Base
attr_accessible :full_date, :start_hour, :end_hour, :address
validate :no_overlapping_events
def no_overlapping_events
overlap = false
same_date = Event.where(:full_date => self.full_date).where("id != ?", self.id).where(:address => self.address)
same_date.each do |t|
if self.start_hour <= t.start_hour
if self.end_hour >= t.start_hour
overlap = true
break
end
end
end
if overlap == true
errors.add(:base, "Can't have overlapping events!")
end
end
end
Also, as a logic error, what about the case when:
self.start_hour > t.start_hour and self.start_hour < t.end_hour