scopeとクラスメソッドの挙動の違い

これまで model で定義する scopeとクラスメソッドの違いについてあまりよくわからないまま使っていたんだけど、明確な挙動の違いに遭遇したのでメモしておく

例えば以下のような scopeを作りたいとする

scope :all_counts_by_name, lambda{|name|
  counts = {} 
  StatusTable.each_key{|key|                                                                                                                     
    counts[key] = self.by_name_and_status(name,key).count  
  }     
  counts 
}

Hashへの代入文がエラーになる

     Failure/Error: @task_counts = Task.s_all_counts_by_name("volpe")
     ArgumentError:
       Unknown key(s): done, waiting, todo_h, doing, todo_m, todo_l
     # ./spec/models/task_spec.rb:135
  • 存在しない key を使って代入しようとすると怒られる
  • しかしなぜかfetchを使うと上手くいく
      counts.fetch(key, self.by_name_and_status(name,key.to_sym).count)   
  • lambda 内では Hash への代入形式が使えないのだろうか?

戻り値の型は Hash を期待しているのに ActiveRecord::Relation が返って来てしまう

  • そのため以下の様なテストコードを書いていても通らない
  @task_counts = Task.s_all_counts_by_name("volpe")
  @task_counts[:todo_h].should >= 1
  • こんなエラー
     Failure/Error: subject[:todo_h].should >= 1
     TypeError:
       Symbol as array index
     # ./spec/models/task_spec.rb:141
  • scope の戻り値の型は ActiveRecord::Relation に決まっている?
結局 scope を使うのは断念し、クラスメソッドにした
def self.all_counts_by_name(name)
  counts = {} 
  StatusTable.each_key{|key|                                                                                                                     
    counts[key] = self.by_name_and_status(name,key).count                                                                           
  }     
  counts                                                                                                                                        
end