objfile
Version:
Read from + write to + update INI and JSON files via a simple asynchronous API
225 lines (173 loc) • 5.95 kB
text/coffeescript
# node dependency
fs = require 'fs'
path = require 'path'
# npm dependency
mkdirp = require 'mkdirp'
loaded_files = {}
class ObjFile
_objFileAllData: null
_objFileCue: null
_objFileData: null
_objFileError: null
_objFileOptions: null
_objFilePath: null
_objFileReading: null
_objFileWriting: null
constructor: (source_path, opts, cb) ->
source_path, opts, cb
_objFileLoad: (source_path, opts, cb) =>
if not cb and typeof opts is 'function'
[cb, opts] = [opts, {}]
opts ?= {}
if typeof opts isnt 'object'
throw new Error 'options must be object: ' +
'ObjFile::_objFileLoad(path, options, callback)'
if ( ?= source_path) isnt source_path
return cb? new Error 'source_path does not match previously set path'
?= []
?= opts
# expose moethods on targets if requested
if (target = opts.expose) and typeof opts.expose is 'object'
unless Array.isArray target
target = [target]
for item in target when item and typeof item is 'object'
item.get = (args...) =>
args...
item.set = (args...) =>
args...
item.del = (args...) =>
args...
if (target = opts.exposeGet) and typeof opts.exposeGet is 'object'
unless Array.isArray target
target = [target]
for item in target when item and typeof item is 'object'
item.get = (args...) =>
args...
setTimeout => # ensure async (return variable assignment happens first)
cb
_objFileRead: (cb) =>
process = =>
= null
= true
error = (err) =>
= if typeof err is 'string' then (new Error err) else err
= false
cb?
.shift()?() # flush next in cue
ready = =>
= false
setTimeout =>
cb?()
.shift()?() # flush next in cue
unless
return error 'File source path missing'
if
return ready()
= {}
if root = .dataRoot # TODO: does only 1 level
= [root] = {}
else
=
fs.readFile , encoding: 'utf8', (err, data) =>
unless err
return .decode data, (err, pkg) =>
if err
return error err
try
for own key of
delete [key]
for key, value of pkg
[key] = value
if root
unless [root] and
typeof [root] is 'object'
= [root] = {}
return (err) ->
if err
return error err
ready()
else
= [root]
else
=
catch _err
return ready _err
ready()
ready()
if
cb?
else if or
.push process
else
process cb
return
_objFileWrite: (cb) =>
error = (err) =>
= if typeof err is 'string' then (new Error err) else err
= false
cb?
.shift()?() # flush next in cue
(err) => # re-read first
if err
return error err
process = =>
= true
.encode , (err, str) =>
if err
return error err
mkdirp path.dirname(), {mode: '0755'}, (err) =>
if err
return error err
fs.writeFile , str, encoding: 'utf8', (err) =>
if err
return error err
= false
cb?()
.shift()?() # flush next in cue
if
cb?
else if or
.push process
else
process()
return
del: (level_names..., name, cb) =>
(err) =>
if err
return cb? err
target =
for level_name in level_names
unless target[level_name] and typeof target[level_name] is 'object'
target[level_name] = {}
target = target[level_name]
delete target[name]
cb
get: (level_names..., name, cb) =>
unless typeof cb is 'function'
throw new Error 'Callback function is required'
(err) =>
if err
return cb? err
undefined_key = ->
cb? new Error 'Undefined: ' + level_names.concat([name]).join '.'
target =
for level_name in level_names
unless target[level_name] and typeof target[level_name] is 'object'
return undefined_key()
target = target[level_name]
unless target.hasOwnProperty name
return undefined_key()
cb null, target[name]
set: (level_names..., name, value, cb) =>
(err) =>
if err
return cb? err
target =
for level_name in level_names
unless target[level_name] and typeof target[level_name] is 'object'
target[level_name] = {}
target = target[level_name]
target[name] = value
cb
module.exports = ObjFile