【Rails】今月を常に表示させるカレンダーの作り方

# 背景
- Railsで個人アプリケーションを作成していましたが、今月の予定表を作る必要がありました。そこで、コントローラー側でRubyを用いてプログラムを書けば、ビューで表示できるかと思ってので実装してみました。変数との関連性と計算に時間が掛かりましたが、そこまで難しいレベルではありませんので参考になればと思い、記事を書かせていただきました。外国人なので変な日本語が混じってるかもしれませんが、ご指摘いただければ幸いです。

# 目次
- Dateクラスについて
- timesメソッドについて
- コントローラーの実装
- ビューの実装
- 参考サイト

## Dateクラスについて
DateクラスはRubyの組み込みクラスであり、今回使用したのは以下のメソッドです。

- Date.today  
今日の日付を取得します。今年の場合、戻り値は2021-01-28で年月日の形式になります。

```ruby
now = Date.today
```
- Date.today.year
今年の値を取得します。戻り値は2021になります。

```ruby
this_year = Date.today.year
```

- Date.today.month
今月の値を取得します。戻り値は1になります。

```ruby
this_year = Date.today.month
```

- Date.today.wday
今日の日付を取得します。戻り値は0から6までの値です。

| 戻り値  | 意味  |
|:----------|:----------|
| 0    | 日曜日    |
| 1    | 月曜日   |
| 2   | 火曜日   |
| 3    | 水曜日   |
| 4    | 木曜日    |
| 5    | 金曜日    |
| 6    | 土曜日    |
```ruby
weekday = Date.today.wday
```
- Date.today.beginning_of_month  
今月の初日を取得します。戻り値は2021-01-01になります。

```ruby
first_day = Date.today.beginning_of_month  
```
## timesメソッドについて
- timesは同じ処理を回数分だけ繰り返す時に使うメソッドです。 
- 使い方  
 回数.times do |変数|

```ruby
10.times do |i|
  （処理）
end
```
## コントローラーの実装
以上のメソッドを踏まえて、コントローラーの実装を行いました。全体のコードは以下の通りです。

```ruby
class SdatesController < ApplicationController
  def index
    date_today
  end

  private

  def date_today
    @now = Date.today
    @wday_jan = ["月","火","水","木","金","土","日"]
    @this_year = @now.year
    @this_month = @now.month
    
    
    @first_day = @now.beginning_of_month
    @last_day = (@first_day + (7-@first_day.wday))
    @week_period = (@first_day..@last_day)

    @last_day2 = (@last_day + (7-@last_day.wday))
    @week_period2 = ((@last_day+1)..@last_day2)

    @last_day3 = (@last_day2 + (7-@last_day2.wday))
    @week_period3 = ((@last_day2+1)..@last_day3)

    @last_day4 = (@last_day3 + (7-@last_day3.wday))
    @week_period4 = ((@last_day3+1)..@last_day4)

    @last_day5 = (@last_day4 + (7-@last_day4.wday))
    @week_period5 = ((@last_day4+1)..@last_day5)

    @last_day6 = (@last_day5 + (7-@last_day5.wday))
    @week_period6 = ((@last_day5+1)..@last_day6)

  end
end
```
今回のカレンダー機能と関係あるところはprivate以下のコードです。
長いように見えますが、@last_day2以下は同じような処理の繰り返しです。

- まずは今日の日付を変数@nowに代入します。

```ruby
@now = Date.today
```
- wdayメソッドは戻り値が数字であるため、曜日として表示するための配列@wday_janを用意します。

```ruby
@wday_jan = ["月","火","水","木","金","土","日"]
```
例えば、@wday_jan[1]だった場合、"月"が出力されるようにします。 

- 今年の値を@this_yearに代入します。

```ruby
@this_year = @now.year
```
- 今月の値を@this_monthに代入します。

```ruby
 @this_month = @now.month
```

- 今月の初日を取得するために.beginning_of_monthメソッドを使います。値は@first_dayに代入します。

```ruby
 @this_month = @now.month
```
- 初日から最初の週（first weekend)の末日（日曜日）を計算し、  
 値を@last_dayに代入します。@last_dayは「初日の日付 + 日曜日までの日数」であり、  
 ここでポイントになるのはが「日曜日までの日数」の計算です。 
@wday_janには["月","火","水","木","金","土","日"]が格納されて"1"が"月"、"2"が"火"・・・"0”が"日"などのようにマッチングしています。例えば、初日が水曜日だった場合、@wday_janには"水"="3"です。水曜日から最初の週の末日（日曜日）までの日数は"7-3"となります。この関係を数式で表現すると「@wday_janの配列の数-初日の曜日数（3)」になります。代入した変数で式を表すと以下になります。

