ar_sync 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
- }