From 545926c1e373ba7381b46a462ca7c3ec7d311b38 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Mon, 3 Dec 2018 11:58:22 -0500 Subject: [PATCH 1/2] Editor: Add createWeakCache utility --- .../editor/src/utils/create-weak-cache.js | 25 +++++++++++ .../src/utils/test/create-weak-cache.js | 41 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 packages/editor/src/utils/create-weak-cache.js create mode 100644 packages/editor/src/utils/test/create-weak-cache.js diff --git a/packages/editor/src/utils/create-weak-cache.js b/packages/editor/src/utils/create-weak-cache.js new file mode 100644 index 00000000000000..455a89b201ac22 --- /dev/null +++ b/packages/editor/src/utils/create-weak-cache.js @@ -0,0 +1,25 @@ +/** + * Returns a function which caches a computed value on a given object key. Due + * to its caching being achieved by WeakCache, the function must only accept a + * valid WeakMap key as its argument (objects, arrays). The function is only + * passed the key; any other arguments are discarded. + * + * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap + * + * @param {Function} getCacheValue Function to compute the cache value. + * + * @return {Function} Function whose computed value is weakly cached. + */ +function createWeakCache( getCacheValue ) { + const cache = new WeakMap(); + + return ( key ) => { + if ( ! cache.has( key ) ) { + cache.set( key, getCacheValue( key ) ); + } + + return cache.get( key ); + }; +} + +export default createWeakCache; diff --git a/packages/editor/src/utils/test/create-weak-cache.js b/packages/editor/src/utils/test/create-weak-cache.js new file mode 100644 index 00000000000000..4ca1aeebb00c99 --- /dev/null +++ b/packages/editor/src/utils/test/create-weak-cache.js @@ -0,0 +1,41 @@ +/** + * Internal dependencies + */ +import createWeakCache from '../create-weak-cache'; + +describe( 'createWeakCache', () => { + const getNumKeys = jest.fn().mockImplementation( ( object ) => Object.keys( object ).length ); + const getNumKeysCached = createWeakCache( getNumKeys ); + + beforeEach( () => { + getNumKeys.mockClear(); + } ); + + it( 'should return the value from the function argument', () => { + const object = { a: 1 }; + + expect( getNumKeysCached( object ) ).toBe( 1 ); + } ); + + it( 'should return the value from cache', () => { + const object = { a: 1 }; + + expect( getNumKeysCached( object ) ).toBe( 1 ); + expect( getNumKeysCached( object ) ).toBe( 1 ); + expect( getNumKeys ).toHaveBeenCalledTimes( 1 ); + } ); + + it( 'should return the value from the function argument on new reference', () => { + expect( getNumKeysCached( { a: 1 } ) ).toBe( 1 ); + expect( getNumKeysCached( { a: 1 } ) ).toBe( 1 ); + expect( getNumKeys ).toHaveBeenCalledTimes( 2 ); + } ); + + it( 'should throw on invalid key argument', () => { + expect( () => getNumKeysCached( undefined ) ).toThrow(); + } ); + + it( 'should discard additional arguments', () => { + expect( createWeakCache( ( a, b ) => b || 'ok' )( {}, 10 ) ).toBe( 'ok' ); + } ); +} ); From 3ee21cda760c29db5334da2786f1428a9ddc368b Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Mon, 3 Dec 2018 11:58:44 -0500 Subject: [PATCH 2/2] Editor: Cache block meta-sourcing for getBlock selector --- packages/editor/src/store/selectors.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index d8b019930d1a07..bc4934a47fd6d6 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -40,6 +40,7 @@ import { addQueryArgs } from '@wordpress/url'; */ import { PREFERENCES_DEFAULTS } from './defaults'; import { EDIT_MERGE_PROPERTIES } from './constants'; +import createWeakCache from '../utils/create-weak-cache'; /*** * Module constants @@ -66,6 +67,21 @@ const ONE_MINUTE_IN_MS = 60 * 1000; */ const EMPTY_ARRAY = []; +/** + * Given a block type object, returns true if an attribute value is sourced + * from a meta property, or false otherwise. + * + * The value is cached weakly by strict object reference to the provided block + * type argument. + * + * @param {WPBlockType} blockType Block type against which to test. + * + * @return {boolean} Whether block type has an meta-sourced attribute. + */ +const hasMetaSourcedAttribute = createWeakCache( ( blockType ) => { + return some( blockType.attributes, { source: 'meta' } ); +} ); + /** * Returns true if any past editor history snapshots exist, or false otherwise. * @@ -649,7 +665,7 @@ export const getBlockAttributes = createSelector( // TODO: Create generic external sourcing pattern, not explicitly // targeting meta attributes. const type = getBlockType( block.name ); - if ( type ) { + if ( type && hasMetaSourcedAttribute( type ) ) { attributes = reduce( type.attributes, ( result, value, key ) => { if ( value.source === 'meta' ) { if ( result === attributes ) {