• Jump To … +
    json-interface.js json-interface.spec.js
  • ¶
    'use strict';
  • ¶

    json-config-interface

    Loads a config file from a given path. Generates an API based on permissions requested.

    /** 
     * Supplies the root menu for program opperation, 
     * if commandline arguments supplied, moves user to the correct jump point.
     * @author Jon L-W
     * @module json-config-interface
     * @requires module:fs
     * @requires module:bluebird
    */
  • ¶

    Dependancies

    • fs
    • bluebird
    var fs = require('fs')
    , Q = require('q')
  • ¶

    configCache Object that stores loaded JSON config files indexed by path, Anywhere a config file is required, the same data object is returned to act upon, ensuring data integrity across a project.

    , configCache = {}
  • ¶

    base path used to prepend path param provided to get()

    , configBasePath = '';
  • ¶

    Exports

    • setDir - specify base directory to load configs from
    • get - get a configuration files data and methods to act upon it
    • purge - cache clearing operations
    /**
     * Sets the base working directory to load JSON config files from.
     * @param {string} basePath=false - the path to the directory to load config files in
     * @returns {bool} true on good directory, false on bad directory
     * @throws {badParam} basePath param not provided, or not string
     * @throws {nonDir} basePath does not point to a directory
    */
  • ¶

    exports.setDir

    module.exports.setDir = function(basePath = false){
  • ¶

    if not a good parameter

      if(!basePath || typeof basePath !== 'string'){
  • ¶

    throw badParam

        throw new Error('badParam');
        return false;
      }
      try {
  • ¶

    Query provided path string

        var stats = fs.lstatSync(basePath);
  • ¶

    if it is a directory

        if (stats.isDirectory()) {
  • ¶

    set basePath to module var for usage during exports.get

          configBasePath = basePath;
          return true;
        }
        else{
  • ¶

    throw nonDir

          throw new Error('nonDir')
        }
      }
  • ¶

    throw any lstatSync errors up to caller

      catch (err) {
          throw err;
          return false;
      }
    }
    /**
     * @typedef ConfigAPI
     * @type Object
     * @property {object} data - the parsed JSON config file
     * @property {function} [save] - method to save any changes back to JSON config file
     * @property {function} [revert] - rollback any changes made in .data to state when it was loaded
     */
    /**
     * Request a config file be loaded, assign API methods based on permision arg.
     * @param {string} path - The location of the config file. 
     * @param {string} [permision='read'] - If `"write"` the config api will be returned with save and revert methods
     * @param {boolean} [noCache=false] - If `true` will ignore cached instances of config file
     * @return {Promise<ConfigAPI|Error>}
     * @throws {badParam} path param undefined or not a string
     * @throws {fs.stat.err} any errors during fs.stat
     * @throws {fs.readFileSync.err} any errors during fs.readFileSync
    */
  • ¶

    exports.get

    module.exports.get = function(path, permision='read', noCache=false){
      var deferred = Q.defer()
  • ¶

    configAPI object to be returned containing the JSON object .data and if permision=='write' the save() & revert() methods that act upon the

      , configAPI = {};
  • ¶

    build full path string using configBasePath and path

      if(path!==undefined && typeof path === 'string'){
  • ¶

    if path name supplied without .json extension, add it

        if(path.indexOf('.json')===-1){
          path += '.json';
        }
  • ¶

    if path contains ./ at it’s begining, don’t add base class, as this is a relative path else

        var configPath = (/^\.\//.exec(path)!==null ? path : (path[0] === '/' ? configBasePath + path : configBasePath + '/' + path))
      }
      else{
        deferred.reject(new Error('badParam'));
        return deferred.promise;
      }
    
      console.log(configPath)
  • ¶

    define configAPI.data with false

      configAPI.data = false;
  • ¶

    if write permmision set, attach the save method to configAPI

      if(permision==='write'){
  • ¶

    setup write API

        var original = false
  • ¶

    save()

        , save = function(){
          var saveDeferred = Q.defer();
  • ¶

    Stringify to JSON with indentation of 2 spaces and write to config file

          fs.writeFile(configPath, JSON.stringify(configAPI.data, null, 2), function(err){
            if(err){
  • ¶

    if write error, reject promise with err object

              saveDeferred.reject(err);
            } 
            else{
  • ¶

    if write success, resolve with true val

              saveDeferred.resolve(true);
            }
          });
    
          return saveDeferred.promise;
        }
  • ¶

    revert()

        , revert = function(){
          configAPI.data = original;
          return configAPI;
        }
  • ¶

    assign save and revert to configAPI

        configAPI.save = save;
        configAPI.revert = revert;
      }
  • ¶

    cache

    If config has already been loaded, return the instance of it’s parsed object from cache

      if(configCache[configPath]!==undefined && !noCache){
  • ¶

    set property configAPI.data as the previously parsed JSON object

        configAPI.data = configCache[configPath];
        original = function(){ return JSON.parse(JSON.stringify(configCache[configPath])); }();
  • ¶

    return resolved

        return Q(configAPI);
      }
  • ¶

    config file load & parse

    check config file exists, if it does load or return with error

      fs.stat(configPath, function(err, stat){
  • ¶

    if no error in file stat

        if(err == null) {
  • ¶

    try to read file and parse JSON to var

          try{    
            var jsonContents = fs.readFileSync(configPath, 'utf8');
            var parsedJSONdata = JSON.parse(jsonContents);
          }
  • ¶

    catch any errors during read or parse, if so reject with error

          catch(err) {
            deferred.reject(err);
          }
  • ¶

    if good load and parse

          if(parsedJSONdata!==undefined){
  • ¶

    add loaded config to cache

            if(!noCache){
              configCache[configPath] = parsedJSONdata;
            }
  • ¶

    assign fileData to property data of the config object

            configAPI.data = parsedJSONdata;
  • ¶

    make clone of config file to be used if revert method is called of API

            configAPI.original = JSON.parse(JSON.stringify(parsedJSONdata));
  • ¶

    resolve with the built configAPI

            deferred.resolve(configAPI);
          }
        }
  • ¶

    catch all for errors in reading config

        else{ 
          deferred.reject(err);
        }
      });
    
      return deferred.promise;
    }
    /**
     * Cleans entire cache or cleans cache of specific file if path provided
     * @param {string} [path] The location of the config file to purge from cache. If not provided, cleans entire cache
     * @return {boolean} true for successful purges, false if path supplied and not found in cache
    */
  • ¶

    exports.purge

    module.exports.purge = function(path){
  • ¶

    if no path then clean entire cache

      if(path===undefined){
          configCache = {};
          return true;
      }
  • ¶

    if path given and index in the object not undefined.

      if(configCache[path]!==undefined){
  • ¶

    set the cache for path to undefined

        configCache[path] = undefined;
        return true;
      }
    
      return false;
    }