Skip to content

Commit 29daf3f

Browse files
authored
Merge pull request #55 from cwillisf/storage-helper-priorities
feat: create `addHelper` to add helper w/ priority
2 parents 9ec8c38 + c7bba01 commit 29daf3f

File tree

2 files changed

+109
-1
lines changed

2 files changed

+109
-1
lines changed

src/ScratchStorage.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ class ScratchStorage {
1414
this.builtinHelper = new BuiltinHelper(this);
1515
this.webHelper = new WebHelper(this);
1616
this.builtinHelper.registerDefaultAssets(this);
17+
18+
this._helpers = [
19+
{
20+
helper: this.builtinHelper,
21+
priority: 100
22+
},
23+
{
24+
helper: this.webHelper,
25+
priority: -100
26+
}
27+
];
1728
}
1829

1930
/**
@@ -58,6 +69,18 @@ class ScratchStorage {
5869
return _AssetType;
5970
}
6071

72+
/**
73+
* Add a storage helper to this manager. Helpers with a higher priority number will be checked first when loading
74+
* or storing assets. For comparison, the helper for built-in assets has `priority=100` and the default web helper
75+
* has `priority=-100`. The relative order of helpers with equal priorities is undefined.
76+
* @param {Helper} helper - the helper to be added.
77+
* @param {number} [priority] - the priority for this new helper (default: 0).
78+
*/
79+
addHelper (helper, priority = 0) {
80+
this._helpers.push({helper, priority});
81+
this._helpers.sort((a, b) => b.priority - a.priority);
82+
}
83+
6184
/**
6285
* Synchronously fetch a cached asset from built-in storage. Assets are cached when they are loaded.
6386
* @param {string} assetId - The id of the asset to fetch.
@@ -152,7 +175,7 @@ class ScratchStorage {
152175
*/
153176
load (assetType, assetId, dataFormat) {
154177
/** @type {Helper[]} */
155-
const helpers = [this.builtinHelper, this.webHelper];
178+
const helpers = this._helpers.map(x => x.helper);
156179
const errors = [];
157180
let helperIndex = 0;
158181
dataFormat = dataFormat || assetType.runtimeFormat;

test/unit/add-helper.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
const test = require('tap').test;
2+
3+
const ScratchStorage = require('../../dist/node/scratch-storage');
4+
5+
/**
6+
* Simulate a storage helper, adding log messages when "load" is called rather than actually loading anything.
7+
*/
8+
class LoggingHelper {
9+
/**
10+
* Construct a LoggingHelper instance.
11+
* @param {Storage} storage - An instance of the storage module.
12+
* @param {string} label - A label for this instance.
13+
* @param {boolean} shouldSucceed - set to true to make `load` always succeed, or false to make `load` always fail.
14+
* @param {Array.<string>} logContainer - an array in which log messages will be stored.
15+
* @constructor
16+
*/
17+
constructor (storage, label, shouldSucceed, logContainer) {
18+
this.storage = storage;
19+
this.label = label;
20+
this.shouldSucceed = shouldSucceed;
21+
this.logContainer = logContainer;
22+
}
23+
24+
/**
25+
* Pretend to fetch an asset, but instead add a message to the log container.
26+
* @param {AssetType} assetType - The type of asset to fetch.
27+
* @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
28+
* @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
29+
* @return {Promise.<Asset>} A promise for the contents of the asset.
30+
*/
31+
load (assetType, assetId, dataFormat) {
32+
this.logContainer.push(this.label);
33+
return this.shouldSucceed ?
34+
Promise.resolve(new this.storage.Asset(assetType, assetId, dataFormat, Buffer.from(this.label))) :
35+
Promise.reject(`This is an expected failure from ${this.label}`);
36+
}
37+
}
38+
39+
test('ScratchStorage constructor', t => {
40+
const storage = new ScratchStorage();
41+
t.type(storage, ScratchStorage);
42+
t.end();
43+
});
44+
45+
test('LoggingHelper constructor', t => {
46+
const storage = new ScratchStorage();
47+
const loggingHelper = new LoggingHelper(storage, 'constructor test', true, []);
48+
t.type(loggingHelper, LoggingHelper);
49+
t.end();
50+
});
51+
52+
test('addHelper', t => {
53+
const logContainer = [];
54+
const storage = new ScratchStorage();
55+
56+
const initialHelperCount = storage._helpers.length;
57+
58+
// The first two helpers should fail (shouldSucceed=false) so that the storage module continues through the list.
59+
// The third helper should succeed (shouldSucceed=true) so that the overall load succeeds.
60+
const loggingHelpers = [
61+
new LoggingHelper(storage, 'first', false, logContainer),
62+
new LoggingHelper(storage, 'second', false, logContainer),
63+
new LoggingHelper(storage, 'third', true, logContainer)
64+
];
65+
66+
// Add out of order to check that the priority values are respected
67+
storage.addHelper(loggingHelpers[2], -50);
68+
storage.addHelper(loggingHelpers[0], 50);
69+
storage.addHelper(loggingHelpers[1], 0);
70+
71+
// Did they all get added?
72+
t.equal(storage._helpers.length, initialHelperCount + loggingHelpers.length);
73+
74+
// We shouldn't have any log entries yet
75+
t.deepEqual(logContainer, []);
76+
77+
return storage.load(storage.AssetType.Project, '0').then(() => {
78+
// Verify that all helpers were consulted, and in the correct order
79+
t.deepEqual(logContainer, [
80+
'first',
81+
'second',
82+
'third'
83+
]);
84+
});
85+
});

0 commit comments

Comments
 (0)