ar_sync 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -2
  3. data/README.md +6 -6
  4. data/ar_sync.gemspec +2 -2
  5. data/bin/console +1 -2
  6. data/core/ActioncableAdapter.d.ts +26 -2
  7. data/core/ActioncableAdapter.js +3 -3
  8. data/core/ArSyncApi.d.ts +7 -4
  9. data/core/ArSyncApi.js +9 -4
  10. data/core/{ArSyncModelBase.d.ts → ArSyncModel.d.ts} +13 -18
  11. data/core/{ArSyncModelBase.js → ArSyncModel.js} +33 -10
  12. data/{graph → core}/ArSyncStore.d.ts +3 -3
  13. data/{graph → core}/ArSyncStore.js +188 -57
  14. data/core/DataType.d.ts +17 -13
  15. data/core/hooks.d.ts +28 -0
  16. data/core/hooks.js +105 -0
  17. data/index.d.ts +2 -0
  18. data/index.js +6 -0
  19. data/lib/ar_sync.rb +1 -18
  20. data/lib/ar_sync/class_methods.rb +31 -89
  21. data/lib/ar_sync/collection.rb +4 -29
  22. data/lib/ar_sync/core.rb +35 -67
  23. data/lib/ar_sync/instance_methods.rb +40 -86
  24. data/lib/ar_sync/rails.rb +18 -27
  25. data/lib/ar_sync/type_script.rb +39 -18
  26. data/lib/ar_sync/version.rb +1 -1
  27. data/lib/generators/ar_sync/install/install_generator.rb +33 -32
  28. data/lib/generators/ar_sync/types/types_generator.rb +6 -3
  29. data/package-lock.json +21 -10
  30. data/package.json +1 -1
  31. data/src/core/ActioncableAdapter.ts +28 -3
  32. data/src/core/ArSyncApi.ts +8 -4
  33. data/src/core/{ArSyncModelBase.ts → ArSyncModel.ts} +51 -20
  34. data/src/{graph → core}/ArSyncStore.ts +199 -84
  35. data/src/core/DataType.ts +33 -20
  36. data/src/core/hooks.ts +108 -0
  37. data/src/index.ts +2 -0
  38. data/vendor/assets/javascripts/{ar_sync_tree.js.erb → ar_sync.js.erb} +6 -7
  39. metadata +33 -38
  40. data/core/hooksBase.d.ts +0 -29
  41. data/core/hooksBase.js +0 -80
  42. data/graph/ArSyncModel.d.ts +0 -10
  43. data/graph/ArSyncModel.js +0 -22
  44. data/graph/hooks.d.ts +0 -3
  45. data/graph/hooks.js +0 -10
  46. data/graph/index.d.ts +0 -2
  47. data/graph/index.js +0 -4
  48. data/src/core/hooksBase.ts +0 -86
  49. data/src/graph/ArSyncModel.ts +0 -21
  50. data/src/graph/hooks.ts +0 -7
  51. data/src/graph/index.ts +0 -2
  52. data/src/tree/ArSyncModel.ts +0 -145
  53. data/src/tree/ArSyncStore.ts +0 -323
  54. data/src/tree/hooks.ts +0 -7
  55. data/src/tree/index.ts +0 -2
  56. data/tree/ArSyncModel.d.ts +0 -39
  57. data/tree/ArSyncModel.js +0 -143
  58. data/tree/ArSyncStore.d.ts +0 -21
  59. data/tree/ArSyncStore.js +0 -365
  60. data/tree/hooks.d.ts +0 -3
  61. data/tree/hooks.js +0 -10
  62. data/tree/index.d.ts +0 -2
  63. data/tree/index.js +0 -4
  64. data/vendor/assets/javascripts/ar_sync_graph.js.erb +0 -17
@@ -1,10 +0,0 @@
1
- import ArSyncModelBase from '../core/ArSyncModelBase';
2
- import ConnectionAdapter from '../core/ConnectionAdapter';
3
- export default class ArSyncModel<T> extends ArSyncModelBase<T> {
4
- static setConnectionAdapter(adapter: ConnectionAdapter): void;
5
- static createRefModel(request: any, option: any): any;
6
- refManagerClass(): typeof ArSyncModel;
7
- connectionManager(): any;
8
- static _cache: {};
9
- static cacheTimeout: number;
10
- }
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const ArSyncStore_1 = require("./ArSyncStore");
4
- const ConnectionManager_1 = require("../core/ConnectionManager");
5
- const ArSyncModelBase_1 = require("../core/ArSyncModelBase");
6
- class ArSyncModel extends ArSyncModelBase_1.default {
7
- static setConnectionAdapter(adapter) {
8
- ArSyncStore_1.default.connectionManager = new ConnectionManager_1.default(adapter);
9
- }
10
- static createRefModel(request, option) {
11
- return new ArSyncStore_1.default(request, option);
12
- }
13
- refManagerClass() {
14
- return ArSyncModel;
15
- }
16
- connectionManager() {
17
- return ArSyncStore_1.default.connectionManager;
18
- }
19
- }
20
- ArSyncModel._cache = {};
21
- ArSyncModel.cacheTimeout = 10 * 1000;
22
- exports.default = ArSyncModel;
@@ -1,3 +0,0 @@
1
- export { useArSyncFetch } from '../core/hooksBase';
2
- import { Request, DataAndStatus } from '../core/hooksBase';
3
- export declare function useArSyncModel<T>(request: Request | null): DataAndStatus<T>;
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- var hooksBase_1 = require("../core/hooksBase");
4
- exports.useArSyncFetch = hooksBase_1.useArSyncFetch;
5
- const hooksBase_2 = require("../core/hooksBase");
6
- const ArSyncModel_1 = require("./ArSyncModel");
7
- function useArSyncModel(request) {
8
- return hooksBase_2.useArSyncModelWithClass(ArSyncModel_1.default, request);
9
- }
10
- exports.useArSyncModel = useArSyncModel;
@@ -1,2 +0,0 @@
1
- import ArSyncModel from './ArSyncModel';
2
- export default ArSyncModel;
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const ArSyncModel_1 = require("./ArSyncModel");
4
- exports.default = ArSyncModel_1.default;
@@ -1,86 +0,0 @@
1
- import { useState, useEffect, useCallback } from 'react'
2
- import ArSyncAPI from './ArSyncApi'
3
-
4
- interface ModelStatus { complete: boolean; notfound?: boolean; connected: boolean }
5
- export type DataAndStatus<T> = [T | null, ModelStatus]
6
- export interface Request { api: string; params?: any; query: any }
7
-
8
- interface ArSyncModel<T> {
9
- data: T | null
10
- complete: boolean
11
- connected: boolean
12
- notfound?: boolean
13
- release(): void
14
- subscribe(type: any, callback: any): any
15
- }
16
- export function useArSyncModelWithClass<T>(modelClass: { new<T>(req: Request, option?: any): ArSyncModel<T> }, request: Request | null): DataAndStatus<T> {
17
- const [data, setData] = useState<T | null>(null)
18
- const [status, setStatus] = useState<ModelStatus>({ complete: false, connected: true })
19
- const updateStatus = (complete: boolean, notfound: boolean | undefined, connected: boolean) => {
20
- if (complete === status.complete || notfound === status.notfound || connected === status.notfound) return
21
- setStatus({ complete, notfound, connected })
22
- }
23
- useEffect(() => {
24
- if (!request) return () => {}
25
- const model = new modelClass<T>(request, { immutable: true })
26
- if (model.complete) setData(model.data)
27
- updateStatus(model.complete, model.notfound, model.connected)
28
- model.subscribe('change', () => {
29
- updateStatus(model.complete, model.notfound, model.connected)
30
- setData(model.data)
31
- })
32
- model.subscribe('connection', () => {
33
- updateStatus(model.complete, model.notfound, model.connected)
34
- })
35
- return () => model.release()
36
- }, [JSON.stringify(request && request.params)])
37
- return [data, status]
38
- }
39
-
40
-
41
- interface FetchStatus { complete: boolean; notfound?: boolean }
42
- type DataAndStatusAndUpdater<T> = [T | null, FetchStatus, () => void]
43
- export function useArSyncFetch<T>(request: Request | null): DataAndStatusAndUpdater<T> {
44
- const [response, setResponse] = useState<T | null>(null)
45
- const [status, setStatus] = useState<FetchStatus>({ complete: false })
46
- const requestString = JSON.stringify(request && request.params)
47
- let canceled = false
48
- let timer: number | null = null
49
- const update = useCallback(() => {
50
- if (!request) {
51
- setStatus({ complete: false, notfound: undefined })
52
- return () => {}
53
- }
54
- canceled = false
55
- timer = null
56
- const fetch = (count: number) => {
57
- if (timer) clearTimeout(timer)
58
- timer = null
59
- ArSyncAPI.fetch(request)
60
- .then((response) => {
61
- if (canceled) return
62
- setResponse(response as T)
63
- setStatus({ complete: true, notfound: false })
64
- })
65
- .catch(e => {
66
- if (canceled) return
67
- if (!e.retry) {
68
- setResponse(null)
69
- setStatus({ complete: true, notfound: true })
70
- return
71
- }
72
- timer = setTimeout(() => fetch(count + 1), 1000 * Math.min(4 ** count, 30))
73
- })
74
- }
75
- fetch(0)
76
- }, [requestString])
77
- useEffect(() => {
78
- update()
79
- return () => {
80
- canceled = true
81
- if (timer) clearTimeout(timer)
82
- timer = null
83
- }
84
- }, [requestString])
85
- return [response, status, update]
86
- }
@@ -1,21 +0,0 @@
1
- import ArSyncStore from './ArSyncStore'
2
- import ArSyncConnectionManager from '../core/ConnectionManager'
3
- import ArSyncModelBase from '../core/ArSyncModelBase'
4
- import ConnectionAdapter from '../core/ConnectionAdapter'
5
-
6
- export default class ArSyncModel<T> extends ArSyncModelBase<T> {
7
- static setConnectionAdapter(adapter: ConnectionAdapter) {
8
- ArSyncStore.connectionManager = new ArSyncConnectionManager(adapter)
9
- }
10
- static createRefModel(request, option): any {
11
- return new ArSyncStore(request, option)
12
- }
13
- refManagerClass() {
14
- return ArSyncModel
15
- }
16
- connectionManager() {
17
- return ArSyncStore.connectionManager
18
- }
19
- static _cache = {}
20
- static cacheTimeout = 10 * 1000
21
- }
@@ -1,7 +0,0 @@
1
- export { useArSyncFetch } from '../core/hooksBase'
2
- import { useArSyncModelWithClass, Request, DataAndStatus } from '../core/hooksBase'
3
- import ArSyncModel from './ArSyncModel'
4
-
5
- export function useArSyncModel<T>(request: Request | null): DataAndStatus<T> {
6
- return useArSyncModelWithClass<T>(ArSyncModel, request)
7
- }
@@ -1,2 +0,0 @@
1
- import ArSyncModel from './ArSyncModel'
2
- export default ArSyncModel
@@ -1,145 +0,0 @@
1
- import ArSyncStore from './ArSyncStore'
2
- import ArSyncAPI from '../core/ArSyncApi'
3
- import ArSyncConnectionManager from '../core/ConnectionManager'
4
- import ArSyncModelBase from '../core/ArSyncModelBase'
5
- import ConnectionAdapter from '../core/ConnectionAdapter'
6
-
7
- class ArSyncRecord {
8
- immutable
9
- request
10
- subscriptions
11
- store
12
- retryLoadTimer
13
- data
14
- bufferTimer
15
- bufferedPatches
16
- eventListeners
17
- networkSubscription
18
- complete: boolean
19
- notfound?: boolean
20
- static connectionManager
21
- constructor(request, option = {} as { immutable?: boolean }) {
22
- this.immutable = option.immutable ? true : false
23
- this.request = request
24
- this.subscriptions = []
25
- this.store = null
26
- this.data = null
27
- this.complete = false
28
- this.bufferedPatches = []
29
- this.eventListeners = { events: {}, serial: 0 }
30
- this.networkSubscription = ArSyncRecord.connectionManager.subscribeNetwork((status) => {
31
- if (this.notfound) {
32
- this.trigger('connection', status)
33
- return
34
- }
35
- if (status) {
36
- this.load(() => {
37
- this.trigger('connection', status)
38
- this.trigger('change', { path: [], value: this.data })
39
- })
40
- } else {
41
- this.unsubscribeAll()
42
- this.trigger('connection', false)
43
- }
44
- })
45
- this.load(() => {
46
- this.trigger('load')
47
- this.trigger('change', { path: [], value: this.data })
48
- })
49
- }
50
- release() {
51
- this.unsubscribeAll()
52
- this.networkSubscription.unsubscribe()
53
- }
54
- unsubscribeAll() {
55
- if (this.retryLoadTimer) clearTimeout(this.retryLoadTimer)
56
- for (const s of this.subscriptions) s.unsubscribe()
57
- this.subscriptions = []
58
- }
59
- load(callback, retryCount = 0) {
60
- ArSyncAPI.syncFetch(this.request).then(syncData => {
61
- const { keys, data, limit, order } = syncData as any
62
- this.initializeStore(keys, data, { limit, order, immutable: this.immutable })
63
- if (callback) callback(true, this.data)
64
- }).catch(e => {
65
- console.error(e)
66
- if (e.retry) {
67
- this.retryLoad(callback, retryCount + 1)
68
- } else {
69
- this.initializeStore(null, null, null)
70
- if (callback) callback(false, this.data)
71
- }
72
- })
73
- }
74
- retryLoad(callback, retryCount) {
75
- const sleepSeconds = Math.min(Math.pow(2, retryCount), 30)
76
- this.retryLoadTimer = setTimeout(() => {
77
- this.retryLoadTimer = null
78
- this.load(callback, retryCount)
79
- }, sleepSeconds * 1000)
80
- }
81
- patchReceived(patch) {
82
- const buffer = this.bufferedPatches
83
- buffer.push(patch)
84
- if (this.bufferTimer) return
85
- this.bufferTimer = setTimeout(() => {
86
- this.bufferTimer = null
87
- this.bufferedPatches
88
- const buf = this.bufferedPatches
89
- this.bufferedPatches = []
90
- const { changes, events } = this.store.batchUpdate(buf)
91
- this.data = this.store.data
92
- changes.forEach(change => this.trigger('change', change))
93
- events.forEach(event => {
94
- this.trigger(event.type, event.data)
95
- })
96
- }, 16)
97
- }
98
- subscribe(event, callback) {
99
- let listeners = this.eventListeners.events[event]
100
- if (!listeners) this.eventListeners.events[event] = listeners = {}
101
- const id = this.eventListeners.serial++
102
- listeners[id] = callback
103
- return { unsubscribe: () => { delete listeners[id] } }
104
- }
105
- trigger(event, arg?) {
106
- const listeners = this.eventListeners.events[event]
107
- if (!listeners) return
108
- for (const id in listeners) listeners[id](arg)
109
- }
110
- initializeStore(keys, data, option) {
111
- this.complete = true
112
- if (!keys) {
113
- this.notfound = true
114
- return
115
- }
116
- this.notfound = false
117
- const query = this.request.query
118
- if (this.store) {
119
- this.store.replaceData(data)
120
- } else {
121
- this.store = new ArSyncStore(query, data, option)
122
- this.data = this.store.data
123
- }
124
- this.subscriptions = keys.map(key => {
125
- return ArSyncRecord.connectionManager.subscribe(key, patch => this.patchReceived(patch))
126
- })
127
- }
128
- }
129
-
130
- export default class ArSyncModel<T> extends ArSyncModelBase<T> {
131
- static setConnectionAdapter(adapter: ConnectionAdapter) {
132
- ArSyncRecord.connectionManager = new ArSyncConnectionManager(adapter)
133
- }
134
- static createRefModel(request, option) {
135
- return new ArSyncRecord(request, option)
136
- }
137
- refManagerClass() {
138
- return ArSyncModel
139
- }
140
- connectionManager() {
141
- return ArSyncRecord.connectionManager
142
- }
143
- static _cache = {}
144
- static cacheTimeout = 10 * 1000
145
- }
@@ -1,323 +0,0 @@
1
- class Updator {
2
- changes
3
- markedForFreezeObjects
4
- immutable
5
- data
6
- constructor(immutable) {
7
- this.changes = []
8
- this.markedForFreezeObjects = []
9
- this.immutable = immutable
10
- }
11
- static createFrozenObject(obj) {
12
- if (!obj) return obj
13
- if (obj.constructor === Array) {
14
- obj = obj.map(el => Updator.createFrozenObject(el))
15
- } else if (typeof obj === 'object') {
16
- obj = Object.assign({}, obj)
17
- for (const key in obj) {
18
- obj[key] = Updator.createFrozenObject(obj[key])
19
- }
20
- }
21
- Object.freeze(obj)
22
- return obj
23
- }
24
- replaceData(data, newData) {
25
- if (this.immutable) return Updator.createFrozenObject(newData)
26
- return this.recursivelyReplaceData(data, newData)
27
- }
28
- recursivelyReplaceData(data, newData) {
29
- const replaceArray = (as, bs) => {
30
- const aids = {}
31
- for (const a of as) {
32
- if (!a.id) return false
33
- aids[a.id] = a
34
- }
35
- const order = {}
36
- bs.forEach((b, i) => {
37
- if (!b.id) return false
38
- if (aids[b.id]) {
39
- replaceObject(aids[b.id], b)
40
- } else {
41
- as.push(b)
42
- }
43
- order[b.id] = i + 1
44
- })
45
- as.sort((a, b) => {
46
- const oa = order[a.id] || Infinity
47
- const ob = order[b.id] || Infinity
48
- return oa > ob ? +1 : oa < ob ? -1 : 0
49
- })
50
- while (as.length && !order[as[as.length - 1].id]) as.pop()
51
- return true
52
- }
53
- const replaceObject = (aobj, bobj) => {
54
- const keys = {}
55
- for (const key in aobj) keys[key] = true
56
- for (const key in bobj) keys[key] = true
57
- for (const key in keys) {
58
- const a = aobj[key]
59
- const b = bobj[key]
60
- if ((a instanceof Array) && (b instanceof Array)) {
61
- if (!replaceArray(a, b)) aobj[key] = b
62
- } else if(a && b && (typeof a === 'object') && (typeof b === 'object') && !(a instanceof Array) && !(b instanceof Array)) {
63
- replaceObject(a, b)
64
- } else if (a !== b) {
65
- aobj[key] = b
66
- }
67
- }
68
- }
69
- replaceObject(data, newData)
70
- return data
71
- }
72
- mark(obj) {
73
- if (!this.immutable) return obj
74
- if (!Object.isFrozen(this.data)) return obj
75
- const mobj = (obj.constructor === Array) ? [...obj] : { ...obj }
76
- this.markedForFreezeObjects.push(mobj)
77
- return mobj
78
- }
79
- trace(data, path) {
80
- path.forEach(key => {
81
- if (this.immutable) data[key] = this.mark(data[key])
82
- data = data[key]
83
- })
84
- return data
85
- }
86
- assign(el, path, column, value, orderParam) {
87
- if (this.immutable) value = Updator.createFrozenObject(value)
88
- if (el.constructor === Array && !el[column]) {
89
- this.changes.push({
90
- path: path.concat([value.id]),
91
- target: el,
92
- id: value.id,
93
- valueWas: null,
94
- value
95
- })
96
- const limitReached = orderParam && orderParam.limit != null && el.length === orderParam.limit
97
- let removed
98
- if (orderParam && orderParam.order == 'desc') {
99
- el.unshift(value)
100
- if (limitReached) removed = el.pop()
101
- } else {
102
- el.push(value)
103
- if (limitReached) removed = el.pop()
104
- }
105
- if (removed) this.changes.push({
106
- path: path.concat([removed.id]),
107
- target: el,
108
- id: removed.id,
109
- valueWas: removed,
110
- value: null
111
- })
112
- } else if (!this.valueEquals(el[column], value)) {
113
- this.changes.push({
114
- path: path.concat([column]),
115
- target: el,
116
- column: column,
117
- valueWas: el[column],
118
- value
119
- })
120
- el[column] = value
121
- }
122
- }
123
- valueEquals(a, b) {
124
- if (a === b) return true
125
- if (!a || !b) return a == b
126
- if (typeof a !== 'object') return false
127
- if (typeof b !== 'object') return false
128
- const ja = JSON.stringify(a)
129
- const jb = JSON.stringify(b)
130
- return ja === jb
131
- }
132
- add(tree, accessKeys, path, column, value, orderParam) {
133
- const root = this.mark(tree)
134
- const data = this.trace(root, accessKeys)
135
- if (data) this.assign(data, path, column, value, orderParam)
136
- return root
137
- }
138
- remove(tree, accessKeys, path, column) {
139
- const root = this.mark(tree)
140
- let data = this.trace(root, accessKeys)
141
- if (!data) return root
142
- if (data.constructor === Array) {
143
- this.changes.push({
144
- path: path.concat([data[column].id]),
145
- target: data,
146
- id: data[column].id,
147
- valueWas: data[column],
148
- value: null
149
- })
150
- data.splice(column, 1)
151
- } else if (data[column] !== null) {
152
- this.changes.push({
153
- path: path.concat([column]),
154
- target: data,
155
- column: column,
156
- valueWas: data[column],
157
- value: null
158
- })
159
- data[column] = null
160
- }
161
- return root
162
- }
163
- cleanup() {
164
- this.markedForFreezeObjects.forEach(mobj => Object.freeze(mobj))
165
- }
166
- }
167
-
168
- export default class ArSyncStore {
169
- data
170
- query
171
- immutable
172
- constructor(query, data, option = {} as { immutable?: boolean }) {
173
- this.data = option.immutable ? Updator.createFrozenObject(data) : data
174
- this.query = ArSyncStore.parseQuery(query)
175
- this.immutable = option.immutable
176
- }
177
- replaceData(data) {
178
- this.data = new Updator(this.immutable).replaceData(this.data, data)
179
- }
180
- batchUpdate(patches) {
181
- const events = []
182
- const updator = new Updator(this.immutable)
183
- patches.forEach(patch => this._update(patch, updator, events))
184
- updator.cleanup()
185
- return { changes: updator.changes, events }
186
- }
187
- update(patch) {
188
- return this.batchUpdate([patch])
189
- }
190
- _slicePatch(patchData, query) {
191
- const obj = {}
192
- for (const key in patchData) {
193
- if (key === 'id' || query.attributes['*']) {
194
- obj[key] = patchData[key]
195
- } else {
196
- const subq = query.attributes[key]
197
- if (subq) {
198
- obj[subq.column || key] = patchData[key]
199
- }
200
- }
201
- }
202
- return obj
203
- }
204
- _applyPatch(data, accessKeys, actualPath, updator, query, patchData) {
205
- for (const key in patchData) {
206
- const subq = query.attributes[key]
207
- const value = patchData[key]
208
- if (subq || query.attributes['*']) {
209
- const subcol = (subq && subq.column) || key
210
- if (data[subcol] !== value) {
211
- this.data = updator.add(this.data, accessKeys, actualPath, subcol, value)
212
- }
213
- }
214
- }
215
- }
216
- _update(patch, updator, events) {
217
- const { action, path } = patch
218
- const patchData = patch.data
219
- let query = this.query
220
- let data = this.data
221
- const actualPath: (string | number)[] = []
222
- const accessKeys: (string | number)[] = []
223
- for (let i = 0; i < path.length - 1; i++) {
224
- const nameOrId = path[i]
225
- if (typeof(nameOrId) === 'number') {
226
- const idx = data.findIndex(o => o.id === nameOrId)
227
- if (idx < 0) return
228
- actualPath.push(nameOrId)
229
- accessKeys.push(idx)
230
- data = data[idx]
231
- } else {
232
- const { attributes } = query
233
- if (!attributes[nameOrId]) return
234
- const column = attributes[nameOrId].column || nameOrId
235
- query = attributes[nameOrId]
236
- actualPath.push(column)
237
- accessKeys.push(column)
238
- data = data[column]
239
- }
240
- if (!data) return
241
- }
242
- const nameOrId = path[path.length - 1]
243
- let id, idx, column, target = data
244
- if (typeof(nameOrId) === 'number') {
245
- id = nameOrId
246
- idx = data.findIndex(o => o.id === id)
247
- target = data[idx]
248
- } else if (nameOrId) {
249
- const { attributes } = query
250
- if (!attributes[nameOrId]) return
251
- column = attributes[nameOrId].column || nameOrId
252
- query = attributes[nameOrId]
253
- target = data[column]
254
- }
255
- if (action === 'create') {
256
- const obj = this._slicePatch(patchData, query)
257
- if (column) {
258
- this.data = updator.add(this.data, accessKeys, actualPath, column, obj)
259
- } else if (!target) {
260
- const ordering = Object.assign({}, patch.ordering)
261
- const limitOverride = query.params && query.params.limit
262
- ordering.order = query.params && query.params.order || ordering.order
263
- if (ordering.limit == null || limitOverride != null && limitOverride < ordering.limit) ordering.limit = limitOverride
264
- this.data = updator.add(this.data, accessKeys, actualPath, data.length, obj, ordering)
265
- }
266
- return
267
- }
268
- if (action === 'destroy') {
269
- if (column) {
270
- this.data = updator.remove(this.data, accessKeys, actualPath, column)
271
- } else if (idx >= 0) {
272
- this.data = updator.remove(this.data, accessKeys, actualPath, idx)
273
- }
274
- return
275
- }
276
- if (!target) return
277
- if (column) {
278
- actualPath.push(column)
279
- accessKeys.push(column)
280
- } else if (id) {
281
- actualPath.push(id)
282
- accessKeys.push(idx)
283
- }
284
- if (action === 'update') {
285
- this._applyPatch(target, accessKeys, actualPath, updator, query, patchData)
286
- } else {
287
- const eventData = { target, path: actualPath, data: patchData.data }
288
- events.push({ type: patchData.type, data: eventData })
289
- }
290
- }
291
-
292
- static parseQuery(query, attrsonly?){
293
- const attributes = {}
294
- let column = null
295
- let params = null
296
- if (query.constructor !== Array) query = [query]
297
- for (const arg of query) {
298
- if (typeof(arg) === 'string') {
299
- attributes[arg] = {}
300
- } else if (typeof(arg) === 'object') {
301
- for (const key in arg){
302
- const value = arg[key]
303
- if (attrsonly) {
304
- attributes[key] = this.parseQuery(value)
305
- continue
306
- }
307
- if (key === 'attributes') {
308
- const child = this.parseQuery(value, true)
309
- for (const k in child) attributes[k] = child[k]
310
- } else if (key === 'as') {
311
- column = value
312
- } else if (key === 'params') {
313
- params = value
314
- } else {
315
- attributes[key] = this.parseQuery(value)
316
- }
317
- }
318
- }
319
- }
320
- if (attrsonly) return attributes
321
- return { attributes, column, params }
322
- }
323
- }