It is not creating two new clients with the same attributes. The
“replace” method in AssoctiationCollection, which is what is called
when you do something like model_instance.collection = [other_model1,
other_model2], looks like this:
# Replace this collection with +other_array+
# This will perform a diff and delete/add only records that have
changed.
def replace(other_array)
other_array.each { |val| raise_on_type_mismatch(val) }
load_target
other = other_array.size < 100 ? other_array :
other_array.to_set
current = @target.size < 100 ? @target : @target.to_set
@owner.transaction do
delete(@target.select { |v| !other.include?(v) })
concat(other_array.select { |v| !current.include?(v) })
end
end
Certainly if the size of the existing or supplied array is > 100, it’s
going to ignore duplicates, but also the delete and concat calls,
because they don’t check for duplicate instances of the same object,
will ignore them as well. That means that if @target and other_array
contain the same unique set of objects replace will do nothing. For
example, consider ModelA which has_many ModelB. Assume instance
mod_a.model_bs == [ mod_b ]. If that were the case, the following
line would have no effect:
mod_a.model_bs = [mod_b, mod_b, mod_b, mod_b, mod_b, mod_b, mod_b,
mod_b, mod_b]
Similarly, if mod_a.model_bs already was set to [mod_b, mod_b, mod_b,
mod_b, mod_b, mod_b, mod_b, mod_b, mod_b], and we did the following,
it would have no effect:
mod_a.model_bs = [mod_b]
What I’m suggesting is that if we created the has_many relationship
with :uniq => true I would expect this behavior. Instead it seems
that :uniq => true only has an effect on database reads.
“collection=” is documented in the API as, “replaces the collections
content by deleting and adding objects as appropriate”, which doesn’t
seem true if the current or new collection contain duplicates