包装#find_each并自己公开类似的功能

时间:2016-03-15 14:54:09

标签: ruby-on-rails activerecord

背景

我已将某些功能分离到各种服务/库中,尝试将此功能与我的应用程序的其余部分完全分开。我将从这里称之为服务。

目前这个小服务将ActiveRecord对象泄漏到调用代码中,但我真的更喜欢分离更清晰。

代码示例

服务的API:

module MyService
  # This method should really be read-only, but we're exposing 
  # ActiveRecord objects, so the caller can mess around with them. :(
  def self.people
    Person.all
  end
end

调用代码示例

MyService.people.find_each(batch_size: 500) do |person|
  # Do something useful with person
end

我正在考虑将ActiveRecord对象转换为Structs,然后再将它们返回到调用代码,这可能是实现此目的的一个想法。

问题

这只有一个问题:Person模型有数千个条目。因此,我的天真方法将实例化数千个ActiveRecord,将其内容复制到数千个Structs然后返回。这显然是一个巨大的记忆力。

我想要的是能够让调用代码批量获取Person对象,并避免让MyService.people同时实例化所有记录。

我真的无法完全理解如何实现这一点。谁能指出我在这方面的正确方向?

1 个答案:

答案 0 :(得分:2)

您可以在需要阻止的MyService上编写自己的方法,并将Person转换为yield之前的只读版本。

module MyService
  ReadOnlyPerson = Struct.new(:first_name, :last_name)

  def self.each_person
    Person.find_each do |person|
      read_only_person = ReadOnlyPerson.new(person.first_name, person.last_name)
      yield read_only_person
    end
  end
end
MyWrapper.each_person do |person|
  puts person.first_name
end

如果您希望保留完整的ActiveRecord功能(写作除外),您还可以使用readonly!直接标记您的实例:

def self.each_person
  Person.find_each do |person|
    person.readonly!
    yield person
  end
end

您仍然可以分配person.first_name = 'Phil'之类的内容,但尝试save会引发异常:

ActiveRecord::ReadOnlyRecord: Person is marked as readonly