groupしたら「Hash can't be coerced into Fixnum」が出たけど、pしたら大丈夫だったのはなぜ?


総件数が表示される一覧画面を作っていたところ、エラーが発生。
総件数の部分はhelperで定義されていて、今まで特に問題なく動いていたのに・・・

  def page_navi(objects, class_name = 'per_page')
   return if objects.nil?
   html  = objects.total_count.to_s
   html += '件中 '
   html += "#{ objects.limit_value * objects.offset_value / objects.limit_value + 1 }"
   html += '~'
   html += "#{ objects.limit_value * objects.offset_value / objects.limit_value + objects.size }"
   html += '件を表示  '
   html += select_tag :per_page, options_for_select(per_pages_for_select, params[:per_page]), class: class_name
   html += '件毎に表示にする'
   html.html_safe
  end

エラーを見るとどうやらobjects.sizeが怪しい・・・

よくわからないけど、とりあえずobjectsの中身を確認してみようと思って、

  def page_navi(objects, class_name = 'per_page')
   return if objects.nil?
   p objects
   html  = objects.total_count.to_s

と書いて確認してみたところ、

なんとエラーが発生しなくなった!!

おいおい意味わかんねーぞといろいろ試してみたところ、

   html += "#{ objects.limit_value * objects.offset_value / objects.limit_value + objects.length }"

にしたら大丈夫だった
ちなみに objects.count にすると p をやってもダメだった

sizeとcountとlengthで違いがあるというのは噂レベルで知っていたけど、
いい機会なのでちょっと調べてみたら、
【Rails】sizeとcountとlengthについて
こちらのページがわかりやすかった

カウンタキャッシュを使っていない場合、
sizeは、ロード時には配列の要素数を返し、未ロード時にはcount文を発行しますし、
lengthは、ロード時には配列の要素数を返しますが、未ロード時にはselect * ~を発行したのち配列の要素数を返します。
そんでもって、
countはどんなときであれ毎回count文を発行します。

詳しくはわかってないけど、どうやら自分の作ったクエリではcount文がうまく作れないため、
sizeで作っていたときには未ロード時にcount文を作ろうとしてエラーになっていたらしい。

ちなみに今回作ったクエリはグルーピングしてました。
(グルーピングの部分を外すとsizeでも問題なく動きました。)

lengthを使うとパフォーマンスに影響が出るそうだけど、
今回lengthを使う場面は必ず一覧画面とセットなので、
まぁそれほど影響は出ないと信じ、とりあえずlengthで突っ走ります。