Locating existing records In order to reduce redundancy in my app, I have added the method self.find_or_keep(object) to many of my models in order to find existing records with the same values, so I can reference the already existing ones instead of creating new records. Basically, this method checks most attributes for equality between two objects; id, created_at and updated_at are always excluded for obvious reasons.

class IPv4 &lt; ActiveRecord::Base
  # returns its param or an already existing record
  def self.find_or_keep(ipv4)
    # create new object in memory, if param is a Hash
    ipv4 = IPv4.new(ipv4) if ipv4.is_a?(Hash)
    return (entry = IPv4.find_by(address: ipv4.address, subnetmask: ipv4.subnetmask)) ? entry : ipv4
  end
end

class Service &lt; ActiveRecord::Base
  # returns its param or an already existing record
  def self.find_or_keep(service)
    # create new object in memory, if param is a Hash
    service = Service.new(service) if service.is_a?(Hash)
    return (entry = Service.find_by(protocol: service.protocol, port_from: service.port_from, port_to: service.port_to)) ? entry : service
  end
end


As you can see, it's not very DRY. The problem here is that the method each time has to check for different parameters, so I have to rewrite the find_by every time I want to use the method in another class. What I have in mind for DRYing it up is something like this:


class IPv4 &lt; ActiveRecord::Base
  implement_find_or_keep_with(:address, :subnetmask)
end

class Service &lt; ActiveRecord::Base
  implement_find_or_keep_with(:protocol, :port_from, :port_to)
end

class ActiveRecord::Base
  # whatever is need
end



Is something like this possible? If so, how would I implement it?
Rails already has a built-in mechanism for doing this with the first_or_initialize method within ActiveRecord::Relation.

As an example, you can perform the following

Service.where(protocol: params[:protocol], port_from: params[:port_from], port_to: params[:port_to]).first_or_initialize


This will try and find a service with the given attributes OR initialize a new object with those attributes set if it can't find it.

Hope this helps.