Alesya Huzik
Alesya Huzik

Reputation: 1640

How can I run some code on all objects retrieved from ActiveRecord?

I want to initialize some attributes in retrieved objects with values received from an external API. after_find and after_initialize callbacks won't work for me as this way I have to call the API for each received object, which is is quite slow. I want something like the following:

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

Please note that caching is not a solution, as I need to always have current data, and the data may be changed outside the application.

Upvotes: 0

Views: 73

Answers (1)

Jesse Wolgamott
Jesse Wolgamott

Reputation: 40277

You'd want to have a class method that enhances each server found with your data. so, something like:

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

This way, the ForeignLibrary.get_all_dns_entries only gets run once, and you can enhance your servers with that extra information.

If you wanted to do this every time you initialize a server object, I'd simply delegate rather than use after_initialize. So you'd effectively store the all dns entries in a global variable, and then cache it for a period of time. ForeignLibrary.get_all_dns_entries call. So, it would be something like:

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

(I also used a mutex here since we are doing ||= with class variables)

to use it, you would:

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

Upvotes: 1

Related Questions