@onehat/data 1.6.0 → 1.6.5

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,
@@ -71,7 +78,6 @@ describe('Entity', function() {
71
78
  entity.markSaved();
72
79
  expect(entity.isTempId).to.be.false;
73
80
  });
74
-
75
81
 
76
82
  it('clone', function() {
77
83
  const entity = this.entity;
@@ -100,6 +106,11 @@ describe('Entity', function() {
100
106
  expect(entity.properties.bar.name).to.be.eq('bar');
101
107
  expect(entity.properties.baz.name).to.be.eq('baz');
102
108
  });
109
+
110
+ it('_createMethods', function() {
111
+ this.entity.testMethod();
112
+ expect(this.entity.bar).to.be.eq('test me');
113
+ });
103
114
 
104
115
  it('loadOriginalData', function() {
105
116
  const data = {
@@ -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);
@@ -668,6 +685,11 @@ describe('Repository Base', function() {
668
685
 
669
686
  describe('deleting', function() {
670
687
 
688
+ it('clear', function() {
689
+ this.repository.clear();
690
+ expect(this.repository.entities.length).to.be.eq(0);
691
+ });
692
+
671
693
  it('delete', function() {
672
694
  let didFire = false;
673
695
  this.repository.on('delete', () => {
@@ -681,6 +703,13 @@ describe('Repository Base', function() {
681
703
  expect(didFire).to.be.true;
682
704
  });
683
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
+
684
713
 
685
714
  // I'm going to skip these, as they're just combinations of other, tested functions
686
715
  // deleteByIx
@@ -10,6 +10,8 @@ describe('Schema', function() {
10
10
  it('schema is valid', function() {
11
11
  expect(this.schema instanceof Schema).to.be.true;
12
12
  expect(this.schema.name).to.be.eq('GroupsUsers');
13
+ expect(this.schema.model.associations.hasOne).to.be.an('array');
14
+ expect(this.schema.entity.methods.testMethod).to.be.a('function');
13
15
  expect(this.schema.repository.type).to.be.eq('onebuild');
14
16
  });
15
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onehat/data",
3
- "version": "1.6.0",
3
+ "version": "1.6.5",
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
@@ -150,10 +150,28 @@ class Entity extends EventEmitter {
150
150
 
151
151
  initialize = () => {
152
152
  this.properties = this._createProperties();
153
+ this._createMethods();
153
154
  this.reset();
154
155
  this.isInitialized = true;
155
156
  }
156
157
 
158
+ /**
159
+ * Creates the methods for this Entity, based on Schema.
160
+ * @private
161
+ */
162
+ _createMethods = () => {
163
+ if (this.isDestroyed) {
164
+ throw Error('this._createMethods is no longer valid. Entity has been destroyed.');
165
+ }
166
+ const methodDefinitions = this.schema.entity.methods;
167
+ if (_.isEmpty(methodDefinitions)) {
168
+ return;
169
+ }
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
+ });
173
+ }
174
+
157
175
  /**
158
176
  * Generates a new unique id and assigns it to this entity.
159
177
  */
@@ -833,6 +851,31 @@ class Entity extends EventEmitter {
833
851
  return this._originalData;
834
852
  }
835
853
 
854
+ /**
855
+ * Gets the associated Repository
856
+ * @param {string} repositoryName - Name of the Repository to retrieve
857
+ * @return {boolean} hasProperty
858
+ */
859
+ getAssociatedRepository = (repositoryName) => {
860
+ if (this.isDestroyed) {
861
+ throw Error('this.getAssociatedRepository is no longer valid. Entity has been destroyed.');
862
+ }
863
+
864
+ const schema = this.getSchema();
865
+ if (!schema.associations.hasOne.includes(repositoryName) &&
866
+ !schema.associations.hasMany.includes(repositoryName) &&
867
+ !schema.associations.belongsTo.includes(repositoryName) &&
868
+ !schema.associations.belongsToMany.includes(repositoryName)
869
+ ) {
870
+ throw Error(repositoryName + ' is not associated with ' + this.getRepository().name);
871
+ }
872
+ const repository = this.getRepository();
873
+ if (!repository.oneHatData) {
874
+ throw Error('No global oneHatData object');
875
+ }
876
+ return repository.oneHatData.getRepository(repositoryName);
877
+ }
878
+
836
879
 
837
880
 
838
881
  // _____ __ __
package/src/OneHatData.js CHANGED
@@ -254,8 +254,8 @@ export class OneHatData extends EventEmitter {
254
254
  }
255
255
 
256
256
  // Apply the general config settings to each specific one
257
- const localConfig = _.assign({}, generalConfig, config.local),
258
- remoteConfig = _.assign({}, generalConfig, config.remote);
257
+ const localConfig = _.merge({}, generalConfig, config.local),
258
+ remoteConfig = _.merge({}, generalConfig, config.remote);
259
259
 
260
260
  // Actually create the local and remote repositories
261
261
  config.local = await this.createRepository(localConfig);
@@ -263,7 +263,7 @@ export class OneHatData extends EventEmitter {
263
263
  }
264
264
 
265
265
  const Repository = this._repositoryTypes[config.type],
266
- repository = new Repository(config);
266
+ repository = new Repository(config, this);
267
267
  await repository.initialize();
268
268
 
269
269
  return repository;
@@ -370,7 +370,7 @@ class AjaxRepository extends Repository {
370
370
  }
371
371
 
372
372
  const repository = this;
373
- const data = _.assign({}, this._baseParams, this._params);
373
+ const data = _.merge({}, this._baseParams, this._params);
374
374
 
375
375
  return this._send(this.methods.get, this.api.get, data)
376
376
  .then(result => {
@@ -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';
@@ -23,7 +23,7 @@ export default class Repository extends EventEmitter {
23
23
  * - name {string} - Optional. Defaults to schema.name
24
24
  * - schema - Schema object
25
25
  */
26
- constructor(config = {}) {
26
+ constructor(config = {}, oneHatData = null) {
27
27
  super(...arguments);
28
28
 
29
29
  const { schema } = config;
@@ -214,6 +214,11 @@ export default class Repository extends EventEmitter {
214
214
  */
215
215
  this.isDestroyed = false;
216
216
 
217
+ /**
218
+ * @member {boolean} oneHatData - The global @onehat/data object
219
+ */
220
+ this.oneHatData = oneHatData;
221
+
217
222
  this.registerEvents([
218
223
  'add',
219
224
  'beforeSave',
@@ -972,6 +977,15 @@ export default class Repository extends EventEmitter {
972
977
  this.entities = [];
973
978
  }
974
979
 
980
+ /**
981
+ * Deletes all locally cached entities in repository,
982
+ * usually, the current "page".
983
+ * Does not actually affect anything on the server.
984
+ */
985
+ clear = async () => {
986
+ this._destroyEntities();
987
+ }
988
+
975
989
  /**
976
990
  * Gets an array of "submit" values objects for the entities
977
991
  * @return {array} map -
@@ -1064,20 +1078,19 @@ export default class Repository extends EventEmitter {
1064
1078
  /**
1065
1079
  * Get a single Entity by its id
1066
1080
  * @param {integer} id - id of record to retrieve
1067
- * @return {Entity} The Entity with matching id
1081
+ * @return {Entity} The Entity with matching id, or undefined
1068
1082
  */
1069
1083
  getById = (id) => {
1070
1084
  if (this.isDestroyed) {
1071
1085
  throw Error('this.getById is no longer valid. Repository has been destroyed.');
1072
1086
  }
1073
- const result = this.getBy((entity) => entity.id === id);
1074
- return result.length > 0 ? result[0] : null;
1087
+ return this.getFirstBy(entity => entity.id === id);
1075
1088
  }
1076
1089
 
1077
1090
  /**
1078
1091
  * Get an array of Entities by supplied filter function
1079
1092
  * @param {function} filter - Filter function to apply to all entities
1080
- * @return {Entity[]} Entities that passed through filter
1093
+ * @return {Entity[]} Entities that passed through filter, or []
1081
1094
  */
1082
1095
  getBy = (filter) => {
1083
1096
  if (this.isDestroyed) {
@@ -1093,7 +1106,7 @@ export default class Repository extends EventEmitter {
1093
1106
  * filters into account. Defaults to false.
1094
1107
  *
1095
1108
  * @param {function} filter - Filter function to search by
1096
- * @return {Entity} First Entity found
1109
+ * @return {Entity} First Entity found, or undefined
1097
1110
  */
1098
1111
  getFirstBy = (filter) => {
1099
1112
  if (this.isDestroyed) {
@@ -1104,7 +1117,7 @@ export default class Repository extends EventEmitter {
1104
1117
 
1105
1118
  /**
1106
1119
  * Get all phantom (unsaved) Entities
1107
- * @return {Entity[]} Array of phantom Entities
1120
+ * @return {Entity[]} Array of phantom Entities, or []
1108
1121
  */
1109
1122
  getPhantom = () => {
1110
1123
  if (this.isDestroyed) {
@@ -1115,7 +1128,7 @@ export default class Repository extends EventEmitter {
1115
1128
 
1116
1129
  /**
1117
1130
  * Get all Entities not yet persisted to a storage medium
1118
- * @return {Entity[]} Array of dirty Entities
1131
+ * @return {Entity[]} Array of dirty Entities, or []
1119
1132
  */
1120
1133
  getNonPersisted = () => {
1121
1134
  if (this.isDestroyed) {
@@ -1128,7 +1141,7 @@ export default class Repository extends EventEmitter {
1128
1141
 
1129
1142
  /**
1130
1143
  * Get all dirty (having unsaved changes) Entities
1131
- * @return {Entity[]} Array of dirty Entities
1144
+ * @return {Entity[]} Array of dirty Entities, or []
1132
1145
  */
1133
1146
  getDirty = () => {
1134
1147
  if (this.isDestroyed) {
@@ -1141,7 +1154,7 @@ export default class Repository extends EventEmitter {
1141
1154
 
1142
1155
  /**
1143
1156
  * Get all deleted Entities
1144
- * @return {Entity[]} Array of deleted Entities
1157
+ * @return {Entity[]} Array of deleted Entities, or []
1145
1158
  */
1146
1159
  getDeleted = () => {
1147
1160
  if (this.isDestroyed) {
@@ -1366,7 +1379,7 @@ export default class Repository extends EventEmitter {
1366
1379
 
1367
1380
  /**
1368
1381
  * Marks entities for deletion from storage medium.
1369
- * Actual deletion takes place in save()
1382
+ * Actual deletion takes place in save(), unless isPhantom
1370
1383
  * @param {object|array} entities - one or more entities to delete
1371
1384
  * @fires delete
1372
1385
  */
@@ -1382,7 +1395,12 @@ export default class Repository extends EventEmitter {
1382
1395
  return;
1383
1396
  }
1384
1397
  _.each(entities, (entity) => {
1385
- entity.markDeleted(); // Entity is still there, it's just marked for deletion
1398
+ if (entity.isPhantom) {
1399
+ // Just auto-remove it. Don't bother saving to storage medium.
1400
+ this.removeEntity(entity);
1401
+ } else {
1402
+ entity.markDeleted(); // Entity is still there, it's just marked for deletion
1403
+ }
1386
1404
  });
1387
1405
 
1388
1406
  this.emit('delete', entities);
@@ -1392,6 +1410,16 @@ export default class Repository extends EventEmitter {
1392
1410
  }
1393
1411
  }
1394
1412
 
1413
+ /**
1414
+ * Removes an Entity from the current page
1415
+ * Mainly used for phantom Entities
1416
+ * Helper for delete()
1417
+ */
1418
+ removeEntity = async (entity) => {
1419
+ this.entities = _.filter(this.entities, e => e !== entity);
1420
+ entity.destroy();
1421
+ }
1422
+
1395
1423
  /**
1396
1424
  * Deletes a single Entity by its index (zero-indexed) on the current page
1397
1425
  * @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
@@ -104,7 +109,7 @@ export default class Schema extends EventEmitter {
104
109
 
105
110
  };
106
111
 
107
- this._originalConfig = _.assign({}, defaults, config);
112
+ this._originalConfig = _.merge({}, defaults, config);
108
113
  _.merge(this, this._originalConfig);
109
114
 
110
115
  /**