我想为特定模型的所有集合添加一个方法.假设我想将方法my_complicated_averaging_method添加到WeatherData集合中:
WeatherData.all.limit(3).my_complicated_averaging_method() Station.first.weatherdata.my_complicated_averaging_method()@H_404_4@这样做最好的方法是什么?目前我发现的唯一方法是这样的:
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@H_404_4@这是将方法添加到集合中的好方法吗?有没有更好/支持的方式来做到这一点?
解决方法
有很多方法如何做,而且你完全有效(尽管我个人更喜欢将类方法包装到单独的块检查
this中),但是随着人们向他们的模型添加更多的业务逻辑,并盲目追随“瘦的控制器”胖型“概念,型号变成了完整的混乱.
@H_404_4@为了避免这种混乱,介绍服务对象是一个好主意,在你的情况下,它将是这样的:
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ @H_404_4@UPD @H_404_4@你是正确的使用实例变量是一个可能的方式来弄乱对象内部(加上它不是一个公共接口,它可能会在将来改变).我的建议是使用方法范围.基本上用范围替换@relation. @H_404_4@检查这个例子.我使用我自己的项目中的一个模型来表明它实际上是有效的
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@H_404_4@现在您可以通过将您的集合传递给它来直接调用此类.但您也可以代理这样的呼叫:
def self.my_complicated_averaging_method AverageWeatherData.data(@relation) end@H_404_4@我鼓励你阅读这个博客来更多地了解这种方法:
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ @H_404_4@UPD @H_404_4@你是正确的使用实例变量是一个可能的方式来弄乱对象内部(加上它不是一个公共接口,它可能会在将来改变).我的建议是使用方法范围.基本上用范围替换@relation. @H_404_4@检查这个例子.我使用我自己的项目中的一个模型来表明它实际上是有效的
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]@H_404_4@UPD @H_404_4@在Rails 4范围内被弃用,所以使用全部是正确的.
all.map(&:id)