Active Record Scopes Tutorial - Rails - Booking example
Tags: webdev, rails, ruby, tutorial
Published: 2023-10-04 06:31:07

At [Le Wagon](https://lewagon.com/tokyo), our students spend 1 week building a Ruby on Rails Airbnb clone. We normally end up with a schema relatively close to this:
![DB Schema example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpebhk277l52h58jpm96.png)

When we get to the bookings page, it can be tricky how to retrieve all of the relevant bookings for a particular user. An empty bookings page example (with [tabs](https://dev.to/dmbf29/building-functional-tabs-with-style-pdb))
![Bookings page example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gmvsf9cb5uk2qgjh8foz.png)

If we use `Booking.all`, this will give us all of the bookings for every user in the entire application which is obviously not what we want. The simple version of this would just to get all of the bookings for the person logged in (if you're using [Devise](https://github.com/heartcombo/devise)) `current_user.bookings`

However, you may have noticed that on our `booking`, we have a `start_date`, `end_date`, and `status`. So our bookings become a little bit more complicated. Do we want all of our `pending` bookings? Bookings that are in the future? Bookings that are currently happening? Well when we use `current_user.bookings`, it's giving us everything.

To be able to sort through all the different types of bookings that we have, we can use [Active Record scopes](https://guides.rubyonrails.org/active_record_querying.html#scopes). If you haven't dealt with scopes before, the syntax might feel a bit strange. Here is one example:
```
# booking.rb (model)
scope :past, -> { where('end_date < ?', Date.today) }
```
This gives us access to method `.past` now. So in our controller, we could use `Booking.past` if we want past bookings from all users. But in this particular example, now we can access all the past bookings for a particular user: `current_user.bookings.past`

So given this one example of a scope, we could now go crazy with lot of different types of bookings (depending on how we're using them in the view). Our model might end up looks like this:
```
class Booking < ApplicationRecord
  belongs_to :user
  belongs_to :offer
  validates :start_date, presence: true
  validates :end_date, presence: true
  enum status: { pending: 0, accepted: 1, rejected: 2 }

  scope :past, -> { where('end_date < ?', Date.today) }
  scope :future, -> { where('start_date > ?', Date.today) }
  scope :active, -> { where('start_date < ? AND end_date > ?', Date.today, Date.today) }
  scope :today, -> { accepted.where(date: Date.today) }
  scope :upcoming, -> { accepted.future }
  scope :need_response, -> { pending.future }
  scope :expired, -> { pending.past }
  scope :completed, -> { accepted.past }
  scope :not_rejected, -> { where.not(status: :rejected) }

  def number_of_days
    (start_date - end_date).to_i 
  end

  def price
    offer.price * number_of_days
  end
end
```

Now thinking about our how to use this, we can load all of the user's bookings in the bookings controller.
```
def index
  @bookings = current_user.bookings
end
```
Then in our view we can display whatever type of bookings that we decided, using our newly made scopes!
```
<%= render 'cards', bookings: @bookings.today %>
<%= render 'cards', bookings: @bookings.future %>
<%= render 'cards', bookings: @bookings.active %>
<%= render 'cards', bookings: @bookings.need_response %>
<%= render 'cards', bookings: @bookings.past %>
<!-- etc. -->
```