Skip to content
This repository has been archived by the owner on Jul 24, 2020. It is now read-only.

Commit

Permalink
Refactor reservation scopes using Query Objects
Browse files Browse the repository at this point in the history
Resolves #1288
- add a set of POROs for Query Objects where appropriate
- move reservation scopes back into the model file
- update code where necessary
  • Loading branch information
orenyk committed Jan 12, 2016
1 parent 22807db commit 799a46c
Show file tree
Hide file tree
Showing 24 changed files with 166 additions and 116 deletions.
2 changes: 1 addition & 1 deletion app/controllers/categories_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def deactivate
redirect_to @category
elsif params[:deactivation_confirmed]
@category.equipment_models.each do |em|
Reservation.for_eq_model(em).finalized.each do |r|
Reservation.for_eq_model(em.id).finalized.each do |r|
r.archive(current_user, 'The category was deactivated.')
.save(validate: false)
end
Expand Down
13 changes: 8 additions & 5 deletions app/controllers/equipment_models_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def index
end

def show # rubocop:disable AbcSize, MethodLength
relevant_reservations = Reservation.active.for_eq_model(@equipment_model)
relevant_reservations =
Reservation.for_eq_model(@equipment_model.id).active
@associated_equipment_models =
@equipment_model.associated_equipment_models.sample(6)

Expand Down Expand Up @@ -59,9 +60,11 @@ def show # rubocop:disable AbcSize, MethodLength
@restricted = @equipment_model.model_restricted?(cart.reserver_id)

# For pending reservations table
@pending = relevant_reservations.reserved_in_date_range(Time.zone.today,
Time.zone.today +
8.days).reserved
@pending =
relevant_reservations.reserved_in_date_range(Time.zone.today,
Time.zone.today + 8.days)
# Future reservations using Query object
@future = @pending.future
end

def new
Expand Down Expand Up @@ -124,7 +127,7 @@ def deactivate
flash[:notice] = 'Deactivation cancelled.'
redirect_to @equipment_model
elsif params[:deactivation_confirmed]
Reservation.for_eq_model(@equipment_model).finalized.each do |r|
Reservation.for_eq_model(@equipment_model.id).finalized.each do |r|
r.archive(current_user, 'The equipment model was deactivated.')
.save(validate: false)
end
Expand Down
1 change: 0 additions & 1 deletion app/controllers/reservations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ def index
end

set_counts(source, time)

@reservations_set = time.send(@filter)
end

Expand Down
2 changes: 1 addition & 1 deletion app/decorators/category_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def make_deactivate_btn
# find reservations for models in the category in the next week
res = 0
object.equipment_models.each do |em|
res += Reservation.for_eq_model(em).finalized
res += Reservation.for_eq_model(em.id).finalized
.reserved_in_date_range(Time.zone.today - 1.day,
Time.zone.today + 7.days)
.count
Expand Down
2 changes: 1 addition & 1 deletion app/decorators/equipment_model_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class EquipmentModelDecorator < ApplicationDecorator
def make_deactivate_btn
unless object.deleted_at
# find reservations in the next week
res = Reservation.for_eq_model(object).finalized
res = Reservation.for_eq_model(object.id).finalized
.reserved_in_date_range(Time.zone.today - 1.day,
Time.zone.today + 7.days)
.count
Expand Down
2 changes: 1 addition & 1 deletion app/models/cart_validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def check_max_cat

def check_availability(model = EquipmentModel.find(items.keys.first),
quantity = 1,
source_res = Reservation.for_eq_model(self)
source_res = Reservation.for_eq_model(id)
.active.all)

# checks that the model is available for the given quantity given the
Expand Down
6 changes: 3 additions & 3 deletions app/models/equipment_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def num_available_from_source(start_date, due_date, source_reservations)
def num_available(start_date, due_date)
# for if you just want the number available, 1 query to get
# relevant reservations
relevant_reservations = Reservation.for_eq_model(self).finalized
relevant_reservations = Reservation.for_eq_model(id).finalized
.reserved_in_date_range(start_date, due_date)
.all
num_available_from_source(start_date, due_date, relevant_reservations)
Expand All @@ -207,14 +207,14 @@ def model_restricted?(reserver_id)
# Returns the number of overdue objects for a given model,
# as long as they have been checked out.
def number_overdue
Reservation.overdue.for_eq_model(self).size
Reservation.overdue.for_eq_model(id).size
end

def available_count(date)
# get the total number of items of this kind then subtract the total
# quantity currently reserved, checked-out, and overdue
total = equipment_items.active.count
reserved = Reservation.reserved_on_date(date).for_eq_model(self).count
reserved = Reservation.reserved_on_date(date).for_eq_model(id).count
total - reserved - number_overdue
end

Expand Down
57 changes: 55 additions & 2 deletions app/models/reservation.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# rubocop:disable ClassLength
# rubocop:disable Metrics/ClassLength, Rails/ScopeArgs
class Reservation < ActiveRecord::Base
include Linkable
include ReservationValidations
include ReservationScopes

belongs_to :equipment_model
belongs_to :equipment_item
Expand Down Expand Up @@ -37,6 +36,60 @@ class Reservation < ActiveRecord::Base
fined: (1 << 4), missed_email_sent: (1 << 5),
expired: (1 << 6) }

## Scopes ##
# general scopes
default_scope { order('start_date, due_date, reserver_id') }
scope :for_eq_model, ->(em_id) { where(equipment_model_id: em_id) }
scope :for_reserver, ->(reserver_id) { where(reserver_id: reserver_id) }

# flag scopes
scope :flagged, ->(flag) { where('flags & ? > 0', FLAGS[flag]) }
scope :not_flagged, ->(flag) { where('flags & ? = 0', FLAGS[flag]) }

