Shopify/batman — pull request #542 by hornairs (2012-08-27 03:03:36+00:00)
Title: Has many index fixes

Proposed fix for #537.

This makes the get accessor for the parent in a has many relationship more consistent with what `AssociationSet` it returns. It now returns the same set before and after a record has an id, which it didn't before.

Some background: has many associations use a set index on the child record's loaded set to always reflect the up to date set of child records. They say "give me all the child records who's (for example) `product_id` is equal to 1 and store that Set on the Product with ID 1", and because this is implemented using a Set index, if new records get saved or loaded which have the `product_id` = 1 the SetIndex is looking for, they will automatically appear in the first Product's set. The issue with this is that the SetIndex needs to know what the product's ID is to be able to look in the child record's loaded set, so any time before the product has an ID needs another solution. It used to be just use a dangling `AssociationSet` who had no parent `SetIndex` and then when the ID came around switch to using a `Set` generated by the `SetIndex`. One such situation where the parent record has no ID is during decoding. Decoding works by first extracting all the data and then mixing it into the record all at once, which for us means the `HasManyAssociation` decoder must operate on embedded JSON associations before the parent record has an ID. Before, this decoder would just stick records generated from these embedded associations in the dangling set as mentioned above, and I guess because I didn't understand the first time I implemented this, the `Set` generated by the `SetIndex` would never be swapped in, and the result of the get accessor on the parent record would always be the dangling, "un-smart" set.

The results of making hasMany use the same association sets regardless of record state:
- It means that if a record gets a bunch of child records before it has an ID, those records will be around when the record gets it's ID. This happened before this change too.
- This also means that when a record gets and ID and starts using a SetIndex to index the child record loaded set, the same set it had before it had an ID will be used as destination the SetIndex uses to put records in and out of. This did not happen before.
- The practical ramifications of this are that A) references to the set can be kept by calling code since it won't change at some weird point in the future, B) .add()s that happened will be preserved, and most importantly C) after the parent record gets an ID any changes to child record attributes will be reflected by the `SetIndex` into the parent record's newly `SetIndex` associated `AssociationSet`.

Phew.

@nciagra this is the issue you fixed the other day by adding an ID in the fixtures of a model.

@jamesmacaulay @nciagra please review!