Skip to content

Instantly share code, notes, and snippets.

@ianmurrays
Forked from rstacruz/model_caching.coffee
Created November 15, 2012 00:52
Show Gist options
  • Save ianmurrays/4075927 to your computer and use it in GitHub Desktop.
Save ianmurrays/4075927 to your computer and use it in GitHub Desktop.
Backbone.js model caching
# ## Backbone model caching
# This is a simple solution to the problem "how do I make less requests to
# my server?".
#
# Whenever you need a model instance, this will check first if the same thing
# has been fetched before, and gives you the same instance instead of fetching
# the same data from the server again.
#
# Reusing the same instance also has the great side-effect of making your
# model changeable from one part of your code, with your changes being available
# elsewhere in your code without doing `Model#save()`.
#
# #### Sample implementation
# In your model (let's say, `Article`), you will probably will
# create a new record this way:
#
# a = new Article;
# a.set({ title: "Hello" });
# a.save({}, {
# success: function() {
# this.cache();
# }
# });
#
# console.log(a.id); //=> 2 [from the server]
# console.log(a.cid); //=> "c4" [from the client]
#
# Elsewhere in your code, you can use `Model.fetch()` to retrieve the
# same instance. Notice how the client id (`.cid`) is the same as what
# we had before.
#
# a = Article.fetch(2);
#
# console.log(a.id); //=> 2 [from the server]
# console.log(a.cid); //=> "c4" [from the client]
#
class Cache
constructor: ->
@clear()
set: (id, block) ->
@table[@_idString id] = @_use block
unset: (id) ->
delete @table[@_idString id]
get: (id, block) ->
@table[@_idString id] ||= (if block then @_use(block) else null)
clear: ->
@table = {}
# Uses a given object.
_use: (obj) ->
if typeof obj is "function" then obj() else obj
# Gets a string from any object to be used as a cache key.
_idString: (obj) ->
if typeof obj is "string"
obj
else if typeof obj is "function"
if obj.name isnt "" then obj.name else obj.toString()
else if obj.constructor is Array
_.map(obj, (item) => @_idString item).join(":")
else if typeof obj is "number"
obj
else
_.map(obj, (k, v) => "#{@_idString k}:#{@_idString v}").join(":")
cache = new Cache
# ### Model.cache [attribute]
# Returns the cache object.
Backbone.Model.cache = cache
# ### Model.fetch(id, args...) [class method]
# Fetches a model ID from either the cache or the server. The `id` parameter
# will be the ID of the record you want to fetch, and `args` will be passed
# onto Backbone's `Model#fetch` (often the `success` and `error` callbacks).
#
# # Examples:
# Document.fetch(2);
# Document.fetch(2, { success: function() { ... } });
#
Backbone.Model.fetch = (id, args...) ->
cache.get [this, id], =>
item = new this
item.id = id
item.fetch.apply item, args
item
# ### Model#cache() [method]
# Caches the model instance so `Model.fetch` will retrieve it.
# Add this to your save success callback.
Backbone.Model::cache = ->
cache.set [this, @id], this
# ### Model>uncache() [method]
# Ensures that the item will not be accessible anymore.
# Add this to your delete success callback.
Backbone.Model::uncache = ->
cache.unset [this, @id]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment