notu 0.1.0

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.
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "notu",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/jamescoyle1989/notu.git"
8
+ },
9
+ "author": "James Coyle",
10
+ "license": "MIT",
11
+ "scripts": {
12
+ "test": "vitest",
13
+ "build": "tsc && vite build",
14
+ "publish": "tsc && vite build && npm publish"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.0.2",
18
+ "vite": "^4.4.5",
19
+ "vitest": "^0.34.4"
20
+ }
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import Note from './models/Note';
2
+
3
+ export {
4
+ Note
5
+ };
@@ -0,0 +1,193 @@
1
+ import { expect, test } from 'vitest';
2
+ import Attr from './Attr';
3
+ import Space from './Space';
4
+
5
+
6
+ test('Gets initiated as new', () => {
7
+ const attr = new Attr();
8
+ expect(attr.isNew).toBe(true);
9
+ });
10
+
11
+
12
+ test('Set name marks attr as dirty if currently clean', () => {
13
+ const attr = new Attr().clean();
14
+ attr.name = 'asdf';
15
+ expect(attr.isDirty).toBe(true);
16
+ });
17
+
18
+ test('Set name doesnt change attr state if new', () => {
19
+ const attr = new Attr().new();
20
+ attr.name = 'asdf';
21
+ expect(attr.isNew).toBe(true);
22
+ });
23
+
24
+ test('Set name doesnt change attr state if value not different', () => {
25
+ const attr = new Attr().clean();
26
+ attr.name = '';
27
+ expect(attr.isClean).toBe(true);
28
+ });
29
+
30
+
31
+ test('Set type is allowed if attr is new', () => {
32
+ const attr = new Attr().new();
33
+ attr.type = 'BOOLEAN';
34
+ expect(attr.type).toBe('BOOLEAN');
35
+ });
36
+
37
+ test('Set type is not allowed if attr is not new', () => {
38
+ const attr = new Attr().clean();
39
+ expect(() => attr.type = 'BOOLEAN').toThrowError();
40
+ });
41
+
42
+ test('isText returns true if type is text', () => {
43
+ const attr = new Attr();
44
+ attr.type = 'TEXT';
45
+ expect(attr.isText).toBe(true);
46
+ attr.type = 'NUMBER';
47
+ expect(attr.isText).toBe(false);
48
+ });
49
+
50
+ test('isNumber returns true if type is number', () => {
51
+ const attr = new Attr();
52
+ attr.type = 'TEXT';
53
+ expect(attr.isNumber).toBe(false);
54
+ attr.type = 'NUMBER';
55
+ expect(attr.isNumber).toBe(true);
56
+ });
57
+
58
+ test('isBoolean returns true if type is boolean', () => {
59
+ const attr = new Attr();
60
+ attr.type = 'TEXT';
61
+ expect(attr.isBoolean).toBe(false);
62
+ attr.type = 'BOOLEAN';
63
+ expect(attr.isBoolean).toBe(true);
64
+ });
65
+
66
+ test('isDate returns true if type is date', () => {
67
+ const attr = new Attr();
68
+ attr.type = 'TEXT';
69
+ expect(attr.isDate).toBe(false);
70
+ attr.type = 'DATE';
71
+ expect(attr.isDate).toBe(true);
72
+ });
73
+
74
+ test('asText sets type', () => {
75
+ const attr = new Attr().asText();
76
+ expect(attr.type).toBe('TEXT');
77
+ });
78
+
79
+ test('asNumber sets type', () => {
80
+ const attr = new Attr().asNumber();
81
+ expect(attr.type).toBe('NUMBER');
82
+ });
83
+
84
+ test('asBoolean sets type', () => {
85
+ const attr = new Attr().asBoolean();
86
+ expect(attr.type).toBe('BOOLEAN');
87
+ });
88
+
89
+ test('asDate sets type', () => {
90
+ const attr = new Attr().asDate();
91
+ expect(attr.type).toBe('DATE');
92
+ });
93
+
94
+
95
+ test('Set spaceId marks attr as dirty if currently clean', () => {
96
+ const attr = new Attr().clean();
97
+ attr.spaceId = 123;
98
+ expect(attr.isDirty).toBe(true);
99
+ });
100
+
101
+ test('Set spaceId doesnt change attr state if new', () => {
102
+ const attr = new Attr().new();
103
+ attr.spaceId = 123;
104
+ expect(attr.isNew).toBe(true);
105
+ });
106
+
107
+ test('Set spaceId doesnt change attr state if value not different', () => {
108
+ const attr = new Attr().clean();
109
+ attr.spaceId = attr.spaceId;
110
+ expect(attr.isClean).toBe(true);
111
+ });
112
+
113
+ test('Setting space with id different than current spaceId updates state', () => {
114
+ const attr = new Attr();
115
+ attr.spaceId = 57;
116
+ attr.clean();
117
+ const space = new Space('hello');
118
+ space.id = 60;
119
+
120
+ attr.space = space;
121
+
122
+ expect(attr.spaceId).toBe(60);
123
+ expect(attr.isDirty).toBe(true);
124
+ });
125
+
126
+ test('Setting space with id same as current spaceId preserves state', () => {
127
+ const attr = new Attr();
128
+ attr.spaceId = 80;
129
+ attr.clean();
130
+ const space = new Space('hello');
131
+ space.id = 80;
132
+
133
+ attr.space = space;
134
+
135
+ expect(attr.spaceId).toBe(80);
136
+ expect(attr.isClean).toBe(true);
137
+ });
138
+
139
+ test('Setting spaceId to new value removes space object', () => {
140
+ const attr = new Attr();
141
+ const space = new Space('hello');
142
+ space.id = 80;
143
+ attr.space = space;
144
+
145
+ attr.spaceId = 81;
146
+
147
+ expect(attr.space).toBeNull();
148
+ });
149
+
150
+ test('Setting spaceId to same as current space id preserves it', () => {
151
+ const attr = new Attr();
152
+ const space = new Space('hello');
153
+ space.id = 80;
154
+ attr.space = space;
155
+
156
+ attr.spaceId = 80;
157
+
158
+ expect(attr.space.name).toBe('hello');
159
+ });
160
+
161
+
162
+ test('Can duplicate itself', () => {
163
+ const attr = new Attr();
164
+ const space = new Space('hello');
165
+ space.id = 123;
166
+ attr.space = space;
167
+ const copy = attr.duplicate();
168
+ expect(copy.id).toBe(attr.id);
169
+ expect(copy.name).toBe(attr.name);
170
+ expect(copy.type).toBe(attr.type);
171
+ expect(copy.space).toBe(attr.space);
172
+ expect(copy.spaceId).toBe(attr.spaceId);
173
+ });
174
+
175
+
176
+ test('validate fails if spaceId is 0', () => {
177
+ const model = new Attr();
178
+ model.spaceId = 0;
179
+ expect(model.validate()).toBe(false);
180
+ });
181
+
182
+ test('validate fails if not new and id <= 0', () => {
183
+ const model = new Attr().clean();
184
+ model.id = 0;
185
+ model.spaceId = 123;
186
+ expect(model.validate()).toBe(false);
187
+ });
188
+
189
+ test('validate throws error if arg set to true', () => {
190
+ const model = new Attr();
191
+ model.spaceId = 0;
192
+ expect(() => model.validate(true)).toThrowError();
193
+ });
@@ -0,0 +1,111 @@
1
+ 'use strict';
2
+
3
+ import ModelWithState from './ModelWithState';
4
+ import Space from './Space';
5
+
6
+
7
+ const ATTR_TYPE = {
8
+ TEXT: 'TEXT',
9
+ NUMBER: 'NUMBER',
10
+ BOOLEAN: 'BOOLEAN',
11
+ DATE: 'DATE'
12
+ };
13
+
14
+ export type AttrType = keyof typeof ATTR_TYPE;
15
+
16
+
17
+ export default class Attr extends ModelWithState<Attr> {
18
+ id: number = 0;
19
+
20
+
21
+ private _name: string = '';
22
+ get name(): string { return this._name; }
23
+ set name(value: string) {
24
+ if (value !== this._name) {
25
+ this._name = value;
26
+ if (this.isClean)
27
+ this.dirty();
28
+ }
29
+ }
30
+
31
+ private _type: AttrType = 'TEXT';
32
+ get type(): AttrType { return this._type; }
33
+ set type(value: AttrType) {
34
+ if (!this.isNew)
35
+ throw Error('Cannot change an attribute\'s type once it has been created.');
36
+ this._type = value;
37
+ }
38
+
39
+ get isText(): boolean { return this.type == 'TEXT'; }
40
+
41
+ get isNumber(): boolean { return this.type == 'NUMBER'; }
42
+
43
+ get isBoolean(): boolean { return this.type == 'BOOLEAN'; }
44
+
45
+ get isDate(): boolean { return this.type == 'DATE'; }
46
+
47
+ asText(): Attr {
48
+ this.type = 'TEXT';
49
+ return this;
50
+ }
51
+
52
+ asNumber(): Attr {
53
+ this.type = 'NUMBER';
54
+ return this;
55
+ }
56
+
57
+ asBoolean(): Attr {
58
+ this.type = 'BOOLEAN';
59
+ return this;
60
+ }
61
+
62
+ asDate(): Attr {
63
+ this.type = 'DATE';
64
+ return this;
65
+ }
66
+
67
+
68
+ private _spaceId: number = 0;
69
+ get spaceId(): number { return this._spaceId; }
70
+ set spaceId(value: number) {
71
+ if (value !== this._spaceId) {
72
+ this._spaceId = value;
73
+ if (value !== this.space?.id ?? 0)
74
+ this._space = null;
75
+ if (this.isClean)
76
+ this.dirty();
77
+ }
78
+ }
79
+
80
+ private _space: Space = null;
81
+ get space(): Space { return this._space; }
82
+ set space(value: Space) {
83
+ this._space = value;
84
+ this.spaceId = value?.id ?? 0;
85
+ }
86
+
87
+
88
+ duplicate(): Attr {
89
+ const output = new Attr();
90
+ output.id = this.id;
91
+ output.name = this.name;
92
+ output.type = this.type;
93
+ output.space = this.space;
94
+ return output;
95
+ }
96
+
97
+
98
+ validate(throwError: boolean = false): boolean {
99
+ let output = null;
100
+
101
+ if (this.spaceId <= 0)
102
+ output = 'Note spaceId must be greater than zero.';
103
+ else if (!this.isNew && this.id <= 0)
104
+ output = 'Attr id must be greater than zero if in non-new state.';
105
+
106
+ if (throwError && output != null)
107
+ throw Error(output);
108
+
109
+ return output == null;
110
+ }
111
+ }
@@ -0,0 +1,66 @@
1
+ import { expect, test } from 'vitest';
2
+ import ModelWithState from './ModelWithState';
3
+
4
+
5
+ class TestModel extends ModelWithState<TestModel> {
6
+ }
7
+
8
+
9
+ test('state gets defaulted to new', () => {
10
+ const model = new ModelWithState<TestModel>();
11
+ expect(model.state).toBe('NEW');
12
+ });
13
+
14
+ test('clean() changes model state', () => {
15
+ const model = new ModelWithState<TestModel>().clean();
16
+ expect(model.state).toBe('CLEAN');
17
+ });
18
+
19
+ test('dirty() changes model state', () => {
20
+ const model = new ModelWithState<TestModel>().dirty();
21
+ expect(model.state).toBe('DIRTY');
22
+ });
23
+
24
+ test('delete() changes model state', () => {
25
+ const model = new ModelWithState<TestModel>().delete();
26
+ expect(model.state).toBe('DELETED');
27
+ });
28
+
29
+ test('new() changes model state', () => {
30
+ const model = new ModelWithState<TestModel>().dirty().new();
31
+ expect(model.state).toBe('NEW');
32
+ });
33
+
34
+ test('isNew returns correct value', () => {
35
+ const model = new ModelWithState<TestModel>().new();
36
+ expect(model.isNew).toBe(true);
37
+ model.dirty();
38
+ expect(model.isNew).toBe(false);
39
+ });
40
+
41
+ test('isClean returns correct value', () => {
42
+ const model = new ModelWithState<TestModel>().new();
43
+ expect(model.isClean).toBe(false);
44
+ model.clean();
45
+ expect(model.isClean).toBe(true);
46
+ });
47
+
48
+ test('isDirty returns correct value', () => {
49
+ const model = new ModelWithState<TestModel>().new();
50
+ expect(model.isDirty).toBe(false);
51
+ model.dirty();
52
+ expect(model.isDirty).toBe(true);
53
+ });
54
+
55
+ test('isDeleted returns correct value', () => {
56
+ const model = new ModelWithState<TestModel>().new();
57
+ expect(model.isDeleted).toBe(false);
58
+ model.delete();
59
+ expect(model.isDeleted).toBe(true);
60
+ });
61
+
62
+
63
+ test('validate does nothing', () => {
64
+ const model = new ModelWithState<TestModel>().new();
65
+ expect(model.validate()).toBe(true);
66
+ });
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+
4
+ const MODEL_STATE = {
5
+ NEW: 'NEW',
6
+ CLEAN: 'CLEAN',
7
+ DIRTY: 'DIRTY',
8
+ DELETED: 'DELETED'
9
+ };
10
+
11
+ export type ModelState = keyof typeof MODEL_STATE;
12
+
13
+
14
+ export default class ModelWithState<T extends ModelWithState<T>> {
15
+ state: ModelState = 'NEW';
16
+
17
+ new(): T {
18
+ this.state = 'NEW';
19
+ return (this as any) as T;
20
+ }
21
+
22
+ clean(): T {
23
+ this.state = 'CLEAN';
24
+ return (this as any) as T;
25
+ }
26
+
27
+ dirty(): T {
28
+ this.state = 'DIRTY';
29
+ return (this as any) as T;
30
+ }
31
+
32
+ delete(): T {
33
+ this.state = 'DELETED';
34
+ return (this as any) as T;
35
+ }
36
+
37
+ get isNew(): boolean {
38
+ return this.state == 'NEW';
39
+ }
40
+
41
+ get isClean(): boolean {
42
+ return this.state == 'CLEAN';
43
+ }
44
+
45
+ get isDirty(): boolean {
46
+ return this.state == 'DIRTY';
47
+ }
48
+
49
+ get isDeleted(): boolean {
50
+ return this.state == 'DELETED';
51
+ }
52
+
53
+
54
+ validate(throwError: boolean = false): boolean {
55
+ return true;
56
+ }
57
+ }