我想为特定模型的所有集合添加一个方法.假设我想将方法my_complicated_averaging_method添加到WeatherData集合中:
WeatherData.all.limit(3).my_complicated_averaging_method() Station.first.weatherdata.my_complicated_averaging_method()
class WeatherData < ActiveRecord::Base def self.my_complicated_averaging_method weighted_average = 0 @relation.each do |post| # do something complicated # weighted_average = end return weighted_average end end
解决方法
有很多方法如何做,而且你完全有效(尽管我个人更喜欢将类方法包装到单独的块检查
this中),但是随着人们向他们的模型添加更多的业务逻辑,并盲目追随“瘦的控制器”胖型“概念,型号变成了完整的混乱.
为了避免这种混乱,介绍服务对象是一个好主意,在你的情况下,它将是这样的:
class AverageWeatherData class << self def data(collection) new(collection).data end end def initialize(collection) @collection = collection end def data @collection.reduce do |avg,post| # reduce goes through every post,each next iteration receives in avg a value of the last line of iteration # do something with avg and post end # no need for explicit return,every line of Ruby code returns it's value # so this method would return result of the reduce # more on reduce: http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-reduce end end
现在您可以通过将您的集合传递给它来直接调用此类.但您也可以代理这样的呼叫:
def self.my_complicated_averaging_method AverageWeatherData.data(@relation) end
我鼓励你阅读这个博客来更多地了解这种方法:
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
UPD
你是正确的使用实例变量是一个可能的方式来弄乱对象内部(加上它不是一个公共接口,它可能会在将来改变).我的建议是使用方法范围.基本上用范围替换@relation.
检查这个例子.我使用我自己的项目中的一个模型来表明它实际上是有效的
2.0.0p247 :001 > Tracking # just asking console to load this class before modifying it # => Tracking(id: integer,action: string,cookie_id: string,ext_object_id: integer,created_at: datetime,updated_at: datetime) 2.0.0p247 :002 > class Tracking 2.0.0p247 :003?> def self.fetch_ids 2.0.0p247 :004?> scoped.map(&:id) 2.0.0p247 :005?> end 2.0.0p247 :006?> end # => nil 2.0.0p247 :007 > 2.0.0p247 :008 > Tracking.where(id: (1..100)).fetch_ids # Tracking Load (2.0ms) SELECT "trackings".* FROM "trackings" WHERE ("trackings"."id" BETWEEN 1 AND 100) # => [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
UPD
在Rails 4范围内被弃用,所以使用全部是正确的.
all.map(&:id)