Resolve Promises Using Composite Pattern

When creating an object oriented CRUD operation, often you’ll have a case where an object will have children that are independently managed resources. Each child is simply associated to the parent, but when editing the entire object it’s likely that a better guest experience calls for one screen, rather than many.  The editor shouldn’t have to care if they’re updating one or many objects at once.

If the parent is dependent on the children objects to complete their operations before the parent can complete theirs, then you’ll have to create a dependency tree.  Each child would likely have other dependents, and would thusly need to wait until their dependencies are saved before continuing up the tree.

We cannot simply start with the last child process and work our way back up, this could result in orphaning the children that are in the outer-most parallel branches of the tree. We’ll need to begin from the parent, and recursively work our way down the branches until we’ve resolved each child’s process.  Using the composite pattern, we can treat each child the same way as a single instance of an object, and in this case each one is an ngResource.

app.service 'resourceService', ['$resource', '$rootScope', '$q', ($resource, $rootScope, $q)->
    $rootScope.resources = {}
    resourceService      = 
        newResource : (tag, url, paramDefaults = {}, actions = {}, options = {})->
            if !$rootScope.resources[tag]
                actions = _.extend actions,
                    update : 
                        method : 'PUT'

                resource = $resource(url, paramDefaults, actions, options)
                $rootScope.resources[tag] = resourceService.extendResource resource
            $rootScope.resources[tag]
    
        autoQuery : (tag, url, paramDefaults = {}, actions = {}, options = {})->
            resourceService.newResource(tag, url, paramDefaults, actions, options).query()

        getResource : (tag)->
            $rootScope.resources[tag]

        convertToResource : (obj, resource)->
            if Array.isArray obj
                obj = (item = resourceService.convertToResource item, resource for item in obj)
            else
                proto  = Object.create resource.prototype
                obj    = _.extend proto, obj
                obj    = resourceService.extendResource obj

            obj

        extendResource : (resource)->
            if Array.isArray resource
                resource = (r = resourceService.extendResource r for r in resource)
            else
                # Since JS won't do this for me, let's create a hash for each resource.
                resource._hash = Math.random().toString(36).substring(7)

                # Create a child for this resource
                resource._children = []

                resource.getResourceId = ()->
                    @_hash

                resource.addChild = (child)->
                    # locate the hash and remove it if applicable
                    hash = child.getResourceId()
                    @_children = _.reject @_children, (child)-> return child.id == hash
                    @_children.push
                        id : hash
                        resource : child

                resource.setCallbacks = (@successCallback, @errorCallback, @notifyCallback)->
                    resource
                
                resource.resolve = ()->
                    children = @_children
                    promises = []
                    promises.push $q.when child.resource.resolve() for child in children
                    combined = $q.all promises
                    parent = @
                    combined.then(()-> parent._children = [])
                        .then(@successCallback, @errorCallback, @notifyCallback)

            # return the resource
            resource

    resourceService
]