diff --git a/.eslintrc b/.eslintrc index 0026929..5248af3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,3 @@ { - "extends": "groupon/legacy" + "extends": "groupon/node4" } diff --git a/.gitignore b/.gitignore index 0138158..aea58fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/yarn.lock +/package-lock.json node_modules/ npm-debug.log /tmp diff --git a/.travis.yml b/.travis.yml index ac742ed..c6c63bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,17 +1,13 @@ language: node_js node_js: - - '0.10' - - '4' -before_install: - - npm install -g npm@latest-2 -before_deploy: - - 'git config --global user.email "opensource@groupon.com"' - - 'git config --global user.name "Groupon"' + - 4.6.1 + - 6.11.5 + - 8.9.0 deploy: - provider: script - script: ./node_modules/.bin/nlm release - skip_cleanup: true - 'on': - branch: master - node: '4' + - provider: script + script: ./node_modules/.bin/nlm release + skip_cleanup: true + 'on': + branch: master + node: 8.9.0 services: memcached diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 419c8c5..8461db7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ - + # Contributing @@ -8,6 +8,7 @@ This document outlines some of the practices we care about. If you have any questions or suggestions about the process, feel free to [open an issue](#reporting-issues) . + ## How Can I Contribute? ### Reporting Issues @@ -51,7 +52,7 @@ The general steps for creating a pull request are: 1. If you're fixing a bug, be sure to write a test *first*. That way you can validate that the test actually catches the bug and doesn't pass. 1. Make your changes to the code. - Remember to update the tests if you add new features or change behavior. + Remember to update the tests if you add new features or change behavior. 1. Run the tests via `npm test`. This will also run style checks and other validations. You might see errors about uncommitted files. This is expected until you commit your changes. diff --git a/lib/backend.js b/lib/backend.js index 26a795d..1255d25 100644 --- a/lib/backend.js +++ b/lib/backend.js @@ -29,9 +29,10 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; -var typeMap = Object.create(null); +const typeMap = Object.create(null); function isBackend(object) { return typeof object.get === 'function' && typeof object.set === 'function'; @@ -44,10 +45,10 @@ exports.create = function create(options) { return options; } - var type = options.type || 'noop'; - var BackendClass = typeMap[type]; + const type = options.type || 'noop'; + const BackendClass = typeMap[type]; if (!BackendClass) { - throw new Error(type + ' is not a supported cache backend type'); + throw new Error(`${type} is not a supported cache backend type`); } return new BackendClass(options); }; diff --git a/lib/backends/memcached.js b/lib/backends/memcached.js index d6ee2a3..8b03694 100644 --- a/lib/backends/memcached.js +++ b/lib/backends/memcached.js @@ -29,17 +29,18 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; -var promisify = require('bluebird').promisify; -var _ = require('lodash'); -var Memcached = require('memcached'); +const promisify = require('bluebird').promisify; +const _ = require('lodash'); +const Memcached = require('memcached'); function createClient(options) { if (options.client) { return options.client; } - var hosts = options.hosts || '127.0.0.1:11211'; + const hosts = options.hosts || '127.0.0.1:11211'; return new Memcached(hosts, options); } @@ -50,7 +51,7 @@ function normalizeValue(value) { /* Uses anything supporting the memcache protocol */ function MemcachedBackend(options) { this.type = 'memcached'; - var client = this.client = createClient(options); + const client = (this.client = createClient(options)); this._clientGet = promisify(client.get, { context: client }); this._clientSet = promisify(client.set, { context: client }); this._clientDel = promisify(client.del, { context: client }); @@ -62,8 +63,7 @@ MemcachedBackend.prototype.get = function get(key) { }; MemcachedBackend.prototype.set = function set(key, value, options) { - return this._clientSet(key, value, options.expire) - .then(_.constant(value)); + return this._clientSet(key, value, options.expire).then(_.constant(value)); }; MemcachedBackend.prototype.unset = function unset(key) { diff --git a/lib/backends/memory.js b/lib/backends/memory.js index b3fae23..2f3bc0e 100644 --- a/lib/backends/memory.js +++ b/lib/backends/memory.js @@ -29,11 +29,12 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; -var Bluebird = require('bluebird'); +const Bluebird = require('bluebird'); -var util = require('../util'); +const util = require('../util'); /* Stores everything just in memory */ function MemoryBackend() { @@ -43,7 +44,7 @@ function MemoryBackend() { module.exports = MemoryBackend; MemoryBackend.prototype.get = function get(key) { - var wrappedValue = this.cache[key] || null; + let wrappedValue = this.cache[key] || null; if (util.isExpired(wrappedValue && wrappedValue.e)) { wrappedValue = null; delete this.cache[key]; diff --git a/lib/backends/noop.js b/lib/backends/noop.js index 6454b9a..dbcf862 100644 --- a/lib/backends/noop.js +++ b/lib/backends/noop.js @@ -29,9 +29,10 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; -var Bluebird = require('bluebird'); +const Bluebird = require('bluebird'); /* Simple backend doing nothing */ function NoopBackend() { diff --git a/lib/cache.js b/lib/cache.js index 354883c..530c1f4 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -29,13 +29,16 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* eslint-disable no-underscore-dangle */ + 'use strict'; -var _ = require('lodash'); +const _ = require('lodash'); -var Backend = require('./backend'); -var getOrElse = require('./get-or-else'); -var util = require('./util'); +const Backend = require('./backend'); +const getOrElse = require('./get-or-else'); +const util = require('./util'); function Cache(options) { this.defaults = { @@ -43,7 +46,7 @@ function Cache(options) { expire: 0, }; this.name = options.name || 'default'; - this.prefix = this.name + ':'; + this.prefix = `${this.name}:`; this.staleOrPending = {}; this.setDefaults(options.defaults); @@ -60,9 +63,12 @@ Cache.prototype.setDefaults = function setDefaults(defaults) { }; Cache.prototype.setBackend = function setBackend(backendOptions) { - backendOptions = typeof backendOptions === 'string' ? { - type: backendOptions, - } : backendOptions || {}; + backendOptions = + typeof backendOptions === 'string' + ? { + type: backendOptions, + } + : backendOptions || {}; this.end(); this.backend = Backend.create(backendOptions); return this.backend; @@ -79,27 +85,31 @@ Cache.prototype.prepareOptions = function prepareOptions(options) { }; Cache.prototype._set = function _set(key, val, options) { - var self = this; + const self = this; function writeToBackend(resolvedValue) { - return self.backend.set(key, { - b: util.expiresAt(options.freshFor), - d: resolvedValue, - }, options); + return self.backend.set( + key, + { + b: util.expiresAt(options.freshFor), + d: resolvedValue, + }, + options + ); } return this._applyTimeout(util.toPromise(val).then(writeToBackend)); }; Cache.prototype.set = function set(rawKey, val, _opts, _cb) { - var args = util.optionalOpts(_opts, _cb); - var key = this.applyPrefix(rawKey); - var optsWithDefaults = this.prepareOptions(args.opts); + const args = util.optionalOpts(_opts, _cb); + const key = this.applyPrefix(rawKey); + const optsWithDefaults = this.prepareOptions(args.opts); return this._set(key, val, optsWithDefaults).nodeify(args.cb); }; Cache.prototype._applyTimeout = function _applyTimeout(value) { - var timeoutMs = this.defaults.timeout; + const timeoutMs = this.defaults.timeout; if (timeoutMs > 0) { return value.timeout(timeoutMs); } @@ -114,21 +124,23 @@ Cache.prototype._getWrapped = function _getWrapped(key) { Cache.prototype.getWrapped = Cache.prototype._getWrapped; Cache.prototype.get = function get(rawKey, cb) { - var key = this.applyPrefix(rawKey); + const key = this.applyPrefix(rawKey); - return this._getWrapped(key).then(util.extractValue).nodeify(cb); + return this._getWrapped(key) + .then(util.extractValue) + .nodeify(cb); }; Cache.prototype.getOrElse = function _getOrElse(rawKey, val, _opts, _cb) { - var key = this.applyPrefix(rawKey); - var args = util.optionalOpts(_opts, _cb); - var optsWithDefaults = this.prepareOptions(args.opts); + const key = this.applyPrefix(rawKey); + const args = util.optionalOpts(_opts, _cb); + const optsWithDefaults = this.prepareOptions(args.opts); return getOrElse(this, key, val, optsWithDefaults).nodeify(args.cb); }; Cache.prototype.unset = function unset(rawKey, cb) { - var key = this.applyPrefix(rawKey); + const key = this.applyPrefix(rawKey); return this._unset(key).nodeify(cb); }; diff --git a/lib/cached.js b/lib/cached.js index b265151..95779b6 100644 --- a/lib/cached.js +++ b/lib/cached.js @@ -29,17 +29,18 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; -var Bluebird = require('bluebird'); -var _ = require('lodash'); -var util = require('util'); +const Bluebird = require('bluebird'); +const _ = require('lodash'); +const util = require('util'); -var Cache = require('./cache'); +const Cache = require('./cache'); -var namedCaches = Object.create(null); +let namedCaches = Object.create(null); -var getName = util.deprecate(function getName(name) { +const getName = util.deprecate(function getName(name) { return name || 'default'; }, 'Unnamed caches and caches with non-string names are deprecated.'); @@ -49,9 +50,14 @@ function cached(name, options) { } if (!(name in namedCaches)) { - namedCaches[name] = cached.createCache(_.extend({ - name: name, - }, options || {})); + namedCaches[name] = cached.createCache( + _.extend( + { + name: name, + }, + options || {} + ) + ); } return namedCaches[name]; } diff --git a/lib/get-or-else.js b/lib/get-or-else.js index 35b48b8..f8c55f1 100644 --- a/lib/get-or-else.js +++ b/lib/get-or-else.js @@ -29,12 +29,15 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* eslint-disable no-underscore-dangle */ + 'use strict'; -var Bluebird = require('bluebird'); -var _ = require('lodash'); +const Bluebird = require('bluebird'); +const _ = require('lodash'); -var util = require('./util'); +const util = require('./util'); function getOrElse(cache, key, val, opts) { function resetStaleOrPending(passThroughData) { @@ -47,13 +50,14 @@ function getOrElse(cache, key, val, opts) { return generatedValue === undefined ? null : generatedValue; } - return cache._set(key, generatedValue, opts) + return cache + ._set(key, generatedValue, opts) .then(util.extractValue) .catch(fallbackToLoadedValue); } function refreshValue() { - var refreshed; + let refreshed; function handleWriteError(err) { resetStaleOrPending(); @@ -63,7 +67,8 @@ function getOrElse(cache, key, val, opts) { return null; } - refreshed = util.toPromise(val) + refreshed = util + .toPromise(val) .then(writeValue) .then(resetStaleOrPending, handleWriteError); @@ -71,17 +76,17 @@ function getOrElse(cache, key, val, opts) { } function verifyFreshness(wrappedValue) { - var hit = !!wrappedValue; - var expired = util.isExpired(wrappedValue && wrappedValue.b); - var loadingNewValue = cache.staleOrPending[key] !== undefined; + const hit = !!wrappedValue; + const expired = util.isExpired(wrappedValue && wrappedValue.b); + let loadingNewValue = cache.staleOrPending[key] !== undefined; if ((!hit || expired) && !loadingNewValue) { cache.staleOrPending[key] = refreshValue(); loadingNewValue = true; } - var dataFromCache = util.extractValue(wrappedValue); - if ((dataFromCache === null) && loadingNewValue) { + const dataFromCache = util.extractValue(wrappedValue); + if (dataFromCache === null && loadingNewValue) { cache.staleOrPending[key].wasEverReturned = true; return cache.staleOrPending[key]; } @@ -89,7 +94,8 @@ function getOrElse(cache, key, val, opts) { return dataFromCache; } - return cache._getWrapped(key) + return cache + ._getWrapped(key) .catch(_.constant(null)) .then(verifyFreshness); } diff --git a/lib/util.js b/lib/util.js index d8fc893..e5dc8a8 100644 --- a/lib/util.js +++ b/lib/util.js @@ -29,9 +29,10 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + 'use strict'; -var Bluebird = require('bluebird'); +const Bluebird = require('bluebird'); exports.expiresAt = function expiresAt(seconds) { if (seconds === 0) { @@ -48,7 +49,7 @@ exports.isExpired = function isExpired(expires) { }; exports.extractValue = function extractValue(wrappedValue) { - var value = wrappedValue && wrappedValue.d; + const value = wrappedValue && wrappedValue.d; // Normalize `undefined` into `null` return value === undefined ? null : value; }; diff --git a/package.json b/package.json index e8f4b37..c3f6c1d 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "homepage": "https://github.com/groupon/node-cached", "repository": { "type": "git", - "url": "git+ssh://git@github.com/groupon/node-cached" + "url": "https://github.com/groupon/node-cached" }, "bugs": { "url": "https://github.com/groupon/node-cached/issues" @@ -31,12 +31,19 @@ "memcached": "^2.1.0" }, "devDependencies": { - "assertive": "^2.0.0", + "assertive": "^2.1.0", + "babel-cli": "^6.24.1", "babel-core": "^5.8.33", - "eslint": "^1.0.0", - "eslint-config-groupon": "^2.0.0", - "mocha": "^2.0.0", - "nlm": "^2.0.0" + "babel-eslint": "^8.1.2", + "babel-preset-env": "^1.6.0", + "eslint": "^4.7.1", + "eslint-config-groupon": "^5.0.0", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-node": "^5.2.1", + "eslint-plugin-prettier": "^2.2.0", + "mocha": "^3.1.2", + "nlm": "^3.0.0", + "prettier": "^1.6.1" }, "author": { "name": "Groupon", diff --git a/test/.eslintrc b/test/.eslintrc index ef732be..249ac66 100644 --- a/test/.eslintrc +++ b/test/.eslintrc @@ -1,9 +1,12 @@ { - "extends": "groupon/base", "env": { "mocha": true }, - "rules": { - "func-names": 0 - } + "parser": "babel-eslint", + "parserOptions": { + "sourceType": "module", + "allowImportExportEverywhere": true, + "codeFrame": false + }, + "root": true } diff --git a/test/cached.test.js b/test/cached.test.js index 311eda1..9b4cb93 100644 --- a/test/cached.test.js +++ b/test/cached.test.js @@ -5,8 +5,7 @@ import cached from '..'; describe('cached', () => { beforeEach(() => cached.dropNamedCaches()); - it('is a function', () => - assert.hasType(Function, cached)); + it('is a function', () => assert.hasType(Function, cached)); it('starts out with no known caches', () => assert.deepEqual([], cached.knownCaches())); diff --git a/test/get-or-else.test.js b/test/get-or-else.test.js index 5f761e7..ec215a0 100644 --- a/test/get-or-else.test.js +++ b/test/get-or-else.test.js @@ -25,8 +25,9 @@ describe('Cache::getOrElse', () => { } it('falls back on the value refresher', async () => { - const value = - await cache.getOrElse('bad_get', generateCats, { freshFor: 5 }); + const value = await cache.getOrElse('bad_get', generateCats, { + freshFor: 5, + }); assert.equal('fresh cats', value); }); }); @@ -35,8 +36,7 @@ describe('Cache::getOrElse', () => { let cache; before(() => { cache = new Cache({ backend: 'memory', name: 'awesome-name' }); - cache.set = () => - Bluebird.reject(new Error('backend set troubles')); + cache.set = () => Bluebird.reject(new Error('backend set troubles')); }); function generateBunnies() { @@ -44,8 +44,9 @@ describe('Cache::getOrElse', () => { } it('falls back on the generated value', async () => { - const value = - await cache.getOrElse('bad_set', generateBunnies, { freshFor: 5 }); + const value = await cache.getOrElse('bad_set', generateBunnies, { + freshFor: 5, + }); assert.equal('generated bunnies', value); }); }); @@ -73,8 +74,12 @@ describe('Cache::getOrElse', () => { // 4. The value is stale, so it should be calling the value generator. // But it should *return* the original value asap. - assert.equal(originalValue, - await cache.getOrElse('key1', valueGenerator('G1', 100), { freshFor: 5 })); + assert.equal( + originalValue, + await cache.getOrElse('key1', valueGenerator('G1', 100), { + freshFor: 5, + }) + ); // Let the generator be generating... await Bluebird.delay(50); @@ -82,16 +87,24 @@ describe('Cache::getOrElse', () => { // 5. Generating 'G1' in the last step takes 100ms but we only waited 50ms yet. // This means we still expect to see the original value. // 'G2' should never be generated since there's already a pending value. - assert.equal(originalValue, - await cache.getOrElse('key1', valueGenerator('G2', 5000), { freshFor: 5 })); + assert.equal( + originalValue, + await cache.getOrElse('key1', valueGenerator('G2', 5000), { + freshFor: 5, + }) + ); // Let the generator be generating... await Bluebird.delay(100); // 6. Now G1 is done generating (we waited a total of 150ms), so we shouldn't // see the original value anymore but the new, improved 'G1'. - assert.equal('G1', - await cache.getOrElse('key1', valueGenerator('G3', 5000), { freshFor: 5 })); + assert.equal( + 'G1', + await cache.getOrElse('key1', valueGenerator('G3', 5000), { + freshFor: 5, + }) + ); // 7. Making sure that the value generator was only called once during this test. // We just generated 'G1', the other times we either had a pending value @@ -105,7 +118,8 @@ describe('Cache::getOrElse', () => { } const error = await assertRejects( - cache.getOrElse('bad_keys', errorGenerator, { freshFor: 1 })); + cache.getOrElse('bad_keys', errorGenerator, { freshFor: 1 }) + ); assert.equal('Big Error', error.message); }); @@ -114,7 +128,8 @@ describe('Cache::getOrElse', () => { const value = 'refresh-value'; before('set value that is stale after a second', () => - cache.set(key, value, { freshFor: 1, expire: 3 })); + cache.set(key, value, { freshFor: 1, expire: 3 }) + ); before('wait >1 seconds', () => Bluebird.delay(1100)); diff --git a/test/get-set.test.js b/test/get-set.test.js index 611d251..3e40c46 100644 --- a/test/get-set.test.js +++ b/test/get-set.test.js @@ -26,7 +26,6 @@ describe('Cache::{get,set,unset}', () => { assert.equal('promise-value', await cache.get('promise-key')); }); - it('set/unset (callback style', done => { cache.set('callback-key', 'callback-value', setError => { if (setError) return done(setError); @@ -67,8 +66,10 @@ describe('Cache::{get,set,unset}', () => { await Bluebird.delay(2000); - const [expired, eternal, hit] = - await Bluebird.map(['key', 'key2', 'key3'], key => cache.get(key)); + const [expired, eternal, hit] = await Bluebird.map( + ['key', 'key2', 'key3'], + key => cache.get(key) + ); assert.equal(null, expired); assert.equal(values.key2, eternal); diff --git a/test/timeout.test.js b/test/timeout.test.js index b8d9a91..6c2a221 100644 --- a/test/timeout.test.js +++ b/test/timeout.test.js @@ -19,7 +19,7 @@ describe('Cache timeouts', () => { }); describe('with a timeout <150ms', () => { - before(() => cache.defaults.timeout = 50); + before(() => (cache.defaults.timeout = 50)); it('get fails fast', async () => { const err = await Bluebird.race([ @@ -51,7 +51,7 @@ describe('Cache timeouts', () => { }); describe('with a timeout >150ms', () => { - before(() => cache.defaults.timeout = 250); + before(() => (cache.defaults.timeout = 250)); it('receives the value', async () => { const value = await Bluebird.race([