Skip to content

Commit

Permalink
createResource returns an object with methods instead of a read funct…
Browse files Browse the repository at this point in the history
…ion (facebook#12304)

Changes `createResource` to return an object with `read` and `preload`
methods. Future methods may include `set`, `subscribe`, `invalidate`,
and so on.
  • Loading branch information
acdlite authored Feb 28, 2018
1 parent 86d04f6 commit 2cf9063
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 57 deletions.
71 changes: 36 additions & 35 deletions packages/simple-cache-provider/src/SimpleCacheProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,11 @@ if (__DEV__) {
}

type primitive = string | number | boolean | void | null;
type ResourceReader<K, V> = (Cache, K) => V;

type Resource<K, V> = ResourceReader<K, V> & {
type Resource<K, V> = {|
read(Cache, K): V,
preload(cache: Cache, key: K): void,
};
|};

// These declarations are used to express function overloading. I wish there
// were a more elegant way to do this in the function definition itself.
Expand All @@ -252,43 +252,44 @@ export function createResource<V, K, H: primitive>(
loadResource: K => Promise<V>,
hash: K => H,
): Resource<K, V> {
// The read function itself serves as the resource type.
function read(cache, key) {
if (__DEV__) {
warning(
isCache(cache),
'read(): The first argument must be a cache. Instead received: %s',
cache,
);
}
if (hash === undefined) {
const resource = {
read(cache, key) {
if (__DEV__) {
warnIfNonPrimitiveKey(key, 'read');
warning(
isCache(cache),
'read(): The first argument must be a cache. Instead received: %s',
cache,
);
}
return cache.read(read, key, loadResource, key);
}
const hashedKey = hash(key);
return cache.read(read, hashedKey, loadResource, key);
}
read.preload = function(cache, key) {
if (__DEV__) {
warning(
isCache(cache),
'preload(): The first argument must be a cache. Instead received: %s',
cache,
);
}
if (hash === undefined) {
if (hash === undefined) {
if (__DEV__) {
warnIfNonPrimitiveKey(key, 'read');
}
return cache.read(resource, key, loadResource, key);
}
const hashedKey = hash(key);
return cache.read(resource, hashedKey, loadResource, key);
},
preload(cache, key) {
if (__DEV__) {
warnIfNonPrimitiveKey(key, 'preload');
warning(
isCache(cache),
'preload(): The first argument must be a cache. Instead received: %s',
cache,
);
}
cache.preload(read, key, loadResource, key);
return;
}
const hashedKey = hash(key);
cache.preload(read, hashedKey, loadResource, key);
if (hash === undefined) {
if (__DEV__) {
warnIfNonPrimitiveKey(key, 'preload');
}
cache.preload(resource, key, loadResource, key);
return;
}
const hashedKey = hash(key);
cache.preload(resource, hashedKey, loadResource, key);
},
};
return read;
return resource;
}

// Global cache has no eviction policy (except for, ya know, a browser refresh).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ describe('SimpleCacheProvider', () => {
function loadUpperCase(text) {
return Promise.resolve(text.toUpperCase());
}
const readUpperCase = createResource(loadUpperCase);
const UpperCase = createResource(loadUpperCase);
const cache = createCache();

let suspender;
try {
readUpperCase(cache, 'hello');
UpperCase.read(cache, 'hello');
} catch (v) {
suspender = v;
}

await suspender;
const result = readUpperCase(cache, 'hello');
const result = UpperCase.read(cache, 'hello');
expect(result).toBe('HELLO');
});

Expand All @@ -52,12 +52,12 @@ describe('SimpleCacheProvider', () => {
return Promise.resolve(text.toUpperCase());
}
}
const readUpperCase = createResource(loadUpperCase);
const UpperCase = createResource(loadUpperCase);
const cache = createCache();

let suspender;
try {
readUpperCase(cache, 'hello');
UpperCase.read(cache, 'hello');
} catch (v) {
suspender = v;
}
Expand All @@ -68,17 +68,17 @@ describe('SimpleCacheProvider', () => {
} catch (e) {
error = e;
}
expect(() => readUpperCase(cache, 'hello')).toThrow(error);
expect(() => UpperCase.read(cache, 'hello')).toThrow(error);
expect(error.message).toBe('oh no');

// On a subsequent read, it should still throw.
try {
readUpperCase(cache, 'hello');
UpperCase.read(cache, 'hello');
} catch (v) {
suspender = v;
}
await suspender;
expect(() => readUpperCase(cache, 'hello')).toThrow(error);
expect(() => UpperCase.read(cache, 'hello')).toThrow(error);
expect(error.message).toBe('oh no');
});

Expand All @@ -88,13 +88,13 @@ describe('SimpleCacheProvider', () => {
function loadUpperCase(text) {
return Promise.resolve(text.toUpperCase());
}
const readUpperCase = createResource(loadUpperCase);
const UpperCase = createResource(loadUpperCase);
const cache = createCache();

readUpperCase.preload(cache, 'hello');
UpperCase.preload(cache, 'hello');
// Wait for next tick
await Promise.resolve();
const result = readUpperCase(cache, 'hello');
const result = UpperCase.read(cache, 'hello');
expect(result).toBe('HELLO');
});

Expand All @@ -104,14 +104,14 @@ describe('SimpleCacheProvider', () => {
function loadUpperCase(text) {
return Promise.reject(new Error('uh oh'));
}
const readUpperCase = createResource(loadUpperCase);
const UpperCase = createResource(loadUpperCase);
const cache = createCache();

readUpperCase.preload(cache, 'hello');
UpperCase.preload(cache, 'hello');
// Wait for next tick
await Promise.resolve();

expect(() => readUpperCase(cache, 'hello')).toThrow('uh oh');
expect(() => UpperCase.read(cache, 'hello')).toThrow('uh oh');
});

it('accepts custom hash function', async () => {
Expand All @@ -123,18 +123,18 @@ describe('SimpleCacheProvider', () => {
function hash([a, b]) {
return `${a + b}`;
}
const readSum = createResource(loadSum, hash);
const Sum = createResource(loadSum, hash);
const cache = createCache();

readSum.preload(cache, [5, 5]);
readSum.preload(cache, [1, 2]);
Sum.preload(cache, [5, 5]);
Sum.preload(cache, [1, 2]);
await Promise.resolve();

expect(readSum(cache, [5, 5])).toEqual(10);
expect(readSum(cache, [1, 2])).toEqual(3);
expect(Sum.read(cache, [5, 5])).toEqual(10);
expect(Sum.read(cache, [1, 2])).toEqual(3);
// The fact that the next line returns synchronously and doesn't throw, even
// though [3, 7] was not preloaded, proves that the hashing function works.
expect(readSum(cache, [3, 7])).toEqual(10);
expect(Sum.read(cache, [3, 7])).toEqual(10);
});

it('warns if resourceType is a string or number', () => {
Expand Down Expand Up @@ -171,11 +171,11 @@ describe('SimpleCacheProvider', () => {
return Promise.resolve(a + b);
}

const readSum = createResource(loadSum);
const Sum = createResource(loadSum);
const cache = createCache();

function fn() {
readSum.preload(cache, [5, 5]);
Sum.preload(cache, [5, 5]);
}

if (__DEV__) {
Expand Down

0 comments on commit 2cf9063

Please sign in to comment.