Skip to content
This repository has been archived by the owner on Apr 11, 2023. It is now read-only.

Set options.dirty to true if reading locally due to dirty collection #105

Closed
elad opened this issue Apr 10, 2014 · 12 comments
Closed

Set options.dirty to true if reading locally due to dirty collection #105

elad opened this issue Apr 10, 2014 · 12 comments
Assignees

Comments

@elad
Copy link
Contributor

elad commented Apr 10, 2014

The documentation states that if a collection is dirty, fetch will read from localStorage. This is fine, but similar to how other places set options.dirty to true to indicate the operation happened locally, I feel the same should happen here. Then the pattern of testing options.dirty in the success callback is consistent across all CRUD methods.

It looks like this is a simple change in dualsync to add options.dirty = true below the if localsync('hasDirtyOrDestroyed', model, options) line.

Comments?

@nilbus
Copy link
Owner

nilbus commented Apr 10, 2014

I'm in favor of this.

@elad
Copy link
Contributor Author

elad commented Apr 15, 2014

Here's a diff: elad/Backbone.dualStorage@master...remote_local_fixes

The base is my master branch which already has the fixes discussed in the other issue (offline status handling) since I need it for my app.

What it does:

  • Adds options.dirty = true for "dirty" reads from localStorage as suggested above
  • Exposes hasDirtyOrDestroyed, a function returning true if the collection has any dirty/destroyed models or false otherwise
  • Adds discard, a function that takes a string (dirty/destroyed) or nothing (both) and discards any such models
  • Allows specifying remote or local in the options for individual requests. The use case here for me is importing data. It doesn't make sense to save it to localStorage and say the operation was successful since the very nature of the operation is to persist the data to remote storage.

@nilbus nilbus self-assigned this Apr 30, 2014
nilbus added a commit that referenced this issue May 10, 2014
@nilbus nilbus closed this as completed in 6aa0f07 May 10, 2014
@elad
Copy link
Contributor Author

elad commented May 10, 2014

Thanks! (I was about to look into this today since I remembered seeing this feature documented but not implemented!)

@nilbus
Copy link
Owner

nilbus commented May 10, 2014

Hang tight, I'm rewriting the merge since I messed something up. :P

nilbus added a commit that referenced this issue May 10, 2014
nilbus added a commit that referenced this issue May 10, 2014
@nilbus
Copy link
Owner

nilbus commented May 10, 2014

I cherry-picked the options.dirty commit and fixed it so that options.dirty gets set when the browser is offline (related to #104).

I think it would be better to implement hasDirtyOrDestroyed and discard in terms of Store, rather than using localStorage directly. Store already has a hasDirtyOrDestroyed method you can use, and you can move the implementation of discard there too. It's important to isolate dependencies like localStorage in a single class; this is Store's purpose. #73 actually swaps out localStorage in favor of IndexedDB; resolving this conflict will be easier if only Store references localStorage.

@nilbus
Copy link
Owner

nilbus commented May 10, 2014

Also for these methods please add integration tests (use the external API and don't mock anything) so that we can ensure they continue to work after the backend is switched.

@elad
Copy link
Contributor Author

elad commented May 10, 2014

You're right about the importance of abstraction here, I completely forgot about the 2.5mb localStorage limit... I'll take care of it and add the relevant tests.

By the way, I'm not sure hasDirtyOrDestroyed is necessary - I ended up not using it and prefer to keep the API as clean as possible.

@nilbus
Copy link
Owner

nilbus commented May 10, 2014

okay​. Feel free to remove that.

@nilbus
Copy link
Owner

nilbus commented May 10, 2014

Released in 1.3.0

@elad
Copy link
Contributor Author

elad commented Jun 3, 2014

Hey,

Sorry for getting back to this after so long...

When you talk about implementing discard and hasDirtyOrDestroyed in terms of Store, how do you suggest exposing these functions? collection.sync('clear', null, { local: true });?

(I'm asking because the implementation of all functions on Backbone.Collection directly access localStorage...)

@nilbus
Copy link
Owner

nilbus commented Jun 3, 2014

I wouldn't want to expose anything new on the collection. My thought was to make Store the only thing that references localStorage and the only thing that knows about the suffixes '_dirty' and '_destroyed'. Then this knowledge is in one place instead of being spread out across all the code. Store would need to have a couple other methods such as dirtyRecords and destroyedRecords that would return the corresponding list of ids for the current store.

With Store implemented like this, then all the collection methods can be implemented as such, not needing to know about localStorage or the suffixes:

dirty = new Store(getStoreName(this)).dirtyRecords()
ids = (dirty and dirty.split(',')) or []

@elad
Copy link
Contributor Author

elad commented Jun 3, 2014

Do you mean something along these lines?

Collection:

Backbone.Collection.prototype.dirtyRecords = ->
  new Store(getStoreName(this)).dirtyRecords()

Backbone.Collection.prototype.destroyedRecords = ->
  new Store(getStoreName(this)).destroyedRecords()

Backbone.Collection.prototype.hasDirtyOrDestroyed = ->
  new Store(getStoreName(this)).hasDirtyOrDestroyed()

Backbone.Collection.prototype.discard = (what) ->
  new Store(getStoreName(this)).discard(what)

Store:

  # Return an array of dirty model ids.
  dirtyRecords: ->
    @recordsOn @name + '_dirty'

  # Return an array of destroyed model ids.
  destroyedRecords: ->
    @recordsOn @name + '_destroyed'

  # Discard dirty and/or destroyed models.
  discard: (what) ->
    if not what or what is 'dirty'
      ids = @dirtyRecords()
      for id in ids
        localStorage.removeItem @name + @sep + id
      localStorage.setItem @name + '_dirty', []
    if not what or what is 'destroyed'
      ids = @destroyedRecords()
      for id in ids
        localStorage.removeItem @name + @sep + id
      localStorage.setItem @name + '_destroyed', []
    @

(edited to add missing functions discussed above.)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants