Skip to content

Commit

Permalink
Add autoPush option
Browse files Browse the repository at this point in the history
  • Loading branch information
alekseykulikov committed Apr 13, 2012
1 parent bdb93b5 commit acfeee4
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 23 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ It uses field `sid` in order to save server’s id. This field is used when requ

Optional parameter `keys` is used when your collection has a relation with other collections. In this example local `tag_id` will be changed to necessary primary key from `@tags` when sending data to the server.

Option `autoPush` allows to send request to the server on every save. It simply works like cache for your data.

````
@storage = new Offline.Storage('dreams', this, autoPush: true)
````

### Offline.Sync

This is an algorithm for syncing local storage with a server. For inspiration was used [Evernote EDAM](http://dev.evernote.com/media/pdf/edam-sync.pdf) algorithm, but later it has been changed significantly.
Expand Down
9 changes: 7 additions & 2 deletions js/backbone_offline.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 27 additions & 4 deletions spec/javascripts/storage_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,25 +161,48 @@
this.storage.save(this.dream);
return expect(_.include(this.storage.allIds.values, 'abcd')).toBeTruthy();
});
return it('should call "replaceKeyFields"', function() {
it('should call "replaceKeyFields"', function() {
spyOn(this.storage, 'replaceKeyFields');
this.storage.save(this.dream);
return expect(this.storage.replaceKeyFields).toHaveBeenCalledWith(this.dream, 'local');
});
it("should push item if options.autoPush", function() {
this.storage.autoPush = true;
spyOn(this.storage.sync, 'pushItem');
this.storage.create(this.dream);
return expect(this.storage.sync.pushItem).toHaveBeenCalledWith(this.dream);
});
return it("should does not push item if local=true", function() {
this.storage.autoPush = true;
spyOn(this.storage.sync, 'pushItem');
this.storage.create(this.dream, {
local: true
});
return expect(this.storage.sync.pushItem.callCount).toBe(0);
});
});
describe('remove', function() {
beforeEach(function() {
this.dream = this.dreams.create({
return this.dream = this.dreams.create({
id: 'dcba'
}, {
regenerateId: true
});
return this.storage.remove(this.dream);
});
it('should remove item from localStorage', function() {
this.storage.remove(this.dream);
return expect(localStorage.getItem('dreams-dcba')).toBeNull();
});
return it('should remove item.id from @allIds', function() {
it('should remove item.id from @allIds', function() {
this.storage.remove(this.dream);
return expect(_.include(this.storage.values, 'dcba')).toBeFalsy();
});
return it("should flush item if options.autoPush", function() {
this.storage.autoPush = true;
spyOn(this.storage.sync, 'flushItem');
this.storage.remove(this.dream);
return expect(this.storage.sync.flushItem).toHaveBeenCalledWith('dcba');
});
});
describe('isEmpty', function() {
it("should return true when localStorage's key is null", function() {
Expand Down
12 changes: 6 additions & 6 deletions spec/javascripts/sync_spec.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 24 additions & 3 deletions spec/storage_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,34 @@ describe 'Offline.Storage', ->
@storage.save(@dream)
expect(@storage.replaceKeyFields).toHaveBeenCalledWith(@dream, 'local')

it "should push item if options.autoPush", ->
@storage.autoPush = true
spyOn(@storage.sync, 'pushItem')
@storage.create(@dream)
expect(@storage.sync.pushItem).toHaveBeenCalledWith(@dream)

it "should does not push item if local=true", ->
@storage.autoPush = true
spyOn(@storage.sync, 'pushItem')
@storage.create(@dream, local: true)
expect(@storage.sync.pushItem.callCount).toBe(0)

describe 'remove', ->
beforeEach ->
@dream = @dreams.create(id: 'dcba')
@dream = @dreams.create({id: 'dcba'}, {regenerateId: true})

it 'should remove item from localStorage', ->
@storage.remove(@dream)
expect(localStorage.getItem 'dreams-dcba').toBeNull()
it 'should remove item.id from @allIds', ->
@storage.remove(@dream)
expect(_.include @storage.values, 'dcba').toBeFalsy()

it 'should remove item from localStorage', -> expect(localStorage.getItem 'dreams-dcba').toBeNull()
it 'should remove item.id from @allIds', -> expect(_.include @storage.values, 'dcba').toBeFalsy()
it "should flush item if options.autoPush", ->
@storage.autoPush = true
spyOn(@storage.sync, 'flushItem')
@storage.remove(@dream)
expect(@storage.sync.flushItem).toHaveBeenCalledWith('dcba')

describe 'isEmpty', ->
it "should return true when localStorage's key is null", ->
Expand Down
12 changes: 6 additions & 6 deletions spec/sync_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ describe 'Offline.Sync', ->
@sync.push()
expect(@sync.pushItem.callCount).toBe(2)

it 'should call "destroyBySid" for destroyed items', ->
it 'should call "flushItem" for destroyed items', ->
destroyedDream = @dreams.create({id: '3', name: 'Learning to play on sax', sid: '3'}, {local: true})
destroyedDream.destroy()
spyOn(@sync, 'destroyBySid')
spyOn(@sync, 'flushItem')

@sync.push()
expect(@sync.destroyBySid.callCount).toBe(1)
expect(@sync.flushItem.callCount).toBe(1)

describe 'pushItem', ->
describe 'when item is new', ->
Expand Down Expand Up @@ -232,16 +232,16 @@ describe 'Offline.Sync', ->
expect(@dream.get 'dirty').toBeFalsy()
expect(@dream.id).toEqual(localId)

describe 'destroyBySid', ->
describe 'flushItem', ->
beforeEach ->
@sid = @dreams.create(sid: '3', local: true).get('sid')

it 'should call ajax', ->
spyOn(@sync, 'ajax')
@sync.destroyBySid(@sid)
@sync.flushItem(@sid)
expect(@sync.ajax).toHaveBeenCalledWith('delete', jasmine.any(Object), {success: jasmine.any(Function)})

it 'should clear @destroyIds', ->
registerFakeAjax url: "/api/dreams/#{@sid}", type: 'delete', successData: {}
@sync.destroyBySid(@sid)
@sync.flushItem(@sid)
expect(@storage.destroyIds.values).toEqual([])
9 changes: 7 additions & 2 deletions src/backbone_offline.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class Offline.Storage
@destroyIds = new Offline.Index("#{@name}-destroy")
@sync = new Offline.Sync(collection, this)
@keys = options.keys || {}
@autoPush = options.autoPush || false

# Add a model, giving it a unique GUID. Server id saving to "sid".
# Set a sync's attributes updated_at, dirty and add
Expand Down Expand Up @@ -97,12 +98,16 @@ class Offline.Storage
localStorage.setItem "#{@name}-#{item.id}", JSON.stringify(item)
@allIds.add(item.id)

@sync.pushItem(item) if @autoPush and !options.local
return item

remove: (item) ->
localStorage.removeItem "#{@name}-#{item.id}"
@allIds.remove(item.id)

sid = item.get('sid')
@sync.flushItem(sid) if @autoPush and sid isnt 'new'

return item

isEmpty: ->
Expand Down Expand Up @@ -204,7 +209,7 @@ class Offline.Sync
# after that it sends deleted objects to the server
push: ->
this.pushItem(item) for item in @collection.dirty()
this.destroyBySid(sid) for sid in @storage.destroyIds.values
this.flushItem(sid) for sid in @storage.destroyIds.values

pushItem: (item) ->
@storage.replaceKeyFields(item, 'server')
Expand All @@ -218,7 +223,7 @@ class Offline.Sync

item.attributes.id = localId; item.id = localId

destroyBySid: (sid) ->
flushItem: (sid) ->
model = @collection.fakeModel(sid)
this.ajax 'delete', model, success: (response, status, xhr) =>
@storage.destroyIds.remove(sid)
Expand Down

0 comments on commit acfeee4

Please sign in to comment.