Skip to content

Instantly share code, notes, and snippets.

@long-lazuli
Last active January 18, 2019 15:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save long-lazuli/ca63a91e5a1a76d7efdb21d8380edd9b to your computer and use it in GitHub Desktop.
Save long-lazuli/ca63a91e5a1a76d7efdb21d8380edd9b to your computer and use it in GitHub Desktop.
export class MultilineError extends Error {
constructor( msg, ...otherArgs ){
if( Array.isArray( msg ) ) msg = msg.join( '\n ' )
super( ...[msg, ...otherArgs] )
}
}
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