Skip to content

Commit

Permalink
Merge pull request #279 from funkensturm/feature/key-namespace
Browse files Browse the repository at this point in the history
Add namespace for localStorage keys
  • Loading branch information
fsmanuel authored Jan 4, 2019
2 parents cd24a74 + b4bea18 commit eece52d
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 26 deletions.
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ See the [CHANGELOG](CHANGELOG.md)

## Usage

* [Configuration](#configuration)
* [Object & Array](#object--array)
* [Object](#object)
* [Array](#array)
Expand All @@ -42,6 +43,33 @@ See the [CHANGELOG](CHANGELOG.md)
* [.query() & .queryRecord()](#query--queryrecord)
* [Import & Export of localStorage records](#export--import)

### Configuration

#### Namespace & keyDelimiter

In you apps `config/environment.js` you can set a `namespace` and a `keyDelimiter`. For backward compatibility this is a opt-in feature.

**Important:** Don't turn this feature on for existing apps. You will lose access to existing keys.

To activate it there are the following options:

- `namespace` can be `true` or a string. If set to `true` it will use `modulePrefix` as the namespace
- `keyDelimiter` is a string. The default is `:`

```js
// config/environment.js
module.exports = function() {
var ENV = {
modulePrefix: 'my-app',
'ember-local-storage': {
namespace: true, // will use the modulePrefix e.g. 'my-app'
namespace: 'customNamespace', // will use 'customNamespace'
keyDelimiter: '/' // will use / as a delimiter - the default is :
}
}
};
```

### Object & Array

#### Object
Expand Down Expand Up @@ -165,7 +193,7 @@ export default Ember.Component.extend({
`model` Optional string - The dependent property. Must be an ember data model or an object with `modelName` and `id` properties. (It is still experimental)

`options` are:
- `legacyKey` String
- `legacyKey` String - **Deprecated see [Deprecations](#deprecations)**


#### Methods
Expand Down Expand Up @@ -456,6 +484,15 @@ module('basic acceptance test', function(hooks) {

```
## Deprecations
### storageFor - legacyKey
until: 2.0.0
id: ember-local-storage.storageFor.options.legacyKey
Using `legacyKey` has been deprecated and will be removed in version 2.0.0. You should migrate your key to the new format. For `storageFor('settings')` that would be `storage:settings`.
## Running
* `ember serve`
Expand Down
3 changes: 2 additions & 1 deletion addon/adapters/base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Ember from 'ember';
import DS from 'ember-data';
import ImportExportMixin from '../mixins/adapters/import-export';
import { _buildKey } from '../helpers/storage';

const keys = Object.keys || Ember.keys;

Expand Down Expand Up @@ -323,7 +324,7 @@ export default JSONAPIAdapter.extend(ImportExportMixin, {
},

_storageKey(type, id) {
return type + '-' + id;
return _buildKey(this, type + '-' + id);
},

// Should be overwriten
Expand Down
6 changes: 4 additions & 2 deletions addon/adapters/local.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Ember from 'ember';
import BaseAdapter from './base';
import { getStorage } from '../helpers/storage';
import { getStorage, _buildKey } from '../helpers/storage';
import StorageArray from '../local/array';

const {
Expand All @@ -14,8 +14,10 @@ export default BaseAdapter.extend({
const indices = get(this, '_indices');

if (!indices[type]) {
let storageKey = _buildKey(this, 'index-' + type);

indices[type] = StorageArray
.extend({ _storageKey: 'index-' + type })
.extend({ _storageKey: storageKey })
.create();
}

Expand Down
6 changes: 4 additions & 2 deletions addon/adapters/session.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Ember from 'ember';
import BaseAdapter from './base';
import { getStorage } from '../helpers/storage';
import { getStorage, _buildKey } from '../helpers/storage';
import StorageArray from '../session/array';

const {
Expand All @@ -14,8 +14,10 @@ export default BaseAdapter.extend({
const indices = get(this, '_indices');

if (!indices[type]) {
let storageKey = _buildKey(this, 'index-' + type);

indices[type] = StorageArray
.extend({ _storageKey: 'index-' + type })
.extend({ _storageKey: storageKey })
.create();
}

Expand Down
39 changes: 37 additions & 2 deletions addon/helpers/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ const {
getOwner,
String: {
dasherize
}
},
deprecate
} = Ember;

const assign = Ember.assign || Ember.merge;
Expand Down Expand Up @@ -38,6 +39,7 @@ function getStorage(name) {

let storages = {};

// TODO: v2.0 - Remove options
function storageFor(key, modelName, options = {}) {
if (arguments.length === 2 && typeof modelName === 'object') {
options = modelName;
Expand Down Expand Up @@ -85,6 +87,7 @@ function storageFor(key, modelName, options = {}) {
* Looks up the storage factory on the container and sets initial state
* on the instance if desired.
*/
// TODO: v2.0 - Remove options and legacyKey
function createStorage(context, key, modelKey, options) {
const owner = getOwner(context);
const factoryType = 'storage';
Expand All @@ -95,11 +98,19 @@ function createStorage(context, key, modelKey, options) {
owner.registerOptionsForType(factoryType, { instantiate: false });

if (options.legacyKey) {
deprecate('Using legacyKey has been deprecated and will be removed in version 2.0.0', false, {
id: 'ember-local-storage.storageFor.options.legacyKey',
until: '2.0.0',
url: 'https://github.com/funkensturm/ember-local-storage#deprecations'
});

storageKey = options.legacyKey;
} else {
storageKey = modelKey ? `${storageFactory}:${modelKey}` : storageFactory;
}

storageKey = _buildKey(context, storageKey);

const initialState = {},
defaultState = {
_storageKey: storageKey
Expand Down Expand Up @@ -136,6 +147,29 @@ function _modelKey(model) {
return `${modelName}:${id}`;
}

// TODO: v2.0 - Make modulePrefix the default
function _getNamespace(appConfig, addonConfig) {
// For backward compatibility this is a opt-in feature
let namespace = addonConfig.namespace;

// Shortcut for modulePrefix
if (namespace === true) {
namespace = appConfig.modulePrefix
}

return namespace;
}

// TODO: Add migration helper
function _buildKey(context, key) {
let appConfig = getOwner(context).resolveRegistration('config:environment');
let addonConfig = appConfig && appConfig['ember-local-storage'] || {};
let namespace = _getNamespace(appConfig, addonConfig);
let delimiter = addonConfig.keyDelimiter || ':';

return namespace ? `${namespace}${delimiter}${key}` : key;
}

// Testing helper
function _resetStorages() {
storages = {};
Expand All @@ -145,5 +179,6 @@ export {
tryStorage,
getStorage,
storageFor,
_resetStorages
_resetStorages,
_buildKey
};
19 changes: 18 additions & 1 deletion tests/helpers/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,24 @@ function storageDeepEqual(assert, actual, expected, message) {
assert.deepEqual(actual, expected, message);
}

function registerConfigEnvironment(context) {
let environment = {
modulePrefix: 'my-app',
'ember-local-storage': {}
};

context.register('config:environment', environment, { instantiate: false });
}

function setConfigEnvironment(context, key, value) {
let appConfig = context.container.lookup('config:environment');
let addonConfig = appConfig['ember-local-storage'] || {};
addonConfig[key] = value;
}

export {
storageEqual,
storageDeepEqual
storageDeepEqual,
registerConfigEnvironment,
setConfigEnvironment
};
69 changes: 67 additions & 2 deletions tests/unit/adapters/indices-test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { moduleFor, test } from 'ember-qunit';
import {
storageEqual,
storageDeepEqual
storageDeepEqual,
registerConfigEnvironment,
setConfigEnvironment
} from '../../helpers/storage';

moduleFor('adapter:application', 'Unit | Adapter | indices', {
// Specify the other units that are required for this test.
// needs: ['serializer:foo']
beforeEach: function() {
registerConfigEnvironment(this);

window.localStorage.clear();
window.sessionStorage.clear();
}
Expand All @@ -34,7 +38,7 @@ test('it does not persists duplicates to index', function(assert) {
storageDeepEqual(assert, window.localStorage['index-projects'], ['1234']);
});

test('it removes ids from index', function(assert) {
test('it removes ids from index (namespace not set)', function(assert) {
assert.expect(3);
var adapter = this.subject();

Expand All @@ -46,3 +50,64 @@ test('it removes ids from index', function(assert) {
adapter._removeFromIndex('projects', '1234');
storageDeepEqual(assert, window.localStorage['index-projects'], []);
});

test('it removes ids from index (namespace: true)', function(assert) {
assert.expect(3);

setConfigEnvironment(this, 'namespace', true);

var adapter = this.subject();

storageEqual(assert, window.localStorage['index-projects'], undefined);

adapter._addToIndex('projects', '1234');
storageDeepEqual(
assert,
window.localStorage['my-app:index-projects'],
['1234']
);

adapter._removeFromIndex('projects', '1234');
storageDeepEqual(assert, window.localStorage['my-app:index-projects'], []);
});

test('it removes ids from index (namespace: "custom")', function(assert) {
assert.expect(3);

setConfigEnvironment(this, 'namespace', 'custom');

var adapter = this.subject();

storageEqual(assert, window.localStorage['index-projects'], undefined);

adapter._addToIndex('projects', '1234');
storageDeepEqual(
assert,
window.localStorage['custom:index-projects'],
['1234']
);

adapter._removeFromIndex('projects', '1234');
storageDeepEqual(assert, window.localStorage['custom:index-projects'], []);
});

test('it removes ids from index (keyDelimiter: "/")', function(assert) {
assert.expect(3);

setConfigEnvironment(this, 'namespace', true);
setConfigEnvironment(this, 'keyDelimiter', '/');

var adapter = this.subject();

storageEqual(assert, window.localStorage['index-projects'], undefined);

adapter._addToIndex('projects', '1234');
storageDeepEqual(
assert,
window.localStorage['my-app/index-projects'],
['1234']
);

adapter._removeFromIndex('projects', '1234');
storageDeepEqual(assert, window.localStorage['my-app/index-projects'], []);
});
Loading

0 comments on commit eece52d

Please sign in to comment.