Skip to content

Instantly share code, notes, and snippets.

@kakra
Created October 15, 2010 17:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kakra/628584 to your computer and use it in GitHub Desktop.
Save kakra/628584 to your computer and use it in GitHub Desktop.
Associates models to a parent by other methods, similar to delegates
# AssociateBy
#
# Associates models to a parent by other methods, similar to delegates but
# changing the association itself instead of the associated object when
# writing to the delegated attribute.
#
# Usage:
# ActiveRecord::Base.class_eval { include AssociateBy }
#
# Example:
#
# class Street < ActiveRecord::Base
# has_many :locations
# end
#
# class Location < ActiveRecord::Base
# belongs_to :street
# associate :street, :by => :name
# end
#
# @street = Street.create :name => "First Street"
# puts @street.id #==> 1
# Street.create :name => "Second Ave"
#
# puts @location.street_name #==> nil
# @location.street_name = "First Street"
# puts @location.street.id #==> 1
#
# @location.street_name = "Second Ave"
# puts @location.street.id #==> 2
#
# @location.street = Street.first
# puts @location.street_name #==> "First Street"
#
# written by Kai Krakow, http://github.com/kakra
# Provided as-is
module AssociateBy
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
# TODO check sanity of parameters
# TODO check "by_method" is an attribute of the association
# TODO check "by_method" is a unique key of the association
# TODO watch changes to the association to invalidate memoization
def associate(association, options = {})
by_method = options[:by].to_s
associated_method = "#{association.to_s}_#{by_method}"
src = <<-SRC
def #{associated_method}
@#{associated_method} ||= #{association.to_s}.#{by_method}
rescue NoMethodError
@#{associated_method} = nil
end
def #{associated_method}=(#{by_method})
if self.#{association.to_s} = #{association.to_s.classify}.find_by_#{by_method}(#{by_method})
@#{associated_method} = #{association}.#{by_method}
else
@#{associated_method} = nil
end
end
SRC
self.class_eval src, __FILE__, __LINE__
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment