@onehat/data 1.5.2 → 1.6.4

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.
@@ -27,6 +27,14 @@ const GroupsUsers = {
27
27
 
28
28
  },
29
29
 
30
+ entity: {
31
+ methods: {
32
+ testMethod: function() {
33
+ this.users__login_count = this.users__login_count +1;
34
+ },
35
+ },
36
+ },
37
+
30
38
  repository: 'onebuild',
31
39
 
32
40
  };
@@ -17,6 +17,13 @@ describe('Entity', function() {
17
17
  { name: 'baz', mapping: 'baz.test.val', type: 'bool', defaultValue: null, },
18
18
  ],
19
19
  },
20
+ entity: {
21
+ methods: {
22
+ testMethod: function() {
23
+ this.bar = 'test me';
24
+ },
25
+ },
26
+ },
20
27
  });
21
28
  this.data = {
22
29
  foo: 1,
@@ -44,7 +51,7 @@ describe('Entity', function() {
44
51
  expect(this.entity.id).to.be.eq(1);
45
52
  });
46
53
 
47
- it('createId, isTempId', function() {
54
+ it('createTempId, isTempId', function() {
48
55
  const schema = new Schema({
49
56
  name: 'baz',
50
57
  model: {
@@ -62,35 +69,15 @@ describe('Entity', function() {
62
69
  entity = new Entity(schema, data);
63
70
  entity.initialize();
64
71
  expect(entity.id).to.be.null;
72
+ expect(entity.isTempId).to.be.false;
65
73
 
66
- entity.createId();
74
+ entity.createTempId();
67
75
  expect(entity.id).to.be.not.null;
68
- expect(Entity.isTempId(entity.id)).to.be.true;
69
-
76
+ expect(entity.isTempId).to.be.true;
70
77
 
71
-
72
- const schema2 = new Schema({
73
- name: 'baz',
74
- model: {
75
- idProperty: 'foo',
76
- displayProperty: 'bar',
77
- properties: [
78
- { name: 'foo', type: 'uuid' }, // MOD
79
- { name: 'bar' },
80
- ],
81
- },
82
- }),
83
- entity2 = new Entity(schema2, data);
84
-
85
- entity2.initialize();
86
- expect(entity2.id).to.be.null;
87
-
88
- entity2.createId();
89
- expect(entity2.id).to.be.not.null;
90
- const idProperty = entity2.getIdProperty();
91
- expect(Entity.isTempId(entity2.id)).to.be.false;
78
+ entity.markSaved();
79
+ expect(entity.isTempId).to.be.false;
92
80
  });
93
-
94
81
 
95
82
  it('clone', function() {
96
83
  const entity = this.entity;
@@ -119,6 +106,11 @@ describe('Entity', function() {
119
106
  expect(entity.properties.bar.name).to.be.eq('bar');
120
107
  expect(entity.properties.baz.name).to.be.eq('baz');
121
108
  });
109
+
110
+ it('_createMethods', function() {
111
+ this.entity.testMethod();
112
+ expect(this.entity.bar).to.be.eq('test me');
113
+ });
122
114
 
123
115
  it('loadOriginalData', function() {
124
116
  const data = {
@@ -407,6 +399,12 @@ describe('Entity', function() {
407
399
  const entity = new Entity(this.schema, {});
408
400
  entity.initialize();
409
401
  expect(entity.isPhantom).to.be.true;
402
+
403
+ entity.createTempId();
404
+ expect(entity.isPhantom).to.be.true;
405
+
406
+ entity.markSaved();
407
+ expect(entity.isPhantom).to.be.false;
410
408
  });
411
409
 
412
410
  it('isDirty', function() {
@@ -469,6 +467,15 @@ describe('Entity', function() {
469
467
  expect(this.entity.isDirty).to.be.true;
470
468
  });
471
469
 
470
+ it('setId', function() {
471
+ expect(this.entity.foo).to.be.eq(1);
472
+ expect(this.entity.isTempId).to.be.false;
473
+
474
+ this.entity.setId(2);
475
+ expect(this.entity.foo).to.be.eq(2);
476
+ expect(this.entity.isTempId).to.be.false;
477
+ });
478
+
472
479
  it('_recalculateDependentProperties', function() {
473
480
  const schema = new Schema({
474
481
  name: 'baz',
@@ -508,6 +515,7 @@ describe('Entity', function() {
508
515
 
509
516
  entity.bar = 'Test';
510
517
  entity.markSaved();
518
+ expect(entity.isTempId).to.be.false;
511
519
 
512
520
  expect(entity.isPersisted).to.be.true;
513
521
  const expected = {
@@ -19,6 +19,14 @@ describe('IntegerProperty', function() {
19
19
  expect(className).to.be.eq('Integer');
20
20
  });
21
21
 
22
+ it('newId', function() {
23
+ const id1 = this.property.newId();
24
+ expect(id1.valueOf()).to.be.eq(this.property.idStartsAt);
25
+
26
+ const id2 = this.property.newId();
27
+ expect(id2.valueOf()).to.be.eq(this.property.idStartsAt +1);
28
+ });
29
+
22
30
  // it('default value', function() {
23
31
  // const property = this.property,
24
32
  // rawValue = property.getDefaultValue();
@@ -112,6 +112,20 @@ describe('Property', function() {
112
112
  it('getMapping', function() {
113
113
  expect(this.property.getMapping()).to.be.eq('id');
114
114
  });
115
+
116
+ it('getMapping', function() {
117
+ expect(this.property.getMapping()).to.be.eq('id');
118
+ });
119
+
120
+ it('isTempId', function() {
121
+ const definition = {
122
+ type: 'int',
123
+ isTempId: true,
124
+ },
125
+ Property = PropertyTypes[definition.type];
126
+ const property = new Property(definition);
127
+ expect(property.isTempId).to.be.true;
128
+ });
115
129
 
116
130
  });
117
131
 
@@ -10,9 +10,21 @@ describe('StringProperty', function() {
10
10
  this.property = new Property(definition);
11
11
  });
12
12
 
13
- it('className', function() {
14
- const className = this.property.getClassName();
15
- expect(className).to.be.eq('String');
13
+ describe('general', function() {
14
+
15
+ it('className', function() {
16
+ const className = this.property.getClassName();
17
+ expect(className).to.be.eq('String');
18
+ });
19
+
20
+ it('newId', function() {
21
+ const id1 = this.property.newId();
22
+ expect(id1.valueOf()).to.be.eq('TEMP-1');
23
+
24
+ const id2 = this.property.newId();
25
+ expect(id2.valueOf()).to.be.eq('TEMP-2');
26
+ });
27
+
16
28
  });
17
29
 
18
30
  describe('parse', function() {
@@ -581,6 +581,23 @@ describe('Repository Base', function() {
581
581
  expect(result.value).to.be.eq('three');
582
582
  });
583
583
 
584
+ it('getById', function() {
585
+ const result = this.repository.getById(3);
586
+ expect(result.value).to.be.eq('three');
587
+ });
588
+
589
+ it('getBy', function() {
590
+ const result = this.repository.getBy(entity => entity.id === 2 || entity.id === 3);
591
+ expect(result.length).to.be.eq(2);
592
+ expect(result[0].value).to.be.eq('two');
593
+ expect(result[1].value).to.be.eq('three');
594
+ });
595
+
596
+ it('getFirstBy', function() {
597
+ const result = this.repository.getFirstBy(entity => entity.id === 3);
598
+ expect(result.value).to.be.eq('three');
599
+ });
600
+
584
601
  it('getByRange', function() {
585
602
  const result = this.repository.getByRange(1, 4);
586
603
  expect(_.size(result)).to.be.eq(4);
@@ -622,11 +639,18 @@ describe('Repository Base', function() {
622
639
  expect(_.isEqual(entity, deleted[0])).to.be.true;
623
640
  });
624
641
 
625
- it('isInRepository', function() {
642
+ it('isInRepository, hasId', function() {
626
643
  this.repository.setAutoSave(false);
627
- const entity = this.repository.getByIx(0),
628
- result = this.repository.isInRepository(entity);
629
-
644
+ const id = 1,
645
+ entity = this.repository.getById(id);
646
+
647
+ let result = this.repository.isInRepository(entity);
648
+ expect(result).to.be.true;
649
+
650
+ result = this.repository.isInRepository(id);
651
+ expect(result).to.be.true;
652
+
653
+ result = this.repository.hasId(id);
630
654
  expect(result).to.be.true;
631
655
  });
632
656
 
@@ -661,6 +685,11 @@ describe('Repository Base', function() {
661
685
 
662
686
  describe('deleting', function() {
663
687
 
688
+ it('clear', function() {
689
+ this.repository.clear();
690
+ expect(this.repository.entities.length).to.be.eq(0);
691
+ });
692
+
664
693
  it('delete', function() {
665
694
  let didFire = false;
666
695
  this.repository.on('delete', () => {
@@ -674,6 +703,13 @@ describe('Repository Base', function() {
674
703
  expect(didFire).to.be.true;
675
704
  });
676
705
 
706
+ it('delete() / removeEntity', async function() {
707
+ this.repository.setAutoSave(false);
708
+ const entity = await this.repository.add({ value: 'six' });
709
+ this.repository.delete(entity);
710
+ expect(this.repository.entities.length).to.be.eq(5);
711
+ });
712
+
677
713
 
678
714
  // I'm going to skip these, as they're just combinations of other, tested functions
679
715
  // deleteByIx
@@ -11,6 +11,7 @@ describe('Schema', function() {
11
11
  expect(this.schema instanceof Schema).to.be.true;
12
12
  expect(this.schema.name).to.be.eq('GroupsUsers');
13
13
  expect(this.schema.repository.type).to.be.eq('onebuild');
14
+ expect(this.schema.entity.methods.testMethod).to.be.a('function');
14
15
  });
15
16
 
16
17
  it('clone', function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.5.2",
3
+ "version": "1.6.4",
4
4
  "description": "JS data modeling package with adapters for many storage mediums.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/Entity.js CHANGED
@@ -4,8 +4,6 @@ import EventEmitter from '@onehat/events';
4
4
  import PropertyTypes from './Property';
5
5
  import _ from 'lodash';
6
6
 
7
- const TEMP_PREFIX = 'TEMP-';
8
-
9
7
  /**
10
8
  * Class represents a single Entity (i.e. a record)
11
9
  * which is a collection of Properties with current values.
@@ -152,37 +150,48 @@ class Entity extends EventEmitter {
152
150
 
153
151
  initialize = () => {
154
152
  this.properties = this._createProperties();
153
+ this._createMethods();
155
154
  this.reset();
156
155
  this.isInitialized = true;
157
156
  }
158
157
 
159
158
  /**
160
- * Generates a new unique id and assigns it to this entity.
161
- * If the idProperty is of type 'uuid', then it generates a new UUID.
162
- * If not, then it generates a temp id.
159
+ * Creates the methods for this Entity, based on Schema.
160
+ * @private
163
161
  */
164
- createId = () => {
162
+ _createMethods = () => {
165
163
  if (this.isDestroyed) {
166
- throw Error('this.generateTempId is no longer valid. Entity has been destroyed.');
164
+ throw Error('this._createMethods is no longer valid. Entity has been destroyed.');
167
165
  }
168
- if (!_.isNil(this.id)) {
169
- throw new Error('Entity id already exists.');
166
+ const methodDefinitions = this.schema.entity.methods;
167
+ if (_.isEmpty(methodDefinitions)) {
168
+ return;
170
169
  }
171
- const idProperty = this.getIdProperty(),
172
- id = (idProperty.type === 'uuid') ? idProperty.newId() : _.uniqueId(TEMP_PREFIX);
173
- this.setId(id);
170
+ _.each(methodDefinitions, (method, name) => {
171
+ this[name] = method; // NOTE: Methods must be defined in schema as "function() {}", not as "() => {}" so "this" will be assigned correctly
172
+ });
174
173
  }
175
174
 
176
175
  /**
177
- * Determines whether submitted id is a "temp" id.
178
- * @param {any} id
179
- * @return {boolean} isTempId
176
+ * Generates a new unique id and assigns it to this entity.
180
177
  */
181
- static isTempId(id) {
182
- if (_.isString(id) && id.match(new RegExp('^' + TEMP_PREFIX))) {
183
- return true;
178
+ createTempId = () => {
179
+ if (this.isDestroyed) {
180
+ throw Error('this.createTempId is no longer valid. Entity has been destroyed.');
184
181
  }
185
- return false;
182
+ if (!_.isNil(this.id)) {
183
+ throw new Error('Entity id already exists.');
184
+ }
185
+
186
+ const idProperty = this.getIdProperty();
187
+
188
+ if (!idProperty.newId) {
189
+ throw new Error('idProperty.newId() does not exist');
190
+ }
191
+
192
+ this.setId(idProperty.newId());
193
+
194
+ idProperty.isTempId = true;
186
195
  }
187
196
 
188
197
  /**
@@ -739,6 +748,14 @@ class Entity extends EventEmitter {
739
748
  return this.getId();
740
749
  }
741
750
 
751
+ /**
752
+ * Is this Entity's idProperty using a temporary ID?
753
+ * @return {boolean} isTempId
754
+ */
755
+ get isTempId() {
756
+ return this.getIdProperty().isTempId;
757
+ }
758
+
742
759
  /**
743
760
  * Gets the "Display" Property object for this Entity.
744
761
  * This is the Property whose value can easily identify the whole Entity itself.
@@ -793,13 +810,13 @@ class Entity extends EventEmitter {
793
810
  const idProperty = this.getIdProperty(),
794
811
  id = idProperty.getSubmitValue();
795
812
 
796
- // No id
813
+ // No ID
797
814
  if (_.isNil(id)) {
798
815
  return true;
799
816
  }
800
817
 
801
- // Temp id
802
- if (Entity.isTempId(id)) {
818
+ // ID is temporary
819
+ if (idProperty.isTempId) {
803
820
  return true;
804
821
  }
805
822
 
@@ -851,23 +868,25 @@ class Entity extends EventEmitter {
851
868
  */
852
869
  setId = (id, force = false) => {
853
870
  let isChanged = false;
854
- const property = this.getIdProperty();
871
+ const idProperty = this.getIdProperty();
855
872
 
856
- property.pauseEvents(); // We don't need property_change to fire
857
- if (property.setValue(id)) {
873
+ idProperty.pauseEvents(); // We don't need property_change to fire
874
+ if (idProperty.setValue(id)) {
858
875
  isChanged = true;
859
876
  }
860
- property.resumeEvents();
877
+ idProperty.resumeEvents();
861
878
 
862
879
  if (isChanged || force) {
863
880
  // Set this id on the _originalData* objects
864
- if (property.hasMapping) {
865
- _.merge(this._originalData, Entity.getReverseMappedRawValue(property));
881
+ if (idProperty.hasMapping) {
882
+ _.merge(this._originalData, Entity.getReverseMappedRawValue(idProperty));
866
883
  } else {
867
- this._originalData[property.name] = property.getRawValue();
884
+ this._originalData[idProperty.name] = idProperty.getRawValue();
868
885
  }
869
- this._originalDataParsed[property.name] = property.getParsedValue();
886
+ this._originalDataParsed[idProperty.name] = idProperty.getParsedValue();
870
887
  }
888
+
889
+ idProperty.isTempId = false;
871
890
 
872
891
  return isChanged;
873
892
  }
@@ -973,6 +992,7 @@ class Entity extends EventEmitter {
973
992
  throw Error('this.markSaved is no longer valid. Entity has been destroyed.');
974
993
  }
975
994
  this.isPersisted = true;
995
+ this.getIdProperty().isTempId = false;
976
996
  this._originalData = this._getReconstructedOriginalData();
977
997
  this._originalDataParsed = this.getParsedValues();
978
998
  }
@@ -4,6 +4,8 @@ import Property from './Property';
4
4
  import Parsers from '../Util/Parsers';
5
5
  import _ from 'lodash';
6
6
 
7
+ let lastId = 0;
8
+
7
9
  /**
8
10
  * Class represents a Property that stores an integer value.
9
11
  * @extends Property
@@ -15,6 +17,7 @@ export default class IntegerProperty extends Property {
15
17
 
16
18
  const defaults = {
17
19
  // defaultValue: 0,
20
+ idStartsAt: 100 * 1000 * 1000 * 1000, // 100 billion
18
21
  };
19
22
 
20
23
  _.merge(this, defaults, config);
@@ -29,6 +32,32 @@ export default class IntegerProperty extends Property {
29
32
  }
30
33
  return Parsers.ParseInt(value);
31
34
  }
35
+
36
+ /**
37
+ * Generates a new id
38
+ * Mainly for temporary, in-memory usage
39
+ */
40
+ newId = () => {
41
+ let id,
42
+ hasId = false;
43
+
44
+ const entity = this.getEntity(),
45
+ repository = entity && entity.repository;
46
+
47
+ if (lastId < this.idStartsAt) {
48
+ lastId = this.idStartsAt;
49
+ }
50
+
51
+ id = lastId++;
52
+ hasId = repository ? repository.hasId(id) : false;
53
+
54
+ while(hasId) {
55
+ id = lastId++;
56
+ hasId = repository.hasId(id);
57
+ }
58
+ return id;
59
+ }
60
+
32
61
 
33
62
  };
34
63
 
@@ -83,6 +83,12 @@ export default class Property extends EventEmitter {
83
83
  * @member {boolean} isSortable - Whether this property type is sortable
84
84
  */
85
85
  isSortable: true,
86
+
87
+ /**
88
+ * @member {boolean} isTempId - Whether this property's ID is temporary
89
+ */
90
+ isTempId: false,
91
+
86
92
 
87
93
 
88
94
  // OneBuild META attributes, just bring these over wholesale for now
@@ -4,6 +4,8 @@ import Property from './Property';
4
4
  import Parsers from '../Util/Parsers';
5
5
  import _ from 'lodash';
6
6
 
7
+ const TEMP_PREFIX = 'TEMP-';
8
+
7
9
  /**
8
10
  * Class represents a Property that stores string data.
9
11
  * @extends Property
@@ -29,6 +31,23 @@ export default class StringProperty extends Property {
29
31
  }
30
32
  return Parsers.ParseString(value);
31
33
  }
34
+
35
+ newId = () => {
36
+ let id,
37
+ hasId = false;
38
+
39
+ const entity = this.getEntity(),
40
+ repository = entity && entity.repository;
41
+
42
+ id = _.uniqueId(TEMP_PREFIX);
43
+ hasId = repository ? repository.hasId(id) : false;
44
+
45
+ while(hasId) {
46
+ id = _.uniqueId(TEMP_PREFIX);
47
+ hasId = repository.hasId(id);
48
+ }
49
+ return id;
50
+ }
32
51
  };
33
52
 
34
53
  StringProperty.className = 'String';
@@ -395,7 +395,7 @@ class AjaxRepository extends Repository {
395
395
  });
396
396
 
397
397
  // Set the total records that pass filter
398
- this.total = total;
398
+ this.total = total;
399
399
  this._setPaginationVars();
400
400
 
401
401
  this.isLoading = false;
@@ -678,15 +678,6 @@ class AjaxRepository extends Repository {
678
678
  }));
679
679
  }
680
680
 
681
- /**
682
- * Deletes all locally cached entities in repository,
683
- * usually, the current "page".
684
- * Does not actually affect anything on the server.
685
- */
686
- clear = async () => {
687
- this._destroyEntities();
688
- }
689
-
690
681
  }
691
682
 
692
683
  AjaxRepository.className = 'Ajax';
@@ -134,7 +134,7 @@ class MemoryRepository extends Repository {
134
134
  // Add to internal store
135
135
  _.each(entities, (entity) => {
136
136
  if (entity.isPhantom) {
137
- entity.createId();
137
+ entity.createTempId();
138
138
  }
139
139
  this._keyedEntities[entity.id] = entity;
140
140
  });
@@ -865,8 +865,8 @@ export default class Repository extends EventEmitter {
865
865
  this.entities.push(entity);
866
866
 
867
867
  // Create id if needed
868
- if (entity.isPhantom) {
869
- entity.createId(); // either a UUID or a temp id
868
+ if (entity.isPhantom) { // i.e. idProperty has no value
869
+ entity.createTempId();
870
870
  }
871
871
 
872
872
  this.emit('add', entity);
@@ -893,7 +893,7 @@ export default class Repository extends EventEmitter {
893
893
  const entity = Repository._createEntity(this.schema, data, this, isPersisted, originalIsMapped);
894
894
 
895
895
  if (entity.isPhantom) {
896
- entity.createId(); // either a UUID or a temp id
896
+ entity.createTempId();
897
897
  }
898
898
 
899
899
  return entity;
@@ -972,6 +972,15 @@ export default class Repository extends EventEmitter {
972
972
  this.entities = [];
973
973
  }
974
974
 
975
+ /**
976
+ * Deletes all locally cached entities in repository,
977
+ * usually, the current "page".
978
+ * Does not actually affect anything on the server.
979
+ */
980
+ clear = async () => {
981
+ this._destroyEntities();
982
+ }
983
+
975
984
  /**
976
985
  * Gets an array of "submit" values objects for the entities
977
986
  * @return {array} map -
@@ -1064,20 +1073,19 @@ export default class Repository extends EventEmitter {
1064
1073
  /**
1065
1074
  * Get a single Entity by its id
1066
1075
  * @param {integer} id - id of record to retrieve
1067
- * @return {Entity} The Entity with matching id
1076
+ * @return {Entity} The Entity with matching id, or undefined
1068
1077
  */
1069
1078
  getById = (id) => {
1070
1079
  if (this.isDestroyed) {
1071
1080
  throw Error('this.getById is no longer valid. Repository has been destroyed.');
1072
1081
  }
1073
- const result = this.getBy((entity) => entity.id === id);
1074
- return result.length > 0 ? result[0] : null;
1082
+ return this.getFirstBy(entity => entity.id === id);
1075
1083
  }
1076
1084
 
1077
1085
  /**
1078
1086
  * Get an array of Entities by supplied filter function
1079
1087
  * @param {function} filter - Filter function to apply to all entities
1080
- * @return {Entity[]} Entities that passed through filter
1088
+ * @return {Entity[]} Entities that passed through filter, or []
1081
1089
  */
1082
1090
  getBy = (filter) => {
1083
1091
  if (this.isDestroyed) {
@@ -1093,7 +1101,7 @@ export default class Repository extends EventEmitter {
1093
1101
  * filters into account. Defaults to false.
1094
1102
  *
1095
1103
  * @param {function} filter - Filter function to search by
1096
- * @return {Entity} First Entity found
1104
+ * @return {Entity} First Entity found, or undefined
1097
1105
  */
1098
1106
  getFirstBy = (filter) => {
1099
1107
  if (this.isDestroyed) {
@@ -1104,7 +1112,7 @@ export default class Repository extends EventEmitter {
1104
1112
 
1105
1113
  /**
1106
1114
  * Get all phantom (unsaved) Entities
1107
- * @return {Entity[]} Array of phantom Entities
1115
+ * @return {Entity[]} Array of phantom Entities, or []
1108
1116
  */
1109
1117
  getPhantom = () => {
1110
1118
  if (this.isDestroyed) {
@@ -1115,7 +1123,7 @@ export default class Repository extends EventEmitter {
1115
1123
 
1116
1124
  /**
1117
1125
  * Get all Entities not yet persisted to a storage medium
1118
- * @return {Entity[]} Array of dirty Entities
1126
+ * @return {Entity[]} Array of dirty Entities, or []
1119
1127
  */
1120
1128
  getNonPersisted = () => {
1121
1129
  if (this.isDestroyed) {
@@ -1128,7 +1136,7 @@ export default class Repository extends EventEmitter {
1128
1136
 
1129
1137
  /**
1130
1138
  * Get all dirty (having unsaved changes) Entities
1131
- * @return {Entity[]} Array of dirty Entities
1139
+ * @return {Entity[]} Array of dirty Entities, or []
1132
1140
  */
1133
1141
  getDirty = () => {
1134
1142
  if (this.isDestroyed) {
@@ -1141,7 +1149,7 @@ export default class Repository extends EventEmitter {
1141
1149
 
1142
1150
  /**
1143
1151
  * Get all deleted Entities
1144
- * @return {Entity[]} Array of deleted Entities
1152
+ * @return {Entity[]} Array of deleted Entities, or []
1145
1153
  */
1146
1154
  getDeleted = () => {
1147
1155
  if (this.isDestroyed) {
@@ -1168,6 +1176,15 @@ export default class Repository extends EventEmitter {
1168
1176
  return !_.isNil(this.getById(idOrEntity));
1169
1177
  }
1170
1178
 
1179
+ /**
1180
+ * Convenience function
1181
+ * Alias for isInRepository
1182
+ * NOTE: It only searches in memory. Doesn't query server
1183
+ */
1184
+ hasId = (id) => {
1185
+ return this.isInRepository(id);
1186
+ }
1187
+
1171
1188
  /**
1172
1189
  * Queues up batch operations for saving
1173
1190
  * new, edited, and deleted entities to storage medium.
@@ -1357,7 +1374,7 @@ export default class Repository extends EventEmitter {
1357
1374
 
1358
1375
  /**
1359
1376
  * Marks entities for deletion from storage medium.
1360
- * Actual deletion takes place in save()
1377
+ * Actual deletion takes place in save(), unless isPhantom
1361
1378
  * @param {object|array} entities - one or more entities to delete
1362
1379
  * @fires delete
1363
1380
  */
@@ -1373,7 +1390,12 @@ export default class Repository extends EventEmitter {
1373
1390
  return;
1374
1391
  }
1375
1392
  _.each(entities, (entity) => {
1376
- entity.markDeleted(); // Entity is still there, it's just marked for deletion
1393
+ if (entity.isPhantom) {
1394
+ // Just auto-remove it. Don't bother saving to storage medium.
1395
+ this.removeEntity(entity);
1396
+ } else {
1397
+ entity.markDeleted(); // Entity is still there, it's just marked for deletion
1398
+ }
1377
1399
  });
1378
1400
 
1379
1401
  this.emit('delete', entities);
@@ -1383,6 +1405,16 @@ export default class Repository extends EventEmitter {
1383
1405
  }
1384
1406
  }
1385
1407
 
1408
+ /**
1409
+ * Removes an Entity from the current page
1410
+ * Mainly used for phantom Entities
1411
+ * Helper for delete()
1412
+ */
1413
+ removeEntity = async (entity) => {
1414
+ this.entities = _.filter(this.entities, e => e !== entity);
1415
+ entity.destroy();
1416
+ }
1417
+
1386
1418
  /**
1387
1419
  * Deletes a single Entity by its index (zero-indexed) on the current page
1388
1420
  * @param {integer} ix - Index
@@ -5,11 +5,12 @@ import _ from 'lodash';
5
5
 
6
6
  /**
7
7
  * Class represents the Schema definition for Model and Source
8
- * This is basically just a big config object, used to instantiate a Model and Source.
8
+ * This is basically just a big config object, used to instantiate an Entity, and Repository.
9
9
  * Usage:
10
10
  * - const schema = new Schema({
11
11
  * name: 'Users',
12
12
  * model: {},
13
+ * entity: {},
13
14
  * repository: {},
14
15
  * });
15
16
  *
@@ -96,6 +97,10 @@ export default class Schema extends EventEmitter {
96
97
  belongsToMany: [],
97
98
  },
98
99
  },
100
+
101
+ entity: {
102
+ methods: {}, // NOTE: Methods must be defined as "function() {}", not as "() => {}" so "this" will be assigned correctly
103
+ },
99
104
 
100
105
  /**
101
106
  * @member {object|string} repository - Config for Repository