Skip to content

Commit

Permalink
Make hasMany associations return useful sets when asked for on new re…
Browse files Browse the repository at this point in the history
…cords.
  • Loading branch information
airhorns committed Dec 19, 2011
1 parent 39a1f19 commit fa9f216
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 17 deletions.
27 changes: 18 additions & 9 deletions lib/batman.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 14 additions & 6 deletions src/batman.coffee
Expand Up @@ -2622,6 +2622,7 @@ class Batman.AssociationSet extends Batman.Set
constructor: (@key, @association) -> super()
loaded: false
load: (callback) ->
return callback(undefined, @) unless @key?
loadOptions = {}
loadOptions[@association.foreignKey] = @key
@association.getRelatedModel().load loadOptions, (err, records) =>
Expand All @@ -2640,6 +2641,8 @@ class Batman.AssociationSetIndex extends Batman.SetIndex
@_storage.getOrSet key, =>
new Batman.AssociationSet(key, @association)

_setResultSet: (key, set) ->
@_storage.set key, set

class Batman.AssociationCurator extends Batman.SimpleHash
@availableAssociations: ['belongsTo', 'hasOne', 'hasMany']
Expand Down Expand Up @@ -2707,6 +2710,8 @@ class Batman.Association

getFromAttributes: (record) ->
record.constructor.defaultAccessor.get.call(record, @label)
setIntoAttributes: (record, value) ->
record.constructor.defaultAccessor.set.call(record, @label, value)

encoder: -> developer.error "You must override encoder in Batman.Association subclasses."
setIndex: -> developer.error "You must override setIndex in Batman.Association subclasses."
Expand Down Expand Up @@ -2752,18 +2757,21 @@ class Batman.PluralAssociation extends Batman.Association
Batman.Property.withoutTracking =>
if id = record.get(@localKey)
@setIndex().get(id)
else
new Batman.AssociationSet(undefined, @)

getAccessor: (self, model, label) ->
return if @amSetting
return unless self.getRelatedModel()

# Check whether the relation has already been set on this model
if setInAttributes = self.getFromAttributes(@)
return setInAttributes
setInAttributes
else
relatedRecords = self.setForRecord(@)
self.setIntoAttributes(@, relatedRecords)

if relatedRecords = self.setForRecord(@)
Batman.Property.withoutTracking ->
if self.options.autoload and not relatedRecords.loaded
Batman.Property.withoutTracking =>
if self.options.autoload and not @isNew() and not relatedRecords.loaded
relatedRecords.load (error, records) -> throw error if error

relatedRecords
Expand Down Expand Up @@ -2882,7 +2890,7 @@ class Batman.HasManyAssociation extends Batman.PluralAssociation
jsonArray

decode: (data, key, _, __, parentRecord) ->
relations = association.setForRecord(parentRecord) || new Batman.AssociationSet(undefined, association)
relations = association.setForRecord(parentRecord)
if relatedModel = association.getRelatedModel()
for jsonObject in data
record = new relatedModel
Expand Down
40 changes: 38 additions & 2 deletions tests/batman/model/associations/has_many_test.coffee
Expand Up @@ -107,6 +107,7 @@ asyncTest "hasMany associations are not loaded when autoload is false", 1, ->
asyncTest "hasMany associations can be reloaded", 8, ->
loadSpy = spyOn(@Product, 'load')
@Store.find 1, (err, store) =>
throw err if err
products = store.get('products')
ok !products.loaded
setTimeout =>
Expand All @@ -129,17 +130,19 @@ asyncTest "hasMany associations are saved via the parent model", 5, ->

storeSaveSpy = spyOn store, 'save'
store.save (err, record) =>
throw err if err
equal storeSaveSpy.callCount, 1
equal product1.get('store_id'), record.id
equal product2.get('store_id'), record.id

@Store.find record.id, (err, store2) =>
throw err if err
storedJSON = @storeAdapter.storage["stores#{record.id}"]
deepEqual store2.toJSON(), storedJSON
# hasMany saves inline by default
deepEqual storedJSON.products, [
{name: "Gizmo", store_id: record.id}
{name: "Gadget", store_id: record.id}
{name: "Gizmo", store_id: record.id, productVariants: []}
{name: "Gadget", store_id: record.id, productVariants: []}
]
QUnit.start()

Expand Down Expand Up @@ -208,6 +211,39 @@ asyncTest "hasMany child models are added to the identity map", 1, ->
equal @ProductVariant.get('loaded').length, 2
QUnit.start()

asyncTest "unsaved hasMany models should accept associated children", 2, ->
product = new @Product
variants = product.get('productVariants')
delay =>
equal variants.length, 0
variant = new @ProductVariant
variants.add variant
equal variants.length, 1

asyncTest "unsaved hasMany models should save their associated children", 4, ->
product = new @Product(name: "Hello!")
variants = product.get('productVariants')
variant = new @ProductVariant(price: 100)
variants.add variant
product.save (err, product) =>
throw err if err
storedJSON = @productAdapter.storage["products#{product.get('id')}"]
deepEqual storedJSON,
id: 11
name: "Hello!"
productVariants:[
{price: 100, product_id: product.get('id')}
]

ok !product.isNew()

# Mock out what a realbackend would do: assign ids to the child records
# The TestStorageAdapter is smart enough to do this for the parent, but not the children.
variant.fromJSON {price:100, product_id: product.get('id'), id: 11}
equal variant.get('product_id'), product.get('id')
ok !variant.isNew()
QUnit.start()

asyncTest "hasMany associations render", 4, ->
@Store.find 1, (err, store) =>
throw err if err
Expand Down
1 change: 1 addition & 0 deletions tests/batman/model/model_helper.coffee
Expand Up @@ -18,6 +18,7 @@ class TestStorageAdapter extends Batman.StorageAdapter
id = record.set('id', @counter++)
if id
@storage[@storageKey(record) + id] = record.toJSON()
record.fromJSON {id: id}
callback(undefined, record)
else
callback(new Error("Couldn't get record primary key."))
Expand Down

0 comments on commit fa9f216

Please sign in to comment.