微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ruby-on-rails – 如何向activerecord集合添加方法?

我想为特定模型的所有集合添加一个方法.假设我想将方法​​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)

原文地址:https://www.jb51.cc/ruby/271574.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