Skip to content

Instantly share code, notes, and snippets.

@thehydroimpulse
Last active August 29, 2015 14:08
Show Gist options
  • Select an option

  • Save thehydroimpulse/46ce18966a07b105afd4 to your computer and use it in GitHub Desktop.

Select an option

Save thehydroimpulse/46ce18966a07b105afd4 to your computer and use it in GitHub Desktop.

Revisions

  1. thehydroimpulse revised this gist Oct 27, 2014. 1 changed file with 0 additions and 6 deletions.
    6 changes: 0 additions & 6 deletions cache.coffee
    Original file line number Diff line number Diff line change
    @@ -25,12 +25,6 @@ class Jobber.Cache
    if value is request
    return Promise.resolve(value)
    Promise.reject()

    class CacheItem
    @_request: null,
    @_item: null

    constructor: (@_request, @_item) ->


    getPhotos = () ->
  2. thehydroimpulse revised this gist Oct 27, 2014. 1 changed file with 10 additions and 0 deletions.
    10 changes: 10 additions & 0 deletions resource.coffee
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    # A slightly more abstracted version now using a `Resource` concept. Resources build
    # on-top of a `Request` and `Cache` seeing as there's a common idiom being formed in the above
    # file. Resource will check the cache first, then do the request and propagate errors exactly
    # the same way.
    #
    # But, this is super short and clear and this fully works offline.
    getPhotos = () ->
    Jobber.Resource('http://flickr.com/api/photos/1').then (res) ->
    res.body.photos
    )
  3. thehydroimpulse created this gist Oct 27, 2014.
    103 changes: 103 additions & 0 deletions cache.coffee
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,103 @@
    # Provide an interface for working with an offline cache
    # that makes it easy to create an offline experience.
    class Jobber.Cache
    @extend Jobber.EmitterMixin

    # An in-memory cache for simplistic case. This should be something on disk to ensure
    # the persistence quality one needs for an offline experience.
    #
    # @property _cache Array<CacheItem>
    @_cache: []

    # Put a new item into the cache
    #
    # @param {Any} request
    # @param {Any} item
    # @chainable
    # @return Jobber.Cache
    @put: (request, item) ->
    @_cache[request] = item
    @_cache.push item
    this

    @matches: (request) ->
    for key, value of @_cache
    if value is request
    return Promise.resolve(value)
    Promise.reject()

    class CacheItem
    @_request: null,
    @_item: null

    constructor: (@_request, @_item) ->


    getPhotos = () ->
    return new Promise( (resolve, reject) ->
    cachedData = null
    # Try and fetch from the cache first.
    Jobber.Cache.matches('http://flickr.com/api/photos/1').then((item) ->
    cachedData = item
    # This is where we would do some DOM operations or something with the cached data. The consumers
    # could listen on a `cached` event and do some early work so the user can use the cached data **before**
    # the API calls are triggered.
    ).finally ->
    # The cache might have failed or succeeded, we don't care. We'll try and
    # retrive the most up-to-date version anyways.
    Jobber.Request.create('http://flickr.com/api/photos/1').exec().then( (res, req) ->
    # Update the cache with the up-to-date version if we ever get to this state in the app.
    Jobber.Cache.put req, res.body.photos
    resolve(res.body.photos);
    ).fail ->
    # Well, we failed to perform an HTTP request. We're probably in an offline state.
    jobberApp.offline(1);
    if cachedData?
    return resolve(cachedData)
    else
    return reject("No internet connectivity, failed to do work!")
    )

    # Show a simple spinner
    # @global
    showSpinner = ->
    $('.spinner').show()
    Promise.resolve()

    # Hide the global spinner
    # @global
    hideSpinner = ->
    $('.spinner').hide()
    Promise.resolve()

    # Now we can **easily** fetch the photos effectively:
    showSpinner()
    .then(getPhotos)
    .then( (photos) ->

    # Do something with the photos. These can be cached photos or
    # up-to-date versions, we don't really care.
    photosEl = $ '.photos'
    containerEl = document.createElement 'div'

    for photo in photos
    el = document.createElement 'div'

    el.classLists.add 'photo'
    el.innerHTML = "<img src='#{photo.src}' alt='#{photo.alt}' />"

    # Append each photo element into a container div. This still isn't
    # touching the DOM and is used to minimize the amount of operations we perform on
    # the DOM. Instead of performing an `.append` for each photo, which is at least O(n)
    # (Not counting the DOM internals work) we now achieve O(1) which is much better.
    containerEl.append el

    # Add the container to the DOM.
    photosEl.append containerEl

    ).fail( (err) ->
    # Show a small error message to say we have failed.
    el = $('.js-errorMessage')
    el.innerHTML = err
    el.show()
    ).finally(hideSpinner)