```ruby
@last_day = (@first_day + (7-@first_day.wday))
```
- 次は初日から最初の週の末日までの日付らをRangeオブジェクト@week_periodに代入します。 

 ```ruby
 @week_period = (@first_day..@last_day)
```
- 同じように第２週目、第３週目....の月曜~日曜までのRangeを求めます。繰り返しになるので説明は割愛します。

 ```ruby
@last_day2 = (@last_day + (7-@last_day.wday))
@week_period2 = ((@last_day+1)..@last_day2)
...以下、繰り返し部分を省略
```
- index.html.erbテンプレートで表示させるために、privateで定義した関数date_todayを記述します。
 
 ```ruby
    (上記、コードは省略）
def index
    date_today
end
```
## ビューの実装
コントローラーで定義したメソッドをindex.html.erbテンプレートに表示するための記述をします。

```ruby
<div class="schedule_title">
    <h1>Schedule Board</h1>
</div>

<div id="todays_date">
    <%= @this_year %>年<%= @this_month %>月
</div>

<table border="1" id="sdate_table">
    <thead>
        <tr>
            <th class="sdate_name">月</th>
            <th class="sdate_name">火</th>
            <th class="sdate_name">水</th>
            <th class="sdate_name">木</th>
            <th class="sdate_name">金</th>
            <th class="sdate_name">土</th>
            <th class="sdate_name">日</th>
        </tr>
    </thead>
    <tbody>
        <tr>
        <% 7.times do |x| %>
            <td>
            <% @week_period.each do |period| %> 
            <% if x == (period -1).wday %>
            <%= period %> 
            <% end %>
            <% end %>
            </td>
        <% end %>
        </tr>

        <tr>
        <% 7.times do |x| %>
            <td>
            <% @week_period2.each do |period| %> 
            <% if x == (period -1).wday %>
            <%= period %> 
            <% end %>
            <% end %>
            </td>
        <% end %>
        </tr>

        <tr>
        <% 7.times do |x| %>
            <td>
            <% @week_period3.each do |period| %> 
            <% if x == (period -1).wday %>
            <%= period %> 
            <% end %>
            <% end %>
            </td>
        <% end %>
        </tr>

        <tr>
        <% 7.times do |x| %>
            <td>
            <% @week_period4.each do |period| %> 
            <% if x == (period -1).wday %>
            <%= period %> 
            <% end %>
            <% end %>
            </td>
        <% end %>
        </tr>

        <tr>
        <% 7.times do |x| %>
            <td>
            <% @week_period5.each do |period| %> 
            <% if x == (period -1).wday %>
            <%= period %> 
            <% end %>
            <% end %>
            </td>
        <% end %>
        </tr>

    
        <% if Random.rand(@week_period6).month == @this_month %>
        <% 7.times do |x| %>
            <td>
            <% @week_period6.each do |period| %> 
            <% if x == (period -1).wday %>
            <%= period %> 
            <% end %>
            <% end %>
            </td>
        <% end %>
        </tr>
        <% end %>
</tbody>
</table>
```

- timesメソッドを使い、テーブルの1行ごとに日付を表示させます。<td></td>の間を７回繰り返すことによって１週間の日付を出力します。

```ruby
 <% 7.times do |x| %>
            <td>
            <% @week_period.each do |period| %> 
            <% if x == (period -1).wday %>
            <%= period %> 
            <% end %>
            <% end %>
            </td>
        <% end %>
```
- if文を使い、繰り返しの変数xと@week_periodに格納されている日付らを変数periodに代入。日付から1を引いた後に、その日付の曜日（0~6の値)とxが一致した場合にのみ表示するようにします。つまり、変数xには1週間分を繰り返す機能と同時に月曜日〜日曜日まで（1,2,3,〜0)までindexの機能を持たせます。こうすることによってこのindexと一致した日付の値のみが表示されます。

- 最後の週には次月の日付が入ってしまったりする場合があるため、if文を使って分岐させます。

```ruby
<% if Random.rand(@week_period6).month == @this_month %>
```
Random.rand(変数)メソッドを使い、変数内の中からランダムで一つの値を取得します。
@week_period6には最後の週の日付らが入ってます。その日付らのどれかになります。(1月25日〜31日までのどれか）その値に.monthメソッドを使い、月を取得します（1月）。それと@this_month(実際の今月。例えば、今日が2月2日の場合は2月）が一致した場合にのみ、表示するようにします。

- index.html.erbの画像
![スクリーンショット 2021-01-28 2.09.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/06c61f34-dea0-6714-b99b-1b83557b81e3.png)

## 参考サイト
[https://www.javadrive.jp/ruby/for/index6.html](url)
[https://qiita.com/geshi/items/685a0814d77688fb1a55](url)
[https://qiita.com/youcune/items/f5371d449d2486c67357](url)