Skip to content

Commit

Permalink
added freeze option
Browse files Browse the repository at this point in the history
  • Loading branch information
kof committed Sep 20, 2012
1 parent 5386ca6 commit 9220f17
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 65 deletions.
71 changes: 45 additions & 26 deletions lib/cjson.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
/*
* CJSON - comments enabled json config loader (Commented Javascript Object Notation)
*
* @author Oleg Slobodskoi
*/

var fs = require('fs'),
Path = require('path');

/**
* Remove single and multilie comments. Make sure to
* leave them inside of strings.
*
* @param {string} str json file contents.
* @return {string} newStr cleaned json.
* @param {String} json file.
* @return {String} json without comments.
*/
exports.decomment = function(str) {
var i,
Expand Down Expand Up @@ -65,8 +59,10 @@ exports.decomment = function(str) {

/**
* Decomment the string and parse json.
* @param {string} str json string.
* @param {=Function} optional will be called for every key and value at every level of the final result.
*
* @param {String} json.
* @param {Function} [reviver] will be called for every key and value at every
* level of the final result.
* @return {Object} parsed json object.
*/
exports.parse = function(str, reviver) {
Expand All @@ -75,20 +71,21 @@ exports.parse = function(str, reviver) {

/**
* Replace templates with data. {{toReplace}}
* @param {string} str json string.
*
* @param {String} json.
* @param {Object} data data hash.
* @return {string} json string with replaced data.
* @return {String} json string with replaced data.
*/
exports.replace = function(str, data) {
return str.replace(/\{\{([^}]+)\}\}/g, function(match, search){
return str.replace(/\{\{([^}]+)\}\}/g, function(match, search) {
return data[search] ? data[search] : match;
})
});
};


/**
* Merge objects to the first one
* @param {boolean|Object} deep if set true, deep merge will be done.
*
* @param {Boolean|Object} deep if set true, deep merge will be done.
* @param {Object} obj1 any object.
* @param {Object} obj2 any object.
* @return {Object} target merged object.
Expand All @@ -102,8 +99,7 @@ exports.extend = (function() {
var args = arguments,
i = deep === true ? 1 : 0,
key,
target = args[i],
shallowCopy;
target = args[i];

for (++i; i < args.length; ++i) {
for (key in args[i]) {
Expand All @@ -128,18 +124,36 @@ exports.extend = (function() {
};
}());

/**
* Freeze the object recursively.
*
* @param {Object} obj.
* @return {Object}
*/
exports.freeze = function freeze(obj) {
var key;

if (obj instanceof Object) {
for (key in obj) {
freeze(obj[key]);
}

Object.freeze(obj);
}
};

/**
* Load and parse a config file/files.
* @param {string|Array} path absolute path/paths to the file/files or dir.
* @param {Object|boolean=} options if true, extend all jsons to the first one,
*
* @param {String|Array} path absolute path/paths to the file/files or dir.
* @param {Object|Boolean} [options] if true, extend all jsons to the first one,
* it can be also object {merge: true, replace: {key: 'value'}}
* @return {Object} conf parsed json object.
*/
exports.load = function load(path, options) {
var data, paths, conf;

options = options || {};
options || (options = {});

if (options === true) {
options = {merge: true};
Expand All @@ -160,7 +174,9 @@ exports.load = function load(path, options) {
});

return conf;
} else if (fs.statSync(path).isDirectory()) {
}

if (fs.statSync(path).isDirectory()) {
paths = [];
fs.readdirSync(path).forEach(function(filename) {
if (/\.json$/.test(filename)) {
Expand All @@ -171,18 +187,21 @@ exports.load = function load(path, options) {
return load(paths, options);
}

data = fs.readFileSync(path, 'utf8');
data = fs.readFileSync(path, 'utf-8');

if (options.replace) {
data = exports.replace(data, options.replace);
}

data = exports.decomment(data);

try {
return JSON.parse(data);
data = exports.parse(data);
} catch(e) {
throw new Error('JSON.parse error - "' + e.message + '"\nFile: "' + path + '"\n');
}

if (options.freeze) {
exports.freeze(data);
}

return data;
};
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"name": "cjson",
"description": "cjson - Commented Javascript Object Notation. It is a json loader, which parses only valide json files, but with comments enabled. Usefull for loading configs.",
"version": "0.0.6",
"version": "0.1.0",
"repository": "git://github.com/kof/node-cjson.git",
"keywords": [ "json", "parser", "comments", "config", "loader"],
"author": "Oleg Slobodskoi <oleg008@gmail.com>",
"engines": {
"node": ">= 0.3.0"
},
"licenses": [
{
{
"type": "MIT",
"url" : "http://www.opensource.org/licenses/mit-license.php"
}
]
}
]
}
58 changes: 32 additions & 26 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![build status](https://secure.travis-ci.org/kof/node-cjson.png)](http://travis-ci.org/kof/node-cjson)
## CJSON (Commented Javascript Object Notation) is a comments enabled json config loader.
## CJSON (Commented Javascript Object Notation) is a comments enabled json config loader.

JSON has a good spec, is implemented in every language, has easy to read syntax and is much more powerfull then ini files.

Expand All @@ -12,10 +12,10 @@ The purpose of this module is to avoid dirty javascript configs and to enable cl
CJSON supports javascript style comments: singleline "//" and multiline "/**/". It takes care about comments inside of strings.

Example of such shiny config file:

/*
* This is my app configuration file.
*
*
*/
{
"host": "localhost",
Expand All @@ -32,48 +32,50 @@ Example of such shiny config file:
### cjson.load(path, [options]);

Load config file from given path, array of paths or directory. Second parameter is optional and can be a boolean or object.

- `path` {string} absolute path to the file
- `options` {boolean|Object} optional options
- `options` {boolean|Object} optional options

`options` defaults:
{
merge: false,
replace: null
replace: null,
freeze: false
}

If you pass `true` as second param, its the same like `{merge: true}` and will merge all configs together.
`replace` allows you to do some string replacements, see `cjson.replace`.

`freeze` - freeze config recursively, see `cjson.freeze`

Examples:
// just one config

// just one config
var conf = cjson.load('/path/to/your/config.json');

// array of configs
// array of configs
var conf = cjson.load(['/path/to/your/config1.json', '/path/to/your/config2.json']);

//output
{
config1: {key1: 'value1'}
config2: {key2: 'value2'}
}


// use optional merge parameter
// array of configs
// array of configs
var conf = cjson.load(['/path/to/your/config1.json', '/path/to/your/config2.json'], true);

// output
{
key1: 'value1',
key2: 'value2'
}


// load all config files from a directory
var conf = cjson.load('/path/to/your/configs');

// overwriting dev config with production
var paths = ['/path/to/conf.json'];
if (process.env.NODE_ENV ==='production') {
Expand All @@ -83,7 +85,7 @@ Examples:

### cjson.extend([deep], target, object1, [objectN])

Merge the contents of two or more objects together into the first object.
Merge the contents of two or more objects together into the first object.

- `deep` If true, the merge becomes recursive.
- `target` The object to extend. It will receive the new properties.
Expand All @@ -96,24 +98,28 @@ Example:

### cjson.decomment(str)

Remove javascript style comments, singleline - '//' and multiline - '/**/'. It takes care
Remove javascript style comments, singleline - '//' and multiline - '/**/'. It takes care
about comments inside of strings and escaping.

### cjson.parse(str, [reviver])

Like `JSON.parse`, but it takes care about comments. Optional `reviver` argument
is for `JSON.parse` method and will be called for every key and value at every level
Like `JSON.parse`, but it takes care about comments. Optional `reviver` argument
is for `JSON.parse` method and will be called for every key and value at every level
of the final result

### cjson.replace(str, obj)

Replace all strings `{{key}}` contained in `{key: 'value'}`, where `key` can be any
Replace all strings `{{key}}` contained in `{key: 'value'}`, where `key` can be any
property of passed `obj`.

Example:
var str = '{"path": "{{root}}/src"}'; // json file contents
cjson.replace(str, {root: '/usr'}); // '{"path": "/usr/src"}'

cjson.replace(str, {root: '/usr'}); // '{"path": "/usr/src"}'

### cjson.freeze(obj)

Recursively freeze an object.

## Installation

npm install cjson
npm install cjson
36 changes: 27 additions & 9 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,33 @@ var str = require('fs').readFileSync(fixtures + '/conf2.json').toString();

a.deepEqual(cjson.parse(str), data.conf2, '.parse method with comments');

a.deepEqual(cjson.extend({test1: 1}, {test2: 2}), {test1: 1, test2: 2}, 'extend 2 simple objects');
a.deepEqual(cjson.extend({test1: 1}, {test2: 2}, {test3: 3}), {test1: 1, test2: 2, test3: 3}, 'extend 3 simple objects');
a.deepEqual(cjson.extend({test1: 1}, true), {test1: 1}, '2 arg is not an object');
a.deepEqual(cjson.extend( true, {test1: {test1: 1}}, {test1: {test2: 2} } ), { test1: {test1: 1, test2: 2} }, 'deep extend' );
a.deepEqual(cjson.extend( true, {test: {test: 'test'}}, {test: {test: 'test'} } ), {test: {test: 'test'} }, 'deep extend, check endless lop' );
var one = {a: {b: 1}},
two = {a: {b: 2}};
cjson.extend(true, {}, one, two);
a.notDeepEqual(one, two, 'original deep object is not mangled');
(function extend() {
a.deepEqual(cjson.extend({test1: 1}, {test2: 2}), {test1: 1, test2: 2}, 'extend 2 simple objects');
a.deepEqual(cjson.extend({test1: 1}, {test2: 2}, {test3: 3}), {test1: 1, test2: 2, test3: 3}, 'extend 3 simple objects');
a.deepEqual(cjson.extend({test1: 1}, true), {test1: 1}, '2 arg is not an object');
a.deepEqual(cjson.extend( true, {test1: {test1: 1}}, {test1: {test2: 2} } ), { test1: {test1: 1, test2: 2} }, 'deep extend' );
a.deepEqual(cjson.extend( true, {test: {test: 'test'}}, {test: {test: 'test'} } ), {test: {test: 'test'} }, 'deep extend, check endless lop' );
var data1 = {a: {b: 1}},
data2 = {a: {b: 2}};
cjson.extend(true, {}, data1, data2);
a.notDeepEqual(data1, data2, 'original deep object is not mangled');
}());

(function freeze() {
var data1 = {a: {b: 1}},
data2 = {a: {b: 1}};

cjson.freeze(data1);
data1.abc = 123;
data1.a = 123;
a.deepEqual(data1, data2, 'data1 wasn\'t changed');

data1 = cjson.load(fixtures + '/conf1.json', {freeze: true}),
data2 = cjson.load(fixtures + '/conf1.json', {freeze: true});
data1.abc = 123;
data1.a = 123;
a.deepEqual(data1, data2, 'data1 wasn\'t changed');
}())

console.log('All tests passed.');

0 comments on commit 9220f17

Please sign in to comment.