Created
April 10, 2013 05:31
-
-
Save leepfrog/5352059 to your computer and use it in GitHub Desktop.
Current WIP of IndexedDB Adapter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Aliases | |
get = Ember.get | |
indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB | |
# Indexed DB Adapter | |
App.IDBAdapter = DS.Adapter.extend | |
# Name of the database store we're going to use | |
dbName: 'myCoolDB' | |
# Version of the schema | |
version: 1 | |
# Handle of the db instance | |
dbHandle: null | |
# Used to determine if we have IndexedDB capabilities | |
isSupported: false | |
# This is used to hold commands that we want to run after our database becomes ready | |
_beforeReadyQueue: null | |
### | |
Sets us up the bomb. | |
### | |
init: -> | |
@_super() | |
# Check to see if we have an IndexedDB variable (meaning the browser supports IndexedDB) | |
if indexedDB? | |
@_beforeReadyQueue = [] | |
@isSupported = true | |
@_setupDBInstance() | |
### | |
@private | |
@needsTest | |
Used for creating our IndexDB instances and registering error handlers. | |
@todo Moooove the schema, version number, etc.. outta herrreee. | |
Maybe look at something like App.Adapter.configure as an API interface | |
@method _setupDBInstance | |
### | |
_setupDBInstance: -> | |
# Open an instance to our db | |
request = indexedDB.open(@dbName, @version) | |
# Setup handlers | |
request.onerror = (e) -> console.error("IDBError: #{e.target.errorCode}") | |
request.onsuccess = (e) => | |
# Hold onto our database handle | |
@dbHandle = request.result | |
# This onerror will capture all of our bubbled up error events | |
@dbHandle.onerror = (e) -> console.error("IDBError: #{e.target.errorCode}") | |
# Finally, process anything we may have waiting for us | |
@_processBeforeReadyQueue() | |
# Our schema definition | |
request.onupgradeneeded = (e) -> | |
# We grab a local handle from the event and let our success callback set the actual handle | |
localDbHandle = e.target.result | |
# TODO: Creating object store entities (this can likely be done based on model definitions) | |
models = [ | |
'books' | |
] | |
# Create an object store for each model | |
for model in models | |
localDbHandle.createObjectStore(model, { keyPath: 'id' }) | |
### | |
# The `find()` method is invoked when the store is asked for a record that | |
# has not previously been loaded. In response to `find()` being called, you | |
# should query your persistence layer for a record with the given ID. Once | |
# found, you can asynchronously call the store's `load()` method to load | |
# the record. | |
# Here is an example `find` implementation: | |
@method find | |
@param {DS.Store} store | |
@param {subclass of DS.Model} type | |
@param {String|Number} id of record to retrieve | |
### | |
find: (store, type, id) -> | |
root = @rootForType(type) | |
plural = @pluralize(root) | |
request = @dbHandle.transaction(plural).objectStore(plural).get(id) | |
request.onsuccess = (e) => @load(store, type, e.target.result) | |
# !!! TODO: what happens when we don't find an id? | |
### | |
Here is an example `findAll` implementation: | |
@method findAll | |
### | |
findAll: (store, type) -> | |
root = @rootForType(type) | |
plural = @pluralize(root) | |
# It's possible that findAll can be called before we have an open handle on the | |
# database. As a result, we need to do the following: | |
# | |
# If we have our dbHandle, do our request | |
if @dbHandle? | |
request = @dbHandle.transaction(plural).objectStore(plural).openCursor() | |
# Our results that we'll use as we're iterating over our cursor | |
results = {} | |
results[plural]=[] | |
request.onsuccess = (e) => | |
cursor = e.target.result | |
if cursor? | |
currentResult = cursor.value | |
results[plural].push(currentResult) | |
cursor.continue() | |
else | |
@didFindAll(store, type, results) | |
# If we don't have a handle, then we check for support / enqueue a request if we're supported | |
else | |
# If we have support, great.. enqueue our request | |
if @isSupported | |
@_enqueueBeforeReady(method: 'findAll', params: arguments) | |
# If we do not, then return didFindAll with no results | |
else | |
return @didFindAll(store, type, []) | |
### | |
@private | |
This method is used whenever we are attempting to run a request before we have a handle open to | |
our database. | |
@method _enqueueBeforeReady | |
### | |
_enqueueBeforeReady: (options) -> @_beforeReadyQueue.push(options) | |
### | |
@private | |
This method will be called by our dbOpen callback to process any requests that are pending. | |
@method _processBeforeReadyQueue | |
### | |
_processBeforeReadyQueue: -> | |
for func in @_beforeReadyQueue | |
get(@, func['method']).apply(this,func['params']) | |
### | |
@needsTest | |
Used for adding objects that have been created into our IndexedDB. | |
@note I don't believe it's necessary to wrap this in a settimeout as indexeddb is already | |
async | |
@method createRecords | |
@param {DS.Store} store | |
@param {subclass of DS.Model} type | |
@param {Ember.Object} Instance of the DS.Model type | |
### | |
createRecord: (store, type, record) -> | |
root = @rootForType(type) | |
plural = @pluralize(root) | |
json = record.serialize(includeId:true) | |
# This is if we do not have a json id | |
json['id']= @_generateRecordId() if !json['id']? | |
# Persist the object | |
request = @dbHandle.transaction(plural, 'readwrite').objectStore(plural).add(json) | |
request.onsuccess = (e) => @didCreateRecord(store, type, record) | |
request.error = (e) => @didError(store, type, record) | |
### | |
@needsTest | |
Used for updating objects that have already been created in our IndexedDB. | |
@note I don't believe it's necessary to wrap this in a settimeout as indexeddb is already | |
async | |
@method createRecords | |
@param {DS.Store} store | |
@param {subclass of DS.Model} type | |
@param {Ember.Object} Instance of the DS.Model type | |
### | |
updateRecord: (store, type, record) -> | |
id = get(record, "id") | |
root = @rootForType(type) | |
plural = @pluralize(root) | |
json = record.serialize(includeId:true) | |
# Update the object | |
request = @dbHandle.transaction(plural, 'readwrite').objectStore(plural).put(json) | |
request.onsuccess = (e) => @didUpdateRecord(store, type, record) | |
request.error = (e) => @didError(store, type, record) | |
### | |
@needsTest | |
Used for deleting objects that have already been created in our IndexedDB. | |
@note I don't believe it's necessary to wrap this in a settimeout as indexeddb is already | |
async | |
@method deleteRecord | |
@param {DS.Store} store | |
@param {subclass of DS.Model} type | |
@param {Ember.Object} Instance of the DS.Model type | |
### | |
deleteRecord: (store, type, record) -> | |
id = get(record, "id") | |
root = @rootForType(type) | |
plural = @pluralize(root) | |
# Delete the object | |
request = @dbHandle.transaction(plural, 'readwrite').objectStore(plural).delete(id) | |
request.onsuccess = (e) => @didDeleteRecord(store, type, record) | |
request.error = (e) => @didError(store, type, record) | |
### | |
@needsTest | |
Used for generating a random client ID. | |
This code is ganked straight out of the Ember TodoMVC code sample. | |
@method _generateRecordId | |
### | |
_generateRecordId: -> return Math.random().toString(32).slice(2).substr(0, 5) | |
### | |
Used to obtain a string representation of a given model type. | |
This code was yanked directly from DS.RESTAdapter since it doesn't | |
exist inside of DS.Adapter for some reason. | |
@method rootForType | |
@param {subclass of DS.Model} type | |
@return {String} type name | |
### | |
rootForType: (type) -> get(this, 'serializer').rootForType(type) | |
### | |
Used to obtain a pluralized representation of a passed in string. | |
Same as rootForType, this code was yanked directly from DS.RESTAdapter | |
@method pluralize | |
@param {String} type name | |
@return {String} pluralized type name | |
### | |
pluralize: (string) -> get(this, 'serializer').pluralize(string) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment