今日はrailsでスパゲッティコードを便利メソッドでキレイにしたよという話です。
環境
- ruby 2.6.6
- rails 5.2.3
参考
【知らずに損した】ActiveSupportの期間指定メソッドall_day, all_week, all_month, all_year
Active Support コア拡張機能
今週の日曜日〜土曜日の期間でデータを取得したい時
使うメソッドとしてはbeginning_of_week
とend_of_week
を使って今週の日曜日と今週の土曜日を取得してみましょう。
# 今週日曜日(日曜始まり)を取得するメソッド
irb(main):001:0> Date.today.beginning_of_week
=> 2020-12-06 Dec 2020
# これだと月曜始まりで取得してしまうので-1します
irb(main):002:0> Date.today.beginning_of_week - 1
=> 2020-12-07 Dec 2020
# 日曜日が取れました!
土曜日を取得しましょう。
日付を範囲指定する際に終わりの日にちの何時までを範囲指定する必要がありました. Time.now
を使って土曜日の23:59:59までを指定する範囲に含めるように修正しました. これで0:00:00 ~ 23:59:59のその日1日までを指定した日付の範囲内に含める事ができます。
# 今週土曜日を取得するメソッド
irb(main):003:0> Time.now.end_of_week - 1
=> 2020-12-12 23:59:58 +0900
# 土曜日が取れました!
次に日曜日〜土曜日までのデータを取得してみる
今回はStudyモデルというTBLにid
, time
, total
, user_id
, created_at
カラムを持つ学習時間を記録するTBLを例に考えます。
カラム | タイプ |
---|---|
id | digint |
time | float |
total | float |
user_id | integer |
created_at | datetime |
このテーブルに以下のデータが入っていると想定します。
irb(main):002:0> Study.all
Study Load (0.6ms) SELECT `studies`.* FROM `studies` LIMIT 11
=> #<ActiveRecord::Relation [#<Study id: 2, time: 0.5, total: 0.5, user_id: nil, created_at: "2020-12-05 11:09:38", updated_at: "2020-12-05 11:09:38">, #<Study id: 3, time: 1.5, total: 2.0, user_id: nil, created_at: "2020-12-07 11:09:42", updated_at: "2020-12-07 11:09:42">, #<Study id: 4, time: 2.0, total: 4.0, user_id: nil, created_at: "2020-12-07 11:09:49", updated_at: "2020-12-07 11:09:49">, #<Study id: 5, time: 0.5, total: 4.5, user_id: nil, created_at: "2020-12-07 11:26:38", updated_at: "2020-12-07 11:26:38">, #<Study id: 6, time: 1.0, total: 5.5, user_id: nil, created_at: "2020-12-07 11:27:31", updated_at: "2020-12-07 11:27:31">, #<Study id: 7, time: 0.5, total: 6.0, user_id: nil, created_at: "2020-12-07 23:16:04", updated_at: "2020-12-07 23:16:04">, #<Study id: 8, time: 1.5, total: 7.5, user_id: nil, created_at: "2020-12-07 23:18:49", updated_at: "2020-12-07 23:18:49">]>
id2~8までの7件のレコードが存在しています(id1が歯抜けしているのは無視して下さいw)
このデータがある状態で先程のメソッドを使って期間を今週に絞ってデータを取得してみたいと思います。
irb(main):003:0> Study.where(created_at: Date.today.beginning_of_week - 1..Time.now.end_of_week - 1).sum(:time)
(4.1ms) SELECT SUM(`studies`.`time`) FROM `studies` WHERE `studies`.`created_at` BETWEEN '2020-12-06' AND '2020-12-12 23:59:58'
=> 7.0
whereでcreated_atが今週日曜日(12/6)〜今週土曜日(12/12)までの7日間のデータを取得して、sumメソッドでtimeカラムの合計を計算します。
スパゲッティコードを書く
まず私が最初に書いたスパゲッティコードで書きますw
# studies_controller.rb
~省略~
def index
@this_week_total_time = Study.where(created_at: Date.today.beginning_of_week - 1..Time.now.end_of_week - 1).sum(:time)
end
~省略~
controllerにこんな書いてたらどんどんfatになっていく予感しかしないですし、汚いコードですね。
リファクタリング1
モデルメソッドを使ってcontrollerの見通しをよくします。
# studies_controller.rb
~省略~
def index
@this_week_total_time = Study.this_week_total
end
~省略~
# models / study.rb
~省略~
def this_week_total
self.where(created_at: date.today.beginning_of_week - 1..Time.now.end_of_week - 1).sum(:time)
end
~省略~
ロジック部分をモデルに移行したので、コントローラーがスッキリしました!
本題railsの便利メソッドを使う
コントローラーはキレイになりましたが、モデルメソッドが冗長です。
そこでrailsが用意してくれているall_week
メソッドを使ってリファクタリングしましょう。
# models / study.rb
~省略~
def this_week_total
@today = Time.now
+@range = @today.all_week(:sunday) ← 追加
self.where(created_at: @range).sum(:time)
end
~省略~
all_weekは引数を取ることで任意の曜日始まりにできます.今回は日曜始まりにしたいので(:sunday)
を渡します。
最初のコードに比べると大分キレイなコードになりました!
以前のコードだとbeginning_of_week - 1
なので、「週の始まりの日付を取得しているけど、-1しているから~えーっと、結局何曜日なの!?」って何ヶ月後の自分が見たら思うでしょうw
リファクタリングした@range = @today.all_week(:sunday)
であれば「日曜始まりで期間が今週なんだな」と直感的に分かるようになりました。
念の為、確認してみましょう。
irb(main):006:0> @today = Time.now
=> 2020-12-09 11:25:43 +0900
irb(main):007:0> @range = @today.all_week(:sunday)
=> 2020-12-06 00:00:00 +0900..2020-12-12 23:59:59 +0900
irb(main):008:0> Study.where(created_at: @range).sum(:time)
(0.5ms) SELECT SUM(`studies`.`time`) FROM `studies` WHERE `studies`.`created_at` BETWEEN '2020-12-06 00:00:00' AND '2020-12-12 23:59:59'
=> 7.0
ちゃんと指定した期間のデータを取得する事ができました!
公式リファレンスに詳しく書いているので、こちらも合わせて読んでみて下さい!!