Skip to content

Commit

Permalink
0.9.7: New-less, store id, docs, tests.
Browse files Browse the repository at this point in the history
* Expose store id to client code as a read-only property
* Make sure new-less invocations don't fail
* Added docs on store namespaces / isolation, and new-less invocation
* Copyright and license info in docs
* Fixed tests (there are now 7 exposed properties on store objects)
* Increased test coverage with tests for new-less invocation
  • Loading branch information
Download committed Sep 2, 2015
1 parent 4088c3b commit dec152d
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 43 deletions.
62 changes: 46 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,70 @@ Download memorystorage.min.js, place it in a folder `lib` in the root of your we
```

## Create a memory storage object
The `MemoryStorage` function creates (or returns) a storage object implementing the W3C Web Storage API.
By default, scripts share a `global` storage object, so scripts can access and mutate each other's store
object. To have MemoryStorage create a storage object that is isolated from other scripts, you pass in
a unique ID which acts as a namespace:

```javascript
var isolated = new MemoryStorage('my-app'); // isolated from other scripts, recommended.
```

If you don't pass in an ID, or use the ID `'global'`, you get a globally shared storage object:

```javascript
var global = new MemoryStorage(); // will default to a globally shared storage object.
var global2 = new MemoryStorage('global'); // effectively same as above
```

For your convenience, the constructor permits `new`-less invocation:
```javascript
var store = MemoryStorage('my-store');
var global = MemoryStorage();
```

Instances of `MemoryStorage` expose an immutable `id` property that is set to
the id the store was created with:

```javascript
var memoryStorage = new MemoryStorage('my-app');
alert(store.id); // alerts 'my-store'
alert(global.id); // alerts 'global'
```

## Use it
```javascript
memoryStorage.setItem('myString', 'Hello MemoryStorage!');
memoryStorage.myObject = JSON.stringify({my: 'object'}));
alert(memoryStorage.getItem('My string')); // alerts 'Hello MemoryStorage!'
alert(memoryStorage['My string']); // alerts 'Hello MemoryStorage!'
alert(memoryStorage.length); // alerts '2'
alert(memoryStorage.key(1)); // alerts 'My object'
memoryStorage.removeItem('My string');
alert(memoryStorage.length); // alerts '1'
memoryStorage.clear();
alert(memoryStorage.length); // alerts '0'
store.setItem('myString', 'Hello MemoryStorage!');
store.myObject = JSON.stringify({my: 'object'}));
alert(store.getItem('My string')); // alerts 'Hello MemoryStorage!'
alert(store['My string']); // alerts 'Hello MemoryStorage!'
alert(store.length); // alerts '2'
alert(store.key(1)); // alerts 'My object'
store.removeItem('My string');
alert(store.length); // alerts '1'
store.clear();
alert(store.length); // alerts '0'
```

## Beyond the Web Storage API
MemoryStorage is type-agnosic; it doesn't care about the type of data you store.
MemoryStorage is type-agnostic; it doesn't care about the type of data you store.
If you want to remain within the Web Storage API, you should only read and write strings,
however if you want you can store other types just as well:
```javascript
memoryStorage.myObject = {my: 'object'};
alert(memoryStorage.myObject.my); // alerts 'object'
store.myObject = {my: 'object'};
alert(store.myObject.my); // alerts 'object'
var tree = {
nested: {
objects: {
working: 'Sure!'
}
}
}
memoryStorage.setItem('tree', tree);
alert(memoryStorage.tree.nested.objects.working); // alerts 'Sure!'
store.setItem('tree', tree);
alert(store.tree.nested.objects.working); // alerts 'Sure!'
```

## Copyright
Copyright 2015 by Stijn de Witt. Some rights reserved.

## License
Licensed under the [Creative Commons Attribution 4.0 International (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/) Open Source license.
4 changes: 2 additions & 2 deletions dist/memorystorage.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/memorystorage.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 24 additions & 9 deletions doc/memorystorage.js.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ <h1 class="page-title">Source: memorystorage.js</h1>
}(this, 'MemoryStorage', function(){
'use strict';

var API = {'clear':1, 'getItem':1, 'key':1, 'length':1, 'removeItem':1, 'setItem':1},
// API methods and properties will be cloaked
var API = {'clear':1, 'getItem':1, 'id':1, 'key':1, 'length':1, 'removeItem':1, 'setItem':1},
API_LENGTH = Object.keys(API).length,
CLOAK = '__memorystorage_cloaked_items__';

// Used to store all data
// Used to store all memorystorage objects
var storage = {};

/** @module memorystorage */
Expand All @@ -97,41 +98,55 @@ <h1 class="page-title">Source: memorystorage.js</h1>
* @class
*/
function MemoryStorage(id) {
// make sure id is assigned
id = id || 'global';
// try to get existing store
var result = storage[id];
// return it if found
if (result) {return result;}

// make sure there is no harm in leaving out new in invocations to MemoryStorage
if (! (this instanceof MemoryStorage)) {return new MemoryStorage(id);}

// create a new store and save a ref to it so we can get it back later
result = storage[id] = this;
// create a space to store 'cloaked' key/values: items that have a key
// that collides with Web Storage API method names.
var cloaked = {};
Object.defineProperty(this, CLOAK, {
Object.defineProperty(result, CLOAK, {
enumerable: false,
get: function(){return cloaked;}
});
Object.defineProperty(this, 'length', {
// Allow client code to read the id
Object.defineProperty(result, 'id', {
enumerable: true,
get: function(){return id;}
});
// Create the length property
Object.defineProperty(result, 'length', {
enumerable: true,
get: function(){
return Object.keys(this).length + Object.keys(this[CLOAK]).length - API_LENGTH;
}
});
this.getItem = function MemoryStorage_getItem(key) {
// Create API methods
result.getItem = function MemoryStorage_getItem(key) {
return key in API ? this[CLOAK][key] : this[key];
};
this.setItem = function MemoryStorage_setItem(key, val) {
result.setItem = function MemoryStorage_setItem(key, val) {
if (key in API) {this[CLOAK][key] = val;}
else {this[key] = val;}
};
this.removeItem = function MemoryStorage_removeItem(key) {
result.removeItem = function MemoryStorage_removeItem(key) {
if (key in API) {delete this[CLOAK][key];}
else {delete this[key];}
};
this.key = function MemoryStorage_key(idx) {
result.key = function MemoryStorage_key(idx) {
var keys = Object.keys(this).concat(Object.keys(this[CLOAK]));
keys = keys.filter(function(x){return !(x in API);});
return idx >= 0 &amp;&amp; idx &lt; keys.length ? keys[idx] : null;
};
this.clear = function MemoryStorage_clear() {
result.clear = function MemoryStorage_clear() {
var keys = Object.keys(this).filter(function(x){return !(x in API);});
for (var i=0,key; key=keys[i]; i++) {
if (! (key in API)) {delete this[key];}
Expand Down
2 changes: 1 addition & 1 deletion doc/module-memorystorage.MemoryStorage.html
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ <h5>Parameters:</h5>
<ul class="dummy">
<li>
<a href="memorystorage.js.html">memorystorage.js</a>,
<a href="memorystorage.js.html#sunlight-1-line-38">line 38</a>
<a href="memorystorage.js.html#sunlight-1-line-39">line 39</a>
</li>
</ul>
</dd>
Expand Down
2 changes: 1 addition & 1 deletion doc/module-memorystorage.html
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ <h1 class="page-title">Module: memorystorage</h1>
<ul class="dummy">
<li>
<a href="memorystorage.js.html">memorystorage.js</a>,
<a href="memorystorage.js.html#sunlight-1-line-21">line 21</a>
<a href="memorystorage.js.html#sunlight-1-line-22">line 22</a>
</li>
</ul>
</dd>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "memorystorage",
"version": "0.9.6",
"version": "0.9.7",
"description": "Memory-backed implementation of the Web Storage API",
"main": "src/memorystorage.js",
"dist": {
Expand Down
33 changes: 24 additions & 9 deletions src/memorystorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
}(this, 'MemoryStorage', function(){
'use strict';

var API = {'clear':1, 'getItem':1, 'key':1, 'length':1, 'removeItem':1, 'setItem':1},
// API methods and properties will be cloaked
var API = {'clear':1, 'getItem':1, 'id':1, 'key':1, 'length':1, 'removeItem':1, 'setItem':1},
API_LENGTH = Object.keys(API).length,
CLOAK = '__memorystorage_cloaked_items__';

// Used to store all data
// Used to store all memorystorage objects
var storage = {};

/** @module memorystorage */
Expand All @@ -36,41 +37,55 @@
* @class
*/
function MemoryStorage(id) {
// make sure id is assigned
id = id || 'global';
// try to get existing store
var result = storage[id];
// return it if found
if (result) {return result;}

// make sure there is no harm in leaving out new in invocations to MemoryStorage
if (! (this instanceof MemoryStorage)) {return new MemoryStorage(id);}

// create a new store and save a ref to it so we can get it back later
result = storage[id] = this;
// create a space to store 'cloaked' key/values: items that have a key
// that collides with Web Storage API method names.
var cloaked = {};
Object.defineProperty(this, CLOAK, {
Object.defineProperty(result, CLOAK, {
enumerable: false,
get: function(){return cloaked;}
});
Object.defineProperty(this, 'length', {
// Allow client code to read the id
Object.defineProperty(result, 'id', {
enumerable: true,
get: function(){return id;}
});
// Create the length property
Object.defineProperty(result, 'length', {
enumerable: true,
get: function(){
return Object.keys(this).length + Object.keys(this[CLOAK]).length - API_LENGTH;
}
});
this.getItem = function MemoryStorage_getItem(key) {
// Create API methods
result.getItem = function MemoryStorage_getItem(key) {
return key in API ? this[CLOAK][key] : this[key];
};
this.setItem = function MemoryStorage_setItem(key, val) {
result.setItem = function MemoryStorage_setItem(key, val) {
if (key in API) {this[CLOAK][key] = val;}
else {this[key] = val;}
};
this.removeItem = function MemoryStorage_removeItem(key) {
result.removeItem = function MemoryStorage_removeItem(key) {
if (key in API) {delete this[CLOAK][key];}
else {delete this[key];}
};
this.key = function MemoryStorage_key(idx) {
result.key = function MemoryStorage_key(idx) {
var keys = Object.keys(this).concat(Object.keys(this[CLOAK]));
keys = keys.filter(function(x){return !(x in API);});
return idx >= 0 && idx < keys.length ? keys[idx] : null;
};
this.clear = function MemoryStorage_clear() {
result.clear = function MemoryStorage_clear() {
var keys = Object.keys(this).filter(function(x){return !(x in API);});
for (var i=0,key; key=keys[i]; i++) {
if (! (key in API)) {delete this[key];}
Expand Down
4 changes: 2 additions & 2 deletions tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>MemoryStorage Tests</title>
<script src="../dist/memorystorage.min.js"></script>
<script src="../src/memorystorage.js"></script>
<link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.14.0.css">
<body>
<a style="font-size:xx-large;" href="test-memorystorage.html">Restart</a>
<a style="font-size:xx-large;" href="index.html">Restart</a>
<h1>MemoryStorage Tests</h1>
<a href="test.js">Test code</a>
<script src="http://code.jquery.com/qunit/qunit-1.14.0.js"></script>
Expand Down
11 changes: 10 additions & 1 deletion tests/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ QUnit.test("W3C Web Storage API Compliance Test", function( assert ) {
assert.ok(store.length===2, 'value added correctly with index operators');
store.setItem('test2', 'data2');
assert.ok(store.length===3, 'three items added to store');
assert.ok(Object.keys(store).length == (6+3), "store has 9 enumerable properties (6 api methods + 3 stored items)");
assert.ok(Object.keys(store).length == (7+3), "store has 10 enumerable properties (id, 6 api methods + 3 stored items)");
assert.ok(store.getItem('test1')==='data1' && store.getItem('test2')==='data2', "retrieved values matches stored values");
var keyOrderBefore = '';
for (var i=0; i<store.length; i++) {
Expand Down Expand Up @@ -86,4 +86,13 @@ QUnit.test("Beyond W3C API Test", function( assert ) {
store.clear();
});

QUnit.test("New-less Construction Test", function( assert ) {
var store1 = MemoryStorage('local');
assert.ok(store1, 'Existing store is defined and not null');
assert.ok(store1 instanceof MemoryStorage, 'Existing store is instanceof MemoryStorage');

var store2 = MemoryStorage('new-store');
assert.ok(store2, 'New store is defined and not null');
assert.ok(store2 instanceof MemoryStorage, 'New store is instanceof MemoryStorage');
});

0 comments on commit dec152d

Please sign in to comment.