Last active
January 18, 2019 15:58
-
-
Save long-lazuli/ca63a91e5a1a76d7efdb21d8380edd9b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class MultilineError extends Error { | |
constructor( msg, ...otherArgs ){ | |
if( Array.isArray( msg ) ) msg = msg.join( '\n ' ) | |
super( ...[msg, ...otherArgs] ) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { MultilineError } from './errors' | |
function getPathParts( path ){ | |
const DIVIDER = /(\]?[.[])/ | |
return path | |
.split( DIVIDER ) | |
// .map( String.trim.bind ) | |
} | |
class JsonStorage { | |
instanciatedStorage= [] | |
originStorage | |
/** | |
* Instantiate a JsonStorage from a Storage | |
* > Usual Storages are localStorage or sessionStorage | |
* > but they can be anything with getItem anpath setItem methods, | |
* > who return strings. | |
* @param { from: Storage, force: boolean | optional } options | |
*/ | |
constructor( options= {}) { | |
if( !options.from ) throw new MultilineError( [ | |
'Wrong arguments', | |
'`options.from` should be a valid Storage (as window.localStorage).' | |
] ) | |
const alreadyInstanciated = this.instanciatedStorage.includes( options.from ) | |
if( !options.force && alreadyInstanciated ) throw new MultilineError( [ | |
'A JsonStorage for this Storage is already instanciated.', | |
'If you know what you\'re doing, you can specified "force: true" in options.' | |
] ) | |
this.originStorage = options.from | |
if( !alreadyInstanciated ) this.instanciatedStorage.push( this.originStorage ) | |
} | |
getRawItem( key ) { | |
return this.originStorage.getItem( key ) | |
} | |
getOrElse( key, fallback ) { | |
const maybeJSON = this.getItem( key ) | |
return ( maybeJSON !== null ) ? maybeJSON : fallback | |
} | |
get length() { return this.originStorage.length } | |
get key() { return this.originStorage.key } | |
getItem( path ) { | |
const [root, ...keys] = getPathParts( path ) | |
try { | |
const jsonSource = JSON.parse( this.getRawItem( root ) ) | |
return keys.reduce( ( json, key ) => { | |
if( json[key] === undefined ) throw new Error() | |
return json[key] | |
}, jsonSource ) | |
} catch ( e ) { | |
return null | |
} | |
} | |
setItem( path, value ) { | |
const [root, ...keys] = getPathParts( path ) | |
try { | |
const jsonToModify = JSON.parse( this.getRawItem( root ) ) | |
keys.reduce( ( json, key, index ) => { | |
if( keys.length === index + 1 ) { | |
if( value === null ) delete( json[key] ) | |
else json[key] = value | |
} else { | |
if( json[key] === undefined ) throw new Error() | |
} | |
return json[key] | |
}, jsonToModify ) | |
this.originStorage.setItem( root, JSON.stringify( jsonToModify ) ) | |
} catch ( e ) { | |
return false | |
} | |
return true | |
} | |
removeItem( path ) { | |
return this.setItem( path, null ) | |
} | |
clear( path ) { | |
const [root, ...keys] = getPathParts( path ) | |
if( keys.length ) throw new Error( 'Clear should not be used for a path.' ) | |
this.originStorage.clear( root ) | |
} | |
} | |
export default JsonStorage |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment