chore: 🤖 add storage service with support for memory and local
This commit is contained in:
5 files changed
+174
-1
No files matched your search
@@ -0,0 +1,129 @@
|
||||
import Service from '@ember/service';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
/**
|
||||
* A dead simple memory storage class used by `StorageService` when it
|
||||
* is configued for memory-only.
|
||||
*/
|
||||
class MemoryStorage {
|
||||
/**
|
||||
* Internal storage object.
|
||||
* @type {object}
|
||||
* @private
|
||||
*/
|
||||
#storage = {};
|
||||
|
||||
/**
|
||||
* Gets a value from storage.
|
||||
* @param {string} key
|
||||
* @return {any}
|
||||
*/
|
||||
getItem(key) {
|
||||
return this.#storage[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value to storage.
|
||||
* @param {string} key
|
||||
*/
|
||||
setItem(key, value) {
|
||||
this.#storage[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a value from storage.
|
||||
* @param {string} key
|
||||
*/
|
||||
removeItem(key) {
|
||||
delete this.#storage[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The storage service provides a simple way to persist data in the browser.
|
||||
* Most of the time localStorage is used. Appplications may specify in-memory
|
||||
* storage via the config, for example:
|
||||
*
|
||||
* ENV.storage = {
|
||||
* memory: true
|
||||
* };
|
||||
*
|
||||
* @example
|
||||
* import Route from '@ember/routing/route';
|
||||
* import { inject as service } from '@ember/service';
|
||||
* export default class ApplicationRoute extends Route {
|
||||
* @service storage;
|
||||
* afterModel() {
|
||||
* this.storage.setItem('key', 'value');
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export default class StorageService extends Service {
|
||||
/**
|
||||
* @type {MemoryStorage}
|
||||
* @private
|
||||
*/
|
||||
#memoryStorage = new MemoryStorage();
|
||||
|
||||
/**
|
||||
* Gets the underlying storage mechanism.
|
||||
* @return {Storage}
|
||||
*/
|
||||
get storage() {
|
||||
// Get the application config to determine if memory storage is enabled
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
const inMemory = get(config, 'storage.memory');
|
||||
// Get the global context object (window) via service lookup,
|
||||
// because just reaching for a global is icky
|
||||
const globalContext = getOwner(this).lookup('service:-document')
|
||||
.documentElement.parentNode.defaultView;
|
||||
// If memory storage enabled, return the memory storage instance,
|
||||
// otherwise return localStorage.
|
||||
return inMemory ? this.#memoryStorage : globalContext.localStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the storage namespace, which is the applications `modulePrefix`.
|
||||
* @return {string}
|
||||
*/
|
||||
get namespace() {
|
||||
const config = getOwner(this).resolveRegistration('config:environment');
|
||||
return config.modulePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fully qualified key from the passed string. A fully qualified
|
||||
* key includes the namespace.
|
||||
* @param {string} key
|
||||
* @return {string} fully qualified key
|
||||
*/
|
||||
qualifiedKey(key) {
|
||||
return `${this.namespace}:${key}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from storage.
|
||||
* @param {string} key
|
||||
* @return {any}
|
||||
*/
|
||||
getItem(key) {
|
||||
return this.storage.getItem(this.qualifiedKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value to storage.
|
||||
* @param {string} key
|
||||
*/
|
||||
setItem(key, value) {
|
||||
this.storage.setItem(this.qualifiedKey(key), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a value from storage.
|
||||
* @param {string} key
|
||||
*/
|
||||
removeItem(key) {
|
||||
this.storage.removeItem(this.qualifiedKey(key));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/services/storage';
|
||||
@@ -3,8 +3,9 @@ import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ApplicationRoute extends Route {
|
||||
@service scope;
|
||||
@service storage;
|
||||
|
||||
afterModel() {
|
||||
//console.log(this.scope);
|
||||
//console.log(this.storage);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,10 @@ module.exports = function (environment) {
|
||||
|
||||
ENV.APP.rootElement = '#ember-testing';
|
||||
ENV.APP.autoboot = false;
|
||||
|
||||
ENV.storage = {
|
||||
memory: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (environment === 'production') {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Service | storage', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it can persist values', function (assert) {
|
||||
assert.expect(1);
|
||||
const service = this.owner.lookup('service:storage');
|
||||
service.setItem('foo', 'bar');
|
||||
assert.equal(service.getItem('foo'), 'bar');
|
||||
});
|
||||
|
||||
test('it can remove persisted values', function (assert) {
|
||||
assert.expect(2);
|
||||
const service = this.owner.lookup('service:storage');
|
||||
service.setItem('foo', 'bar');
|
||||
assert.equal(service.getItem('foo'), 'bar');
|
||||
service.removeItem('foo');
|
||||
assert.notOk(service.getItem('foo'));
|
||||
});
|
||||
|
||||
// NOTE: the following tests are order-dependent, so may be invalidated
|
||||
// if we switch to random test ordering
|
||||
|
||||
test("it's in-memory storage is implicitly refreshed for every test case", function (assert) {
|
||||
assert.expect(1);
|
||||
const service = this.owner.lookup('service:storage');
|
||||
service.setItem('foo', 'bar');
|
||||
assert.equal(service.getItem('foo'), 'bar');
|
||||
});
|
||||
|
||||
test("it's in-memory storage is implicitly refreshed for every test case", function (assert) {
|
||||
assert.expect(1);
|
||||
const service = this.owner.lookup('service:storage');
|
||||
assert.notOk(service.getItem('foo'));
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user