Skip to content

Commit aec8f81

Browse files
authored
Merge pull request #307 from derbyjs/better-get
Add methods getOrDefault and getOrThrow
2 parents d40585b + abd2592 commit aec8f81

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

Diff for: src/Model/collections.ts

+29
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,22 @@ declare module './Model' {
7676
getOrCreateCollection(name: string): Collection;
7777
getOrCreateDoc(collectionName: string, id: string, data: any);
7878

79+
/**
80+
* Gets value at the path if not nullish, otherwise returns provided default value
81+
*
82+
* @param subpath
83+
* @param defaultValue value to return if no value at subpath
84+
*/
85+
getOrDefault<S>(subpath: Path, defaultValue: S): ReadonlyDeep<S>;
86+
87+
/**
88+
* Gets the value located at this model's path or a relative subpath.
89+
*
90+
* If no value exists at the path, or the value is nullish (null or undefined), this will throw an error.
91+
* @param subpath
92+
*/
93+
getOrThrow<S>(subpath: Path): ReadonlyDeep<S>;
94+
7995
_get(segments: Segments): any;
8096
_getCopy(segments: Segments): any;
8197
_getDeepCopy(segments: Segments): any;
@@ -172,6 +188,19 @@ Model.prototype.getOrCreateDoc = function(collectionName, id, data) {
172188
return collection.getOrCreateDoc(id, data);
173189
};
174190

191+
Model.prototype.getOrDefault = function<S>(subpath: Path, defaultValue: S) {
192+
return this.get(subpath) ?? defaultValue as ReadonlyDeep<S>;
193+
};
194+
195+
Model.prototype.getOrThrow = function<S>(subpath?: Path) {
196+
const value = this.get(subpath);
197+
if (value == null) {
198+
const fullpath = this.path(subpath);
199+
throw new Error(`No value at path ${fullpath}`)
200+
}
201+
return value;
202+
};
203+
175204
/**
176205
* @param {String} subpath
177206
*/

Diff for: test/Model/collections.js

+50
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,56 @@ const {expect} = require('../util');
22
const {RootModel} = require('../../lib');
33

44
describe('collections', () => {
5+
describe('getOrDefault', () => {
6+
it('returns value if defined', () => {
7+
const model = new RootModel();
8+
model.add('_test_doc', {name: 'foo'});
9+
const value = model.getOrDefault('_test_doc', {name: 'bar'});
10+
expect(value).not.to.be.undefined;
11+
});
12+
13+
it('returns defuault value if undefined', () => {
14+
const model = new RootModel();
15+
const defaultValue = {name: 'bar'};
16+
const value = model.getOrDefault('_test_doc', defaultValue);
17+
expect(value).not.to.be.undefined;
18+
expect(value.name).to.equal('bar');
19+
expect(value).to.eql(defaultValue);
20+
});
21+
22+
it('returns default value if null', () => {
23+
const model = new RootModel();
24+
const id = model.add('_test_doc', {name: null});
25+
const defaultValue = 'bar';
26+
const value = model.getOrDefault(`_test_doc.${id}.name`, defaultValue);
27+
expect(value).not.to.be.null;
28+
expect(value).to.equal('bar');
29+
expect(value).to.eql(defaultValue);
30+
});
31+
});
32+
33+
describe('getOrThrow', () => {
34+
it('returns value if defined', () => {
35+
const model = new RootModel();
36+
model.add('_test_doc', {name: 'foo'});
37+
const value = model.getOrThrow('_test_doc');
38+
expect(value).not.to.be.undefined;
39+
});
40+
41+
it('throws if value undefined', () => {
42+
const model = new RootModel();
43+
expect(() => model.getOrThrow('_test_doc')).to.throw(`No value at path _test_doc`);
44+
expect(() => model.scope('_test').getOrThrow('doc.1')).to.throw(`No value at path _test.doc.1`);
45+
});
46+
47+
it('throws if value null', () => {
48+
const model = new RootModel();
49+
const id = model.add('_test_doc', {name: null});
50+
expect(model.getOrThrow(`_test_doc.${id}`)).to.eql({id, name: null});
51+
expect(() => model.getOrThrow(`_test_doc.${id}.name`)).to.throw(`No value at path _test_doc`);
52+
});
53+
});
54+
555
describe('getValues', () => {
656
it('returns array of values from collection', () => {
757
const model = new RootModel();

0 commit comments

Comments
 (0)