Skip to content

Instantly share code, notes, and snippets.

@airhorns
Created February 9, 2012 20:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save airhorns/1782762 to your computer and use it in GitHub Desktop.
Save airhorns/1782762 to your computer and use it in GitHub Desktop.
class Batman.SetTransform extends Batman.Set
constructor: (@source, observedItemKeys..., @transform) ->
super()
@transformIndex = new Batman.SimpleHash
if @source.isObservable
@_setObserver = new Batman.SetObserver(@source)
@_setObserver.observedItemKeys = observedItemKeys
@_setObserver.observerForItemAndKey = @observerForItemAndKey
@_setObserver.on 'itemsWereAdded', @add
@_setObserver.on 'itemsWereRemoved', @remove
@startObserving()
@add(@source.toArray()...)
startObserving: -> @_setObserver?.startObserving()
stopObserving: -> @_setObserver?.stopObserving()
observerForItemAndKey: (item, key) =>
(key) =>
@remove(item)
@add(item)
add: (items...) =>
transformedItems = []
for item in items
if transformedItem = @transform(item)
transformedItems.push transformedItem
@transformIndex.set item, transformedItem
super(transformedItems...)
remove: (items...) =>
transformedItems = items.map (item) => @transformIndex.get(item)
@transformIndex.unset(item) for item in items
super(transformedItems...)
class ComponentAssociationSet extends Batman.AssociationSet
foreignKeyKey: false
modelFunction: false
load: (callback) ->
loadOptions = {}
loadOptions[@association[@associationForeignKeyKey]] = @foreignKeyValue
@association[@associationModelFunction]().load loadOptions, (err, records) =>
@set 'loaded', true unless err
@add(records...)
callback(err, @)
class RelatedModelAssociationSet extends ComponentAssociationSet
associationForeignKeyKey: 'foreignKey'
associationModelFunction: 'getRelatedModel'
class JoinModelAssociationSet extends ComponentAssociationSet
associationForeignKeyKey: 'joinLocalKey'
associationModelFunction: 'getJoinModel'
class AssociationSetUnion extends Batman.SetUnion
constructor: (@key, @association, @related, @join) ->
@joinedRecords = new Batman.SetTransform @join, @association.joinLabel, @relatedRecordFromJoin
@join.on 'itemsWereRemoved', (joinRecords...) =>
relatedRecords = (@relatedRecordFromJoin(joinRecord) for joinRecord in joinRecords)
@related.remove(relatedRecords...)
@joinedRecords.on 'itemsWereRemoved', (relatedRecords...) =>
@related.remove(relatedRecords...)
super(@related, @joinedRecords)
relatedRecordFromJoin: (item) =>
proxyOrRecord = item.get(@association.joinLabel)
if (proxy = proxyOrRecord) instanceof Batman.AssociationProxy
return proxy.get('target')
else
return proxyOrRecord
add: (items...) ->
items = (item for item in items when item?)
super(items...)
loaded: false
load: (callback) ->
count = 0
componentLoadCallback = (err, records) =>
unless (err && count = -1) || ++count == 2
@set 'loaded', true
callback(err, @)
@related.load componentLoadCallback
@join.load componentLoadCallback
class AssociationSetIndex extends Batman.SetIndex
constructor: (@association, @setClass) ->
super @association[@setClass::associationModelFunction]().get('loaded'), @association[@setClass::associationForeignKeyKey]
_resultSetForKey: (key) ->
@_storage.getOrSet key, =>
new @setClass(key, @association)
_setResultSet: (key, set) ->
@_storage.set key, set
class Batman.HasAndBelongsToManyAssociation extends Batman.HasManyAssociation
associationType: 'hasMany'
constructor: ->
@setCache = {}
super
@joinLocalKey = @options.joinLocalKey || (Batman.helpers.singularize(Batman._functionName(@model)).toLowerCase() + "_id")
@joinForeignKey = @options.joinForeignKey || (Batman.helpers.singularize(@label).toLowerCase() + "_id")
@joinLabel = @options.joinLabel || Batman.helpers.singularize(@label).toLowerCase()
setForRecord: (record) ->
Batman.Property.withoutTracking =>
if id = record.get(@localKey)
unless @setCache[id]
relatedModelSet = @relatedModelSetIndex().get(id)
joinModelSet = @joinModelSetIndex().get(id)
union = new AssociationSetUnion(id, @, relatedModelSet, joinModelSet)
@setCache[id] = union
@setCache[id]
else
new Batman.AssociationSet(undefined, @)
apply: (error, base) ->
base.set @label, @setForRecord(base) unless error
super
relatedModelSetIndex: ->
@relatedIndex ||= new Batman.HasAndBelongsToMany.AssociationSetIndex(@, RelatedModelAssociationSet)
@relatedIndex
joinModelSetIndex: ->
@joinIndex ||= new Batman.HasAndBelongsToMany.AssociationSetIndex(@, JoinModelAssociationSet)
@joinIndex
getJoinModel: ->
scope = @options.namespace or Batman.currentApp
modelName = @options.joinModelName
scope?[modelName]
reset: ->
delete @joinIndex
delete @relatedIndex
@setCache = {}
true
encoder: -> {encode: false, decode: false}
Batman.HasAndBelongsToMany = {ComponentAssociationSet, RelatedModelAssociationSet,
JoinModelAssociationSet, AssociationSetUnion, AssociationSetIndex}
class MyApp.Model extends Batman.Model
@hasAndBelongsToMany: (label, scope) ->
@_batman.check(@)
collection = @_batman.associations ||= new Batman.AssociationCurator(@)
collection.add new Batman.HasAndBelongsToManyAssociation(@, label, scope)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment