Skip to content

Commit

Permalink
adds .hasOwn, closes #2
Browse files Browse the repository at this point in the history
  • Loading branch information
jonschlinkert committed Jan 27, 2018
1 parent 974e21c commit 2057414
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 34 deletions.
93 changes: 59 additions & 34 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const isObject = require('isobject');
const Emitter = require('@sellside/emitter');
const visit = require('collection-visit');
const hasOwn = require('has-own-deep');
const union = require('union-value');
const del = require('unset-value');
const get = require('get-value');
Expand Down Expand Up @@ -63,15 +64,13 @@ class Cache extends Emitter {
*/

set(key, val) {
if (Array.isArray(key) && arguments.length === 2) {
key = key.join('.');
}
if (isObject(key) || Array.isArray(key)) {
this.visit('set', key);
} else {
set(this.prop ? this[this.prop] : this, key, val);
this.emit('set', key, val);
return this;
}

set(this.prop ? this[this.prop] : this, key, val);
this.emit('set', key, val);
return this;
}

Expand Down Expand Up @@ -123,46 +122,72 @@ class Cache extends Emitter {
*/

get(key) {
if (Array.isArray(key)) key = key.join('.');
if (arguments.length > 1) {
key = [].concat.apply([], arguments).join('.');
}

const ctx = this.prop ? this[this.prop] : this;
const val = get(ctx, key);

this.emit('get', key, val);
return val;
}

/**
* Return true if cache has a stored value for `key`,
* false only if value is `undefined`.
* Return true if `cache[key]` is not `undefined`.
*
* ```js
* cache.set('foo', 'bar');
* cache.has('foo');
* //=> true
* cache.set('foo', true);
* cache.set('baz', null);
* cache.set('bar', undefined);
*
* cache.has('foo'); //=> true
* cache.has('bar'); //=> true
* cache.has('baz'); //=> false
* ```
*
* @name .has
* @emits `has` with `key` and true or false as arguments.
* @param {String|Array} `key` The name of the property to check. Dot-notation or an array of object path segments may be used.
* @param {String|Array} `key` The name of the property to check. Supports [dot-notation](#dot-notation).
* @return {Boolean}
* @api public
*/

has(key) {
if (Array.isArray(key)) key = key.join('.');

const ctx = this.prop ? this[this.prop] : this;
const val = get(ctx, key);

const has = typeof val !== 'undefined';
const has = typeof get(ctx, key) !== 'undefined';
this.emit('has', key, has);
return has;
}

/**
* Returns a boolean indicating whether the object has the specified property as an
* own (not inherited) property. Similar to [.has()](#has), but returns true even
* when the value is `undefined`.
*
* ```js
* cache.set('a.b.c', 'd');
* cache.set('x', false);
* cache.set('y', null);
* cache.set('z', undefined);
*
* cache.hasOwn('a'); //=> true
* cache.hasOwn('b'); //=> true
* cache.hasOwn('c'); //=> true
* cache.hasOwn('a.b.c'); //=> true
* cache.hasOwn('x'); //=> true
* cache.hasOwn('y'); //=> true
* cache.hasOwn('z'); //=> true
* cache.hasOwn('lslsls'); //=> false
* ```
* @name .hasOwn
* @param {String} `prop`
* @return {Boolean} Returns true if object `prop` exists. Supports [dot-notation](#dot-notation).
* @api public
*/

hasOwn(key) {
const ctx = this.prop ? this[this.prop] : this;
const has = hasOwn(ctx, key);
this.emit('hasOwn', key, has);
return has;
}

/**
* Delete one or more properties from the instance.
*
Expand All @@ -183,10 +208,11 @@ class Cache extends Emitter {
del(key) {
if (Array.isArray(key)) {
this.visit('del', key);
} else {
del(this.prop ? this[this.prop] : this, key);
this.emit('del', key);
return this;
}

del(this.prop ? this[this.prop] : this, key);
this.emit('del', key);
return this;
}

Expand All @@ -202,9 +228,6 @@ class Cache extends Emitter {
*/

visit(key, val) {
if (Array.isArray(key)) {
key = key.join('.');
}
visit(this, key, val);
return this;
}
Expand All @@ -220,12 +243,14 @@ class Cache extends Emitter {

clear() {
if (this.prop) {
this[this.prop] = {};
} else {
for (const key of Object.keys(this)) {
delete this[key];
}
delete this[this.prop];
return this;
}

for (const key of Object.keys(this)) {
delete this[key];
}
return this;
}
}

Expand Down
43 changes: 43 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,49 @@ describe('events', function() {
});
});

describe('hasOwn', function() {
it('should return true if a key exists `.hasOwn()` on the cache', function() {
app.set('foo', 'bar');
app.set('baz', null);
app.set('qux', undefined);

assert(app.hasOwn('foo'));
assert(!app.hasOwn('bar'));
assert(app.hasOwn('baz'));
assert(app.hasOwn('qux'));
});

it('should work with escaped keys', function() {
app.set('foo\\.baz', 'bar');
app.set('baz', null);
app.set('qux', undefined);

assert(!app.hasOwn('foo'));
assert(!app.hasOwn('bar'));
assert(app.hasOwn('foo.baz'));
assert(app.hasOwn('baz'));
assert(app.hasOwn('qux'));
});

it('should return true if a nested key exists `.hasOwn()` on the cache', function() {
app.set('a.b.c.d', { x: 'zzz' });
app.set('a.b.c.e', { f: null });
app.set('a.b.g.j', { k: undefined });

assert(app.hasOwn('a.b.c.d'));
assert(app.hasOwn('a.b.c.d.x'));
assert(app.hasOwn('a.b.c.e.f'));
assert(app.hasOwn('a.b.g.j.k'));
assert(app.hasOwn('a.b.g.j.k'));
assert(app.hasOwn('a.b.c.e.f'));

assert(!app.hasOwn('a.b.bar'));
assert(!app.hasOwn('a.b.c.d.z'));
assert(!app.hasOwn('a.b.c.e.bar'));
assert(!app.hasOwn('a.b.g.j.foo'));
});
});

describe('del', function() {
it('should emit a del event', function(cb) {
app.on('del', () => cb());
Expand Down

0 comments on commit 2057414

Please sign in to comment.