Railsとsimple calendarを使って予約システムを実装！datetimeにバリデーションをつけよう！

# 実装したい内容
gemのsimple calendarを使ってカレンダーに予約の予定を入れていく
rubyやRailsの基礎知識があること前提で話を進めていきます

# 開発環境
Rails 6.0.3
ruby 2.7.0

# reservationsテーブルの追加
今回はミニアプリを作る想定をしreservationsテーブルを作成しname(string)とstart_time(datetime型)のカラムを追加していきます。

```
rails new calendar_app
```
まずはお決まりのrails new

```
bundle
```
bundle installも癖づけて行いましょう。

```ruby:Gemfile
gem 'simple_calendar', '~> 2.0'
```
```
bundle
```
gemfileにsimple_calendarを追加したら再びbundle install

```
rails g scaffold reservationc name start_time:datetime
```
今回はスキャフォルドを使って簡単にアプリを実装していきたいと思います。

```
rails db:migrate
```
お決まりのrails db:migrateも忘れないように。

![スクリーンショット 2020-11-02 23.30.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/b3459697-2d36-edc4-91c0-2f67499d6dde.png)

rails sで　http://localhost:3000/　にアクセス！
しっかり立ち上がっていることが確認できたら

```ruby:routes.rb
root 'reservations#index'
```
ルートパスを予約の一覧ページに変更しておきましょう。
![スクリーンショット 2020-11-02 23.33.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/7dfbcf6a-5e5d-bf90-0d5a-91f472643e36.png)

続いてsimple_calendarのviewを整えていきます

```
rails g simple_calendar:views
create  app/views/simple_calendar
      create  app/views/simple_calendar/_calendar.html.erb
      create  app/views/simple_calendar/_month_calendar.html.erb
      create  app/views/simple_calendar/_week_calendar.html.erb
```

ファイルが３つ作成されたのですが今回はmouthを使用していきたいと思います。

```ruby:application.css
 *= require simple_calendar
# simple_calendarのcssを読み込む
```

```ruby:reservations.index.html.erb

<p id="notice"><%= notice %></p>

<h1>Reservations</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Start time</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @reservations.each do |reservation| %>
      <tr>
        <td><%= reservation.name %></td>
        <td><%= reservation.start_time %></td>
        <td><%= link_to 'Show', reservation %></td>
        <td><%= link_to 'Edit', edit_reservation_path(reservation) %></td>
        <td><%= link_to 'Destroy', reservation, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>
</table>

<br>

<!-- ここから --!>
<%= month_calendar events: @reservations do |date, reservations| %>
  <%= date %>

  <% reservations.each do |reservation| %>
    <div>
      <%= reservation.start_time.hour %>:<%= reservation.start_time.min %>
      <%= reservation.name %>
    </div>
  <% end %>
<% end %>
<!-- ここまで追加 --!>

<%= link_to 'New Reservation', new_reservation_path %>
```

![スクリーンショット 2020-11-02 23.49.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/8dc0a2a9-43dc-cbad-81de-041497e7e13c.png)
カレンダーが表示できたかと思います！

![スクリーンショット 2020-11-02 23.52.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/825833/512047a1-e9bf-a022-8173-d738e689143e.png)

予約を追加するとしっかりとカレンダーにも反映されます♪

これだけではつまらないのでバリデーションを追加していきましょう。

# 特殊なバリデーションを自作する
今回は予約の数を制限したいので土日は予約不可。
過去の日付は選択できない
予約できる時間帯は13:15と19:15のみと言った物凄い特殊なバリデーションを組んでいきたいと思います！

```ruby:reservation.rb
class Reservation < ApplicationRecord
  validates :name, presence: true
  validate :date_before_start
  validate :start_time_not_sunday
  validate :start_time_not_saturday
  validate :time_only
  validates :start_time, uniqueness: { message: 'は他のユーザーが予約しています' }

  def date_before_start
    errors.add(:start_time, "は過去の日付を選択できません") if start_time < Date.today
  end

  def start_time_not_sunday
    errors.add(:start_time, "は日曜日を選択できません") if start_time.sunday?
  end

  def start_time_not_saturday
    errors.add(:start_time, "は土曜日を選択できません") if start_time.saturday?
  end
  
  def time_only
    if hour_only_1 && min_only
      true
    elsif hour_only_2 && min_only
      true
    else
      errors.add(:start_time, "(時間)は13:15もしくは19:15になります")
    end
  end

  def hour_only_19
    start_time.hour == 19
  end

  def hour_only_13
    start_time.hour == 13
  end

  def min_only
    start_time.min == 15
  end
end
```

date_before_timeは過去の日付を選択できないことを表しています。
start_time_not_subdayとstart_time_notsaturdayで土日を予約不可とします！
start_time.sunday?などでdate_time(型は)日曜？ですか？みたいな感じに表すことができます！

ここからが本番！！
13:15分と19:15分の2つの時間に予約を限定していきます.

start_time.hourで時間を指定でき、start_time.minで分数を指定できるので
これを13時、19時、15分と限定していきます！

time_onlyメソッドを作成し時間を限定します！！
またstart_time をuniqqueness: trueにすることにより、同じ時間でも予約が不可になるので1日に取れる最大の予約数は2件となります！！

# 番外編
今回はかなり特殊な形だったのでバリデーションを自作していきましたが、datetimeのバリデーションには
validates_timelinessといった便利なgem　があります！
https://github.com/adzap/validates_timeliness

```
# in Gemfile
gem 'validates_timeliness', '~> 5.0.0.beta1'

# Run bundler
$ bundle install
```
例えば予約時間を9時から5時までの間に限定したい場合は
validates_time :booked_at, between: ['9:00am', '5:00pm'] 
みたいな形に書くと簡単に実装できます！！

※このgemはrails５に対応したgemなのでrails6だとコンフリクトする可能性があります