public
Last active

Ruby on Rails. Module to handle 'quantity' attributes by overriding the << and delete methods of the has_many association

  • Download Gist
gistfile1.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
class Package < ActiveRecord::Base
has_many :products, :through => :packagings, extend => QuantityAssociation
 
# ... do stuff
 
end
 
module QuantityAssociation
 
def include_duplicates
return_array = []
through_collection.each do |through|
associate = through.send(reflection_name)
return_array.concat Array.new(through.quantity || 1).fill( associate )
end
return_array
end
 
def <<(*records)
result = true
load_target if proxy_association.owner.new_record?
 
transaction do
flatten_deeper(records).each do |record|
existing = get_existing_through record
if existing.present?
existing.increment! :quantity
else
result &&= super(record)
end
end
end
 
result && self
end
 
def delete(*records)
transaction do
flatten_deeper(records).each do |record|
existing = get_existing_through record
if existing.present? and existing.quantity.present? and existing.quantity > 1
existing.decrement! :quantity
else
super(record)
end
end
end
end
 
private
 
def reflection_name
proxy_association.source_reflection.name
end
 
def through_source_key
proxy_association.reflection.source_reflection.foreign_key
end
 
def through_name
proxy_association.reflection.through_reflection.name
end
 
def through_collection
proxy_association.owner.send through_name
end
 
def get_existing_through record
through_collection.where("#{through_source_key}" => record.id).try(:first)
end
 
def flatten_deeper(array)
array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
end
 
end
 
#--------
#--------
 
# Means you can just do things like
 
package = Package.first
package.products << Product.first
 
# and if the product is already associated, the 'quantity' column will be
# incremented on the 'packagings' table

The 'get_existing_through' method could well be done simpler. Anyone know a better way?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.