Peege151
Peege151

Reputation: 1560

Validates_Overlap: 'Conditional Scope'?

I've asked a question about scopes with validates overlap here, but this question takes the code one step further, and there's a different issue that involves a conditional scope (may be called something else)

Brief synopsis of my app (also described in other question). I am creating an app for booking, and using the validates overlap gem, I want to make sure there isn't an overlap for booking for the same room. There are two rooms. They can both be booked at the same time.

So far solution to other question, and current code is as follows:

class Booking < ActiveRecord::Base
  scope :confirmed_scope, -> {confirmed: true}  
  validates :start_time, :end_time, :overlap => {
    :exclude_edges => ["starts_at", "ends_at"],
    :scope => "studio_id",
    :query_options => {:confirmed_scope => nil}
    }, on: :update
end

Thanks to Robin Bortlik for helping me figure out the scopes to this length.

As of this code, the rooms can't be booked at the same time.

One problem still exists though, and that workers and clients can still book both rooms at once, which isn't ideal (because, obviously, people can't be in two places at once). A client and a worker wouldn't book themselves twice on purpose, but I feel like it would be good to have it in validation to make sure a worker doesn't accidentally book himself with two different clients (in the two different rooms, this is possible)

Since there is no check with worker_id or client_id above, the attempted booking is only allowed to be booked if that room is available. It doesn't care who is doing the booking.

I need to add a 'conditional scope' that checks to see if the client_id or worker_id that is being booked doesn't already have a booking in the OTHER room at the same time.

I tried the following code:

:scope => ["studio_id","worker_id","client_id"], {...}

But this will only flag a validation now if there is a booking in THAT studio with THAT worker with THAT client, making duplicate bookings of THAT studio allowed.

Ideally, the booking will only go through if the room is available, and if so - check to make sure the worker of the attempted booking is currently booked in the other room. How would I alter my scopes to check the above conditions?

Hopefully my problem is clear, please let me know if anything is unclear or if more information is needed. Update: I think the solution provided by Robin is nearly what I need, but keeping the studio_id where it is and including the worker and client id in a separate scope.

Code like this gets me an argument error 1 for 2:

scope :includes_studio_worker_client, -> (client_id, engineer_id) { where('engineer_id = ? OR client_id = ?', engineer_id, client_id) }
validates :start_time, :end_time, :overlap => {
            :scope => "studio_id",
            :query_options => {
                :confirmed_scope => nil,
                :includes_studio_worker_client => 
                    proc{ |booking| [booking.engineer_id, booking.client_id] }
                },
            :exclude_edges => ["start_time", "end_time"]
        }

Upvotes: 0

Views: 470

Answers (1)

Robin Bortl&#237;k
Robin Bortl&#237;k

Reputation: 750

UPDATE: I think , I now understand your problem. What do you need is not search for entries which overlap in time and have same "studio_id" AND "worker_id" AND "client_id". But you need search for entries which overlap in time and have same "studio_id" OR "worker_id" OR "client_id". Is it right?

Everything what you will pass as a :scope option will be concatenated with "AND". So you need to write validation for each attribute.

The result code will look like this:

class Booking < ActiveRecord::Base
    scope :confirmed, -> {where(confirmed: true)}

    validates :starts_at, :ends_at, :overlap => {
      :exclude_edges => ["starts_at", "ends_at"],
      :query_options => {:confirmed => nil},
      :scope => 'client_id',
      :message_content => 'is already taken in this time',
      :message_title => 'Client '
    }, on: :update

    validates :starts_at, :ends_at, :overlap => {
      :exclude_edges => ["starts_at", "ends_at"],
      :query_options => {:confirmed => nil},
      :scope => 'worker_id',
      :message_content => 'is already taken in this time',
      :message_title => 'Worker '
    }, on: :update

    validates :starts_at, :ends_at, :overlap => {
      :exclude_edges => ["starts_at", "ends_at"],
      :query_options => {:confirmed => nil},
      :scope => 'studio_id',
      :message_content => 'is already taken in this time',
      :message_title => 'Studio '
    }, on: :update
end

BTW.. here is how to write scopes in rails 4 scopes with lambda and arguments in Rails 4 style?

Upvotes: 2

Related Questions