質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.78%

Rubyコードの書き方をスマートにしたい

受付中

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,485

score 1017

前提・実現したいこと

以下のコードをスマートにしたいのですが、何かいい方法はないでしょうか?

該当のソースコード

@user = params[:id]

# 今月の月初めと月末までの投稿数を取得
this_month = Time.now.at_beginning_of_month
to_this_month = this_month + 1.month
@this_month = Post.where(user_id: @user.id, created_at: this_month...to_this_month).count

# 先月の投稿数
last_month = this_month.last_month
to_last_month = last_month + 1.month
@last_month = Post.where(user_id: @user.id, created_at: last_month...to_last_month).count

# 先々月の投稿数
month_before_last = this_month.month_ago(2)
to_month_before_last = month_before_last + 1.month
@last_month = Post.where(user_id: @user.id, created_at: last_month...to_last_month).count

もっとすっきりかけると思うのですが、いい案が思いつかず、お力を貸していただきたいです

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • tkow

    2016/09/29 01:07

    上記のコードは月末日の投稿を含めないコードになっていますが正しいですか?

    キャンセル

  • kenny_sayama

    2016/09/29 08:56

    ご指摘の通りでしたので、修正しました!

    キャンセル

回答 3

0

一応実装の時に考えることは
rangeオブジェクトは
ご存知かもしれませんが,

.2個で等号含む
.3個で不等号のみ

を考え,実際にサーバにどのようなqueryが投げられているか確認すると,無駄を削減できると思います。
また,共通の処理を関数に切り出し,いいかもしれません。私は以下のように特定の月次ではなく任意の区間を指定できるような実装にして汎用性を高くします。
もし月次の定義がずれていた場合は,適宣補完していただけると幸いです。

def main_action
  @this_month,@last_month,@before_last_month = fetch_during_month_data
end

private

def fetch_during_month_data(start_month=Date.today.month-2,end_month=Date.today.month,year=Date.today.year)
  end_month.downto(start_month).map do |target_month|
    current_month = Date.new(year,target_month)
    fetch_monthly_data(current_month...current_month.next_month)
  end
end

def fetch_monthly_data(range)
  User.where(user_id: @user.id, created_at:range).count
end

もしくはfetch_monthly_data(range)はまとめてしまって

def main_action
  @this_month,@last_month,@before_last_month = fetch_during_month_data
end

private

def fetch_during_month_data(start_month=Date.today.month-2,end_month=Date.today.month,year=Date.today.year)
  end_month.downto(start_month).map do |target_month|
    current_month = Date.new(year,target_month)
    range = current_month...current_month.next_month
    User.where(user_id: @user.id, created_at:range).count
  end
end


とします。

追記

上記コードは,年次ごとにはわかりやすいのですが,年をまたげないので年をまたぐ必要がある場合にはfetch_during_month_dataを以下のように修正します。

def fetch_during_month_data(end_month=Date.today.month,back_months = 2 ,year=Date.today.year)
   current_month = Date.new(year,end_month)
  (0..back_months).map do |back|
    current_month = current_month.months_ago(back)
    range = current_month...current_month.next_month
    User.where(user_id: @user.id, created_at:range).count
  end
end

期間指定したい場合は,

def main_action
  @this_month,@last_month,@before_last_month = fetch_during_month_data
end

private

def fetch_during_month_data(start_month=Date.today.months_ago(2),end_month=Date.today)
  back_months = (end_month.year - start_month.year)*12 - start_month.month + end_month.month
   (0..back_months).map do |back|
    current_month = current_month.months_ago(back)
    range = current_month...current_month.next_month
    User.where(user_id: @user.id, created_at:range).count
  end
end

とすることでできます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

before_action :current_user, only: [:index]
before_action :last_3_months_post_count, only: [:index]

private

def current_user
  @user = params[:id]
end

def last_3_months_post_count
  # * 今月の月初めと月末までの投稿数を取得
  # * 先月の投稿数
  # * 先々月の投稿数
  @monthly_post_counts =
    [
      monthly_post_count((Time.current)...(1.month.from_now)),
      monthly_post_count((1.month.ago)..(2.month.ago)),
      monthly_post_count((2.month.ago)..(3.month.ago))
    ]
end

def monthly_post_count(duration)
  Post.where(user_id: @user.id, created_at: duration).count
end
  • before_action を書くことで、アクション本体の記述量を削減
  • モデルへの問い合わせ部分は処理が共通しているので、メソッドに書き出す

View 側でどのように表示しているのかが気になりますが、似たような場所に書き出すのであれば、上のように配列にしてしまってもよいかもしれません

「3ヶ月毎」と探す範囲が固定であれば、この処理自体をモデルのメソッドや、 scope メソッドとして移行してしまってもよいかもしれません

@monthly_post_counts = Post.last_3_months_post_count

(※雰囲気をお伝えしたかったので、実際の動作についてはご容赦ください)

Rails は数多くの様々なメソッドを用意してくれています
(私含め)Rails の機能を活用した書き方をしていきたいですね

簡単に調べてみましたが、何か参考になれば幸いです

 Links

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

やりたいことは”月毎の投稿数を取得する”ということですよね。
どれも期間としては1ヶ月ごとで、+ 1.month は共通になっていますね。
なのでそれもメソッドに入れて、post_count_per_monthとしたらどうでしょうか?
月毎の投稿数を取得するという意図が伝わりやすくなるかと思います。

@user = User.find(params[:id])

# 今月の月初めと月末までの投稿数を取得
this_month = Time.now.at_beginning_of_month
@this_month = post_count_per_month(@user.id, this_month)

# 先月の投稿数
@last_month = post_count_per_month(@user.id, this_month.last_month)

# 先々月の投稿数
@last_month = post_count_per_month(@user.id, this_month.month_ago(2))
def post_count_per_month(user_id, month)
  Post.where(user_id: user_id, created_at: month...(month + 1.month)).count
end

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.78%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る