如何在从ActiveRecord检索的所有对象上运行一些代码?

时间:2013-06-09 19:00:08

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2

我想用从外部API接收的值初始化检索到的对象中的一些属性。 after_findafter_initialize回调对我不起作用,因为我必须为每个接收的对象调用API,这是非常慢的。我想要以下内容:

class Server < ActiveRecord::Base
  attr_accessor :dns_names
  ...
  after_find_collection do |servers|
    all_dns_names = ForeignLibrary.get_all_dns_entries
    servers.each do |s|
      s.dns_names = all_dns_names.select{|r| r.ip == s.ip}.map{|r| r.fqdn}
    end
  end
end

请注意,缓存不是解决方案,因为我需要始终拥有当前数据,并且数据可能会在应用程序之外进行更改。

1 个答案:

答案 0 :(得分:1)

您需要一个类方法来增强数据中找到的每个服务器。所以,比如:

def index
  servers = Server.where(condition: params[:condition]).where(second: params[:second])
  @servers = Server.with_domains_names(servers)
end

class Server
  def self.with_domain_names(servers)
    all_dns_names = ForeignLibrary.get_all_dns_entries
    servers.each do |s|
      s.dns_names = all_dns_names.select{|r| r.ip == s.ip}.map{|r| r.fqdn}
    end
  end
end

这样,ForeignLibrary.get_all_dns_entries只运行一次,您可以使用该额外信息增强服务器。

如果您希望每次初始化服务器对象时都这样做,我只需委托而不是使用after_initialize。因此,您可以将所有dns条目有效地存储在全局变量中,然后将其缓存一段时间。 ForeignLibrary.get_all_dns_entries调用。所以,它会是这样的:

class Server
  def dns_names
    ForeignLibrary.dns_for_server(self)
  end
end

class ForeignLibrary

  def self.reset
    @@all_dns_names = nil
  end

  def self.dns_for_server(server)
    all_dns_names.select{|r| r.ip == server.ip}.map{|r| r.fqdn}
  end

  def self.all_dns_names
    Mutex.new.synchronize do
      @@all_dns_names ||= call_the_library_expensively
    end
  end
end

(我在这里也使用了一个互斥锁,因为我们正在使用|| = with class variables)

要使用它,你会:

class ApplicationController
  before_filter do
    ForeignLibrary.reset #ensure every page load has the absolute latest data
  end
end