Skip to content

Instantly share code, notes, and snippets.

@rsierra
Created September 23, 2011 18:35
Show Gist options
  • Save rsierra/1238119 to your computer and use it in GitHub Desktop.
Save rsierra/1238119 to your computer and use it in GitHub Desktop.
Modelo de disponibilidades
class Availability < ActiveRecord::Base
DEFAULT_RENT = "General"
MINIMUM_MINUTES_DURATION = 90
MINIMUN_HOURS_MARGIN = 1
MINIMUN_MINUTES_SEGMENT = 15
belongs_to :rent
belongs_to :carpark
validates_associated :carpark
validates_presence_of :start_time, :end_time
#validate :validate_same_day, :validate_times_order, :validate_day_margin, :validate_duration, :validate_overlaps
validate :validate_same_day, :validate_times_order, :validate_duration, :validate_overlaps
before_validation :ensure_times_not_empty
after_validation :check_joins
after_save :ensure_has_rent
scope :starts_after, ->(date = DateTime.now) { where(['start_time >= ?', date]) }
scope :ends_before, ->(date = DateTime.now) { where(['end_time <= ?', date - MINIMUN_MINUTES_SEGMENT.minutes]) }
scope :in_same_month, ->(date = DateTime.now) { starts_after(date.beginning_of_month).ends_before(date.end_of_month) }
scope :in_same_day, ->(date = DateTime.now) { starts_after(date.beginning_of_day).ends_before(date.end_of_day) }
scope :starts_before, ->(date = DateTime.now) { where(['start_time <= ?', date]) }
scope :ends_after, ->(date = DateTime.now) { where(['end_time >= ?', date - MINIMUN_MINUTES_SEGMENT.minutes]) }
scope :contain, ->(date = DateTime.now) { starts_before(date).ends_after(date) }
#scope :starts_at, ->(date = DateTime.now) { where(:start_time => date) }
#scope :ends_at, ->(date = DateTime.now) { where(:end_time => date) }
scope :not_id, ->(id) { where(['id <> ?',id.to_i]) }
def duration_in_seconds
fixed_end_time - start_time
end
def duration_in_minutes
duration_in_seconds / 60
end
def duration_in_hours
duration_in_minutes / 60
end
def same_day?
start_time.to_date.eql? end_time.to_date
end
def contain?(date)
start_time <= date && fixed_end_time >= date
end
def start_overlap?
!carpark.availabilities.in_same_day(start_time).not_id(id).starts_before(start_time).ends_after(start_time).count.zero?
end
def end_overlap?
!carpark.availabilities.in_same_day(start_time).not_id(id).starts_before(fixed_end_time).ends_after(fixed_end_time).count.zero?
end
def inner_overlap?
!carpark.availabilities.in_same_day(start_time).not_id(id).starts_before(start_time).ends_after(fixed_end_time).count.zero?
end
def wrapper_overlap?
!carpark.availabilities.in_same_day(start_time).not_id(id).starts_after(start_time).ends_before(fixed_end_time).count.zero?
end
def overlap?
start_overlap? || end_overlap? || inner_overlap? || wrapper_overlap?
end
protected
def ensure_times_not_empty
self.start_time ||= Date.today
self.end_time ||= Date.today
end
def ensure_has_rent
if self.rent.blank?
self.rent = self.carpark.rents.find_or_create_by_name DEFAULT_RENT
self.save
end
end
def check_joins
case errors.count
when 1 then
if errors.has_key? :start_overlap
start_join
errors.clear
elsif errors.has_key? :end_overlap
end_join
errors.clear
end
when 2 then
if errors.keys.sort == [:start_overlap, :end_overlap].sort || errors.keys.sort == [:start_overlap, :duration].sort
start_join
errors.clear
elsif errors.keys.sort == [:duration, :end_overlap].sort
end_join
errors.clear
end
when 3 then
if errors.keys.sort == [:start_overlap, :duration, :end_overlap].sort
start_join
errors.clear
end
end
return true
end
def start_join
overlaps = carpark.availabilities.not_id(id).contain(start_time)
if overlaps.any?
overlap = overlaps.first
if self.new_record?
overlap.end_time = end_time
self.destroy
overlap.save
else
self.start_time = overlap.start_time
overlap.destroy
self.save
end
end
end
def end_join
overlaps = carpark.availabilities.not_id(id).contain(fixed_end_time)
if overlaps.any?
overlap = overlaps.first
if self.new_record?
overlap.start_time = start_time
self.destroy
overlap.save
else
self.end_time = overlap.end_time
overlap.destroy
self.save
end
end
end
def validate_same_day
errors.add(:end_time, :not_same_day) unless same_day?
end
def validate_times_order
errors.add(:start_time, :start_before_end) if start_time >= fixed_end_time
end
def validate_day_margin
errors.add(:start_time, :invalid_margin_start) if start_time < DateTime.now + MINIMUN_HOURS_MARGIN.hours
end
def validate_duration
# La internacionalización de este error debe ser manual porque no es sobre un atributo real
errors.add(:duration, I18n.t('activerecord.errors.models.availability.invalid_duration')) if duration_in_minutes < MINIMUM_MINUTES_DURATION
end
def validate_overlaps
errors.add(:start_overlap, I18n.t('activerecord.errors.models.availability.start_overlap')) if start_overlap?
errors.add(:end_overlap, I18n.t('activerecord.errors.models.availability.end_overlap')) if end_overlap?
errors.add(:inner_overlap, I18n.t('activerecord.errors.models.availability.inner_overlap')) if inner_overlap?
errors.add(:wrapper_overlap, I18n.t('activerecord.errors.models.availability.wrapper_overlap')) if wrapper_overlap?
end
private
def fixed_end_time
end_time + MINIMUN_MINUTES_SEGMENT.minutes
end
end
# PUT /carparks/1
# PUT /carparks/1.xml
def update
@carpark = Carpark.find(params[:id])
respond_to do |format|
if @carpark.update_attributes(params[:carpark])
format.html { redirect_to(@carpark, :notice => t('text.flash.model_updated_ok', :name => Carpark.model_name.human)) }
format.xml { head :ok }
format.js { render :text => "OK" }
else
format.html { render :action => "edit" }
format.xml { render :xml => @carpark.errors, :status => :unprocessable_entity }
format.js { render :text => @carpark.errors.full_messages, :status => :unprocessable_entity }
end
end
end
@rsierra
Copy link
Author

rsierra commented Sep 23, 2011

Bueno no se si se verá muy claras las validaciones del modelo :-S
Desde el update de los carparks, entran los nested attributes de las availabilities generadas desde el calendario de la vista.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment