Struct.newでWeeklyreportオブジェクトを作成してViewに渡し、週間テーブルを描画する

アプリ制作

実現したいこと

StatisticコントローラでDiaryの週間集計をする。

週間の各コンディションの値を下のようなテーブルにしたい。

そして配列にしてChart.jsに渡したい。

From: September 15

To: September 21

Image from Gyazo

どうすればこのテーブルを作れるのか?

  1. Statistic Pathのリンクをクリック
  2. Date.todayから週の初めの日、週の終わりの日を算出する
  3. Range(週の初め..週の終わり).to_a でlabel(グラフの横軸)が出来上がる。
  4. labelとカラムから各カラムの値の配列を返す
    1. 戻り値用の配列を初期化
    2. labelの配列とカラムを引数にとる
    3. labelから日付を1つずつ取り出し、その日のDiaryレコードを検索
    4. Diary.カラムで値を取り出して戻り値用の配列につっこむ
  5. labelと各カラムの配列を返す

StatisticControllerの実装

class StatisticsController < ApplicationController
  before_action :require_user_logged_in, :set_current_user

  WeeklyReport = Struct.new(
  :date_from,
  :date_to,
  :labels,
  :activity_array,
  :mood_array,
  :appetite_array
)

  def index
    activity = 'activity'
    @weekly_report = WeeklyReport.new.tap do |r|
                      r.date_from = Date.today.beginning_of_week
                      r.date_to   = Date.today.end_of_week
                      r.labels    = week_dates
                      r.activity_array = array_generator(week_dates, :activity)
                      r.mood_array = array_generator(week_dates, :mood)
                      r.appetite_array = array_generator(week_dates, :appetite)
                    end
  end

  private

  def week_dates
    today = Date.today
    (today.beginning_of_week..today.end_of_week).to_a
  end

  def array_generator(week_dates, colmun)
    week_dates.map do |date|
      current_user.diaries.where(diary_date: date).pluck(colmun)[0]
    end
  end
end

週間集計のテーブルを描画できた。 次は、これをChart.jsに渡してグラフにする。

Image from Gyazo

分からないところ

  • tapメソッド
# tapメソッドは、ブロック変数にレシーバ自身を代入してブロックを実行する。
# 戻り値はレシーバ自身

"hello".bytes.to_a.tap {|arr| p arr }

# "hello".bytes.to_a 
# => [104, 101, 108, 108, 111]
# このArrayがブロック引数(|arr|)に入る。 
# そして p arr が実行される。
# 戻り値はレシーバ自身なので、Arrayが返る。

"hello".bytes.to_a.tap {|arr| p arr }.collect {|byte| byte.to_s(16) }.tap {|arr| p arr }

# つまり...
[104, 101, 108, 108, 111].collect {|byte| byte.to_s(16) }.tap {|arr| p arr }

# mapのエイリアスなので、Arrayを一つずつブロック引数に入れて、ブロックを実行
[104, 101, 108, 108, 111].collect {|byte| byte.to_s(16) }
# => ["68", "65", "6c", "6c", "6f"]

# つまり...
["68", "65", "6c", "6c", "6f"].tap {|arr| p arr }

# レシーバがブロック引数に代入され、ブロックが実行される
p ["68", "65", "6c", "6c", "6f"]
# 戻り値はレシーバ自身 => ["68", "65", "6c", "6c", "6f"]

# 1行にできるのはわかったけど、逆に読みにくいのでは?笑

Rubyのレファレンスを読んで意味をつかんだ。

プロを目指す人のRuby入門

正規表現

  • 初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」
  • 初心者歓迎!手と目で覚える正規表現入門・その2「微妙な違いを許容しつつ置換しよう」
    • 最後の方の解決策2まで