# basic status scopes
scope :active, lambda {
where(status: Reservation.statuses.values_at(*%w(reserved checked_out)))
}
scope :finalized, lambda {
where.not(status: Reservation.statuses.values_at(*%w(denied requested)))
}
scope :active_or_requested, lambda {
where(status: Reservation.statuses.values_at(
*%w(requested reserved checked_out)))
}

# overdue / request scopes
scope :overdue, ->() { where(overdue: true).checked_out }
scope :returned_on_time, ->() { where(overdue: false).returned }
scope :returned_overdue, ->() { where(overdue: true).returned }
scope :approved_requests, ->() { flagged(:request).finalized }
scope :missed_requests, ->() { past_date(:start_date).requested }

# generalized date scopes (pass parameter as either a string or a symbol)
scope :past_date, ->(param) { where("#{param} < ?", Time.zone.today) }
scope :today_date, ->(param) { where(param.to_sym => Time.zone.today) }

# basic date scopes
scope :checked_out_today, ->() { today_date(:checked_out) }
scope :checked_out_previous, ->() { past_date(:checked_out) }
scope :due_today, ->() { today_date(:due_date) }

# more complex / task-specific scopes
scope :checkoutable, Reservations::CheckoutableQuery
scope :ends_on_days, Reservations::EndsOnDaysQuery
scope :future, Reservations::FutureQuery
scope :notes_unsent, Reservations::NotesUnsentQuery
scope :reserved_in_date_range, Reservations::ReservedInDateRangeQuery
scope :reserved_on_date, Reservations::ReservedOnDateQuery
scope :starts_on_days, Reservations::StartsOnDaysQuery
scope :upcoming, Reservations::UpcomingQuery

# join Scopes
scope :with_categories, lambda {
joins(:equipment_model)
.select('reservations.*, equipment_models.category_id as category_id')
}

## Class methods ##

def self.completed_procedures(procedures)
Expand Down
88 changes: 0 additions & 88 deletions app/models/reservation_scopes.rb

This file was deleted.

13 changes: 13 additions & 0 deletions app/queries/query_base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class QueryBase
class << self
delegate :call, to: :new
end

def initialize
fail NotImplementedError
end

def call
fail NotImplementedError
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/checkoutable_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class CheckoutableQuery < Reservations::ReservationsQueryBase
def call
@relation.where('start_date <= ?', Time.zone.today).reserved
end
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/ends_on_days_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class EndsOnDaysQuery < Reservations::ReservationsQueryBase
def call(start_date, end_date)
@relation.where(due_date: start_date..end_date)
end
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/future_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class FutureQuery < Reservations::ReservationsQueryBase
def call
@relation.where('start_date > ?', Time.zone.today.to_time).reserved
end
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/notes_unsent_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class NotesUnsentQuery < Reservations::ReservationsQueryBase
def call
@relation.where(notes_unsent: true).where.not(notes: nil)
end
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/reservations_query_base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class ReservationsQueryBase < QueryBase
def initialize(relation = Reservation.all)
@relation = relation
end
end
end
9 changes: 9 additions & 0 deletions app/queries/reservations/reserved_in_date_range_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Reservations
class ReservedInDateRangeQuery < Reservations::ReservationsQueryBase
def call(start_date, end_date)
@relation
.where('start_date <= ? and due_date >= ?', end_date, start_date)
.reserved
end
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/reserved_on_date_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class ReservedOnDateQuery < Reservations::ReservationsQueryBase
def call(date)
@relation.where('start_date <= ? and due_date >= ?', date, date).active
end
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/starts_on_days_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class StartsOnDaysQuery < Reservations::ReservationsQueryBase
def call(start_date, end_date)
@relation.where(start_date: start_date..end_date)
end
end
end
7 changes: 7 additions & 0 deletions app/queries/reservations/upcoming_query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Reservations
class UpcomingQuery < Reservations::ReservationsQueryBase
def call
@relation.unscoped.today_date(:start_date).reserved.order('reserver_id')
end
end
end
2 changes: 1 addition & 1 deletion app/views/equipment_models/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
<%= @pending.checkoutable.count %>
</div>
<div class="col-md-3">
<%= @pending.future.count %>
<%= @future.count %>
</div>
</div>
<% if @pending.length > 0 %>
Expand Down
2 changes: 1 addition & 1 deletion lib/tasks/email_checkin_reminder.rake
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ desc 'Send email reminder about upcoming check-ins'
task email_checkin_reminder: :environment do
if AppConfig.first.upcoming_checkin_email_active?
# get all reservations that end today and aren't already checked in
upcoming_reservations = Reservation.due_soon
upcoming_reservations = Reservation.due_today
Rails.logger.info "Found #{upcoming_reservations.size} reservations due "\
'for check-in. Sending reminder emails...'
upcoming_reservations.each do |upcoming_reservation|
Expand Down
4 changes: 2 additions & 2 deletions lib/tasks/email_notes_to_admins.rake
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ desc 'Send email to admins on reservations with notes'
task email_notes_to_admins: :environment do
# gets all reservations with notes and sends an email to the admin of the
# application, to alert them.
notes_reservations_out = Reservation.has_notes.checked_out.notes_unsent
notes_reservations_in = Reservation.has_notes.checked_in.notes_unsent
notes_reservations_out = Reservation.checked_out.notes_unsent
notes_reservations_in = Reservation.returned.notes_unsent
Rails.logger.info "Found #{notes_reservations_out.size} reservations "\
"checked out with notes and #{notes_reservations_in.size} "\
'reservations checked in with notes.'
Expand Down
Loading

0 comments on commit 799a46c

Please sign in to comment.