便利なコメントを自動生成: annotate_models

annotate_models https://github.com/ctran/annotate_models

Ruby on Rails 4(たしか)から、モデルに属性を書かなくなった。 そのおかげでモデルのファイルを見ても何のプロパティを持っているかわからない。

schema.rbを見ればいいのだが、あれはなかなか巨大だし見るのは辛い。

annotate_modelsを使うとこんな感じでモデルのファイルに自動でコメントを生成してくれる。

# == Schema Info
#
# Table name: line_items
#
#  id                  :integer(11)    not null, primary key
#  quantity            :integer(11)    not null
#  product_id          :integer(11)    not null
#  unit_price          :float
#  order_id            :integer(11)
#

 class LineItem < ActiveRecord::Base
   belongs_to :product
  . . .

かなり気が利くgemでオプションを色々指定できる。

ストファイルにも書いたり、どのタイミングでファイルに書き込むかとかフォーマットとか。

10以上、50以下の値

前提

  • countには整数が入っている
  • countが10より小さい場合: count = 10とする
  • countが50より大きい場合: count = 50とする

パターン1

単純にコードに置換えた例。一番よく見る。

count = 10 if count < 10
count = 50 if count > 50

パターン2

これもよく見るコード。最小値、最大値でmax, minをする。

count = [[count, 10].max, 50].min

パターン3

こんな風にもかける。

count = [10, count, 50].sort[1]

パターン4

キモい方法思いついた。

{:< => 10, :> => 50, :== => count}.find { |k, v| count.send(k, v) }[1]

パターン5

思いつかぬ。。

Railsでモデルを全列挙する方法

Is there a way to get a collection of all the Models in your Rails app? - Stack Overflow

いろいろ方法があるし、どの方法も一長一短に思える。

その1

# eager_load!...
Rails.application.eager_load!
ActiveRecord::Base.descendants

その2

# 自分のユースケース的にはこれが良かった。全テーブル名を取得して、
tables = ActiveRecord::Base.connection.tables

# こんな感じでmapしてテーブル名からモデルクラスをたどる。
tables.map(&:classify).map(&:safe_constantize).compact

Railsのhas_manyにおけるsize, count, lengthの違い

掲示板アプリみたいなのを想像する。投稿をPostとしてそれにCommentを複数つけられるシンプルなものだ。Post:Commentが1:Nの関係だ。

環境

p = Post.first

#count

[2] pry(main)> p.comments.count
   (0.3ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 1
=> 1
[3] pry(main)> p.comments.count
   (0.5ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 1
=> 1

常にSQLでCOUNT文を流してる。

#size

commentsがロードされている場合、そのcommentsのsizeを返す

[14] pry(main)> p.comments
  Comment Load (0.4ms)  SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` = 1
[15] pry(main)> p.comments.size
=> 1

commentsがロードされてない場合、countと同じ

[4] pry(main)> p.comments.size
   (0.3ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 1
=> 1
[5] pry(main)> p.comments.size
   (0.3ms)  SELECT COUNT(*) FROM `comments` WHERE `comments`.`post_id` = 1
=> 1

※ 0件の場合は、p.comments.sizeを呼ぶと、その時点でロードされたことになって二度目からはキャッシュされた値を使うので注意。

#length

commentsがロードされている場合、そのcommentsのsizeを返す

[20] pry(main)> p.comments.length
=> 1

commentsがロードされていない場合、commentsをロードしてそのsizeを返す

[18] pry(main)> p.comments.length
  Comment Load (0.3ms)  SELECT `comments`.* FROM `comments` WHERE `comments`.`post_id` = 1
=> 1

まとめ

常にCOUNT文による件数が欲しい場合は、#countを使う。それ以外は、#sizeでOK。#lengthはまぁ使うことはあまりなさそうかな。