Skip to content

Commit

Permalink
Add function cache to reduce boilerplate
Browse files Browse the repository at this point in the history
  • Loading branch information
jstayton committed Jun 15, 2020
1 parent 3e04fd3 commit 22818ad
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 63 deletions.
11 changes: 4 additions & 7 deletions src/orchestrators/base.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const is = require('is')

const cache = require('../services/cache_function')
const NotImplementedError = require('../errors/not_implemented')
const ValidationError = require('../errors/validation')

Expand All @@ -8,9 +9,9 @@ class BaseOrchestrator {
this.querier = querier

this.parser = this.buildParser()
this._parse = null

this._validate = null
this.validate = cache(this.validate, this)
this.parse = cache(this.parse, this)
}

get queryKey() {
Expand Down Expand Up @@ -50,11 +51,7 @@ class BaseOrchestrator {
return null
}

if (!this._parse) {
this._parse = this.parser.parse()
}

return this._parse
return this.parser.parse()
}

apply(values, querierMethod = null) {
Expand Down
13 changes: 5 additions & 8 deletions src/orchestrators/filterer.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,11 @@ class Filterer extends BaseOrchestrator {
return true
}

if (!this._validate) {
this._validate =
this.parser.validate() &&
this.querier.adapter.validator.validateFilters(this.parse()) &&
this.querier.validator.validate(this.parser.flatten(this.parse()))
}

return this._validate
return (
this.parser.validate() &&
this.querier.adapter.validator.validateFilters(this.parse()) &&
this.querier.validator.validate(this.parser.flatten(this.parse()))
)
}

run() {
Expand Down
13 changes: 5 additions & 8 deletions src/orchestrators/pager.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,11 @@ class Pager extends BaseOrchestrator {
return true
}

if (!this._validate) {
this._validate =
this.parser.validate() &&
this.querier.adapter.validator.validatePage(this.parse()) &&
this.querier.validator.validate(this.parser.flatten(this.parse()))
}

return this._validate
return (
this.parser.validate() &&
this.querier.adapter.validator.validatePage(this.parse()) &&
this.querier.validator.validate(this.parser.flatten(this.parse()))
)
}

run() {
Expand Down
13 changes: 5 additions & 8 deletions src/orchestrators/sorter.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,11 @@ class Sorter extends BaseOrchestrator {
return true
}

if (!this._validate) {
this._validate =
this.parser.validate() &&
this.querier.adapter.validator.validateSorts(this.parse()) &&
this.querier.validator.validate(this.parser.flatten(this.parse()))
}

return this._validate
return (
this.parser.validate() &&
this.querier.adapter.validator.validateSorts(this.parse()) &&
this.querier.validator.validate(this.parser.flatten(this.parse()))
)
}

run() {
Expand Down
10 changes: 4 additions & 6 deletions src/parsers/base.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const cache = require('../services/cache_function')
const NotImplementedError = require('../errors/not_implemented')
const ParserValidator = require('../validators/parser')

Expand All @@ -13,7 +14,8 @@ class BaseParser {
queryKey,
query
)
this._validate = null

this.validate = cache(this.validate, this)
}

buildKey(/* parsed */) {
Expand Down Expand Up @@ -48,11 +50,7 @@ class BaseParser {
}

validate() {
if (!this._validate) {
this._validate = this.validator.validate()
}

return this._validate
return this.validator.validate()
}
}

Expand Down
13 changes: 13 additions & 0 deletions src/services/cache_function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Caches the return value of the first call to the function and returns that on
// subsequent calls. Only works with functions without arguments.
module.exports = (func, bind = undefined) => {
let cache = undefined

return () => {
if (cache === undefined) {
cache = func.call(bind)
}

return cache
}
}
10 changes: 0 additions & 10 deletions test/src/orchestrators/filterer.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,6 @@ describe('validate', () => {
expect(filterer.validate()).toBe(true)
})

test('returns the cached `true` on subsequent calls', () => {
const filterer = new Filterer(
new TestQuerier({ filter: { test: 123 } }, knex('test'))
)

expect(filterer.validate()).toBe(true)
expect(filterer._validate).toBe(true)
expect(filterer.validate()).toBe(true)
})

test('returns `true` if disabled', () => {
const filterer = new Filterer(new TestQuerier({}, knex('test')))

Expand Down
8 changes: 0 additions & 8 deletions test/src/orchestrators/pager.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,6 @@ describe('validate', () => {
expect(pager.validate()).toBe(true)
})

test('returns the cached `true` on subsequent calls', () => {
const pager = new Pager(new TestQuerier({ page: 2 }, knex('test')))

expect(pager.validate()).toBe(true)
expect(pager._validate).toBe(true)
expect(pager.validate()).toBe(true)
})

test('returns `true` if disabled', () => {
const pager = new Pager(new TestQuerier({}, knex('test')))

Expand Down
8 changes: 0 additions & 8 deletions test/src/orchestrators/sorter.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,6 @@ describe('validate', () => {
expect(sorter.validate()).toBe(true)
})

test('returns the cached `true` on subsequent calls', () => {
const sorter = new Sorter(new TestQuerier({ sort: 'test' }, knex('test')))

expect(sorter.validate()).toBe(true)
expect(sorter._validate).toBe(true)
expect(sorter.validate()).toBe(true)
})

test('returns `true` if disabled', () => {
const sorter = new Sorter(new TestQuerier({}, knex('test')))

Expand Down
63 changes: 63 additions & 0 deletions test/src/services/cache_function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const cacheFunction = require('../../../src/services/cache_function')

test('accepts/calls `func` and returns the value', () => {
const value = 'test123'
const func = jest.fn(() => value)

expect(cacheFunction(func)()).toEqual(value)
expect(func).toHaveBeenCalled()
})

test('accepts optional `bind` to set `this` for `func`', () => {
const value = {
value: 'test123',
test() {
return this.value
},
}

expect(cacheFunction(value.test, value)()).toEqual(value.value)
})

test('sets `this` to `undefined` for `func` if `bind` not set', () => {
const value = {
value: 'test123',
test() {
return this.value
},
}

expect(cacheFunction(value.test)()).toBeUndefined()
})

test('returns the cached value on subsequent calls', () => {
const value = 'test123'
const func = jest.fn(() => value)
const cachedFunc = cacheFunction(func)

expect(cachedFunc()).toEqual(value)
expect(cachedFunc()).toEqual(value)
expect(func).toHaveBeenCalledTimes(1)
})

test('does not cache the value if `undefined`', () => {
const value = undefined
const func = jest.fn(() => value)
const cachedFunc = cacheFunction(func)

expect(cachedFunc()).toEqual(value)
expect(cachedFunc()).toEqual(value)
expect(func).toHaveBeenCalledTimes(2)
})

test('does not cache the value if an error is thrown', () => {
const error = new Error('test123')
const func = jest.fn(() => {
throw error
})
const cachedFunc = cacheFunction(func)

expect(() => cachedFunc()).toThrow(error)
expect(() => cachedFunc()).toThrow(error)
expect(func).toHaveBeenCalledTimes(2)
})

0 comments on commit 22818ad

Please sign in to comment.