{"action":"opened","issue":{"url":"https://api.github.com/repos/omniscientjs/immstruct/issues/44","labels_url":"https://api.github.com/repos/omniscientjs/immstruct/issues/44/labels{/name}","comments_url":"https://api.github.com/repos/omniscientjs/immstruct/issues/44/comments","events_url":"https://api.github.com/repos/omniscientjs/immstruct/issues/44/events","html_url":"https://github.com/omniscientjs/immstruct/issues/44","id":59922012,"number":44,"title":"Refactor Structure to be cursor type agnostic","user":{"login":"Dashed","id":139499,"avatar_url":"https://avatars.githubusercontent.com/u/139499?v=3","gravatar_id":"","url":"https://api.github.com/users/Dashed","html_url":"https://github.com/Dashed","followers_url":"https://api.github.com/users/Dashed/followers","following_url":"https://api.github.com/users/Dashed/following{/other_user}","gists_url":"https://api.github.com/users/Dashed/gists{/gist_id}","starred_url":"https://api.github.com/users/Dashed/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Dashed/subscriptions","organizations_url":"https://api.github.com/users/Dashed/orgs","repos_url":"https://api.github.com/users/Dashed/repos","events_url":"https://api.github.com/users/Dashed/events{/privacy}","received_events_url":"https://api.github.com/users/Dashed/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-03-05T08:50:19Z","updated_at":"2015-03-05T08:50:19Z","closed_at":null,"body":"While sketching out API for some new cursor types that extend Immutable cursors, one type uses `Structure` of a different cursor type in a meta manner. I needed something akin to immstruct Structures.\r\n\r\n So I figured that it could be configured to use a different cursor type. \r\n\r\nThis is needed for some cursor types I'm sketching out to solve the following issues in a more fundamental way: \r\n- https://github.com/omniscientjs/omniscient/issues/84 (batched updates)\r\n- https://github.com/omniscientjs/omniscient/issues/83 (tying to single source of truth instead of the cursor's)\r\n\r\n/cc @Gozala\r\n\r\n-----\r\n\r\nBy default, `Structure` would use some extended cursor type.\r\n\r\nThis is how I would envision the refactor of `Structure`:\r\n\r\n- API is largely the same; some are omitted for brevit\r\n- Reference cursor API are removed, and its feature set are shifted to a cursor type.\r\n\r\n```js\r\n// this is cursor type agnostic\r\n\r\nfunction Structure(options) {\r\n    this.state = options.state || Immutable.Map();\r\n    this.cursorType = options.cursorType || Carbon;\r\n    this.onChange = options.onChange || null;\r\n    this.updateCurrent = options.updateCurrent; // required\r\n    this.captureEvent = options.captureEvent || analyze;\r\n    this.__onCommit = function() {\r\n        self.onCommit.apply(self, arguments)\r\n    };\r\n\r\n    EventEmitter.call(this, arguments);\r\n}\r\n\r\ninherits(Structure, EventEmitter);\r\n\r\nStructure.prototype.onCommit = function() {\r\n\r\n    const oldState = this.state;\r\n    const self = this;\r\n\r\n    this.state = this._onChange.apply(this._onChange, arguments);\r\n    \r\n    const event = this.captureEvent.apply(this.captureEvent, arguments);\r\n\r\n    setTimeout(function() {\r\n        self.onChange && self.onChange();\r\n        self.emit.call(self, 'swap', state, oldState);\r\n        self.emit.apply(self, [event.name].concat(event.arguments));\r\n    }, 0);\r\n\r\n    if(!this.history) return;\r\n\r\n    // update history\r\n    this.history = this.history\r\n      .take(++this._currentRevision)\r\n      .push(this.current);\r\n}\r\n\r\nStructure.prototype.cursor = function(path) {\r\n    path = path || [];\r\n    return this.cursorType.from(this.state, path, this.__onCommit);\r\n}\r\n\r\n// this was forceHasSwapped(...)\r\n// I think this is a better name; inspired by \r\n// http://facebook.github.io/react/docs/component-api.html#replacestate\r\nStructure.prototype.replaceState = function(newState) {\r\n    this.state = newState;\r\n\r\n    const self = this;\r\n    setTimeout(function() {\r\n        self.emit.call(self, 'swap', state, oldState);\r\n    }, 0);\r\n}\r\n\r\n// all of these are the same API. but by semantics, they should call swap.\r\n// user has access to this.history anyways\r\nStructure.prototype.undo = // same ...\r\nStructure.prototype.redo = // same ...\r\nStructure.prototype.undoUntil = // same ...\r\n```"}}