Socket
Socket
Sign inDemoInstall

gestalt

Package Overview
Dependencies
5
Maintainers
1
Versions
2577
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.8 to 0.0.9

5

examples/file_example.js

@@ -6,3 +6,4 @@ var gestalt = require('../lib/gestalt');

var cf = new ConfigFile({ source: config_yaml, format: 'yaml' } );
var cf = new ConfigFile({ source: config_yaml } );
cf.on('state', function(state_change ) {

@@ -12,5 +13,5 @@ if( state_change.state == 'invalid' ) {

} else if ( state_change.state == 'ready' ) {
console.log( cf.get('new:foo') );
cf.report()
}
});

33

lib/config.js
var EventEmitter = require('events').EventEmitter,
util = require('util'),
_ = require('underscore');
_ = require('underscore'),
fs = require('fs');

@@ -454,6 +455,8 @@

p.report = function () {
p.report = function ( stream ) {
stream = stream || process.stdout;
var self = this;
keys = [];
console.log();
stream.write("\n");
_.each( this.keys().sort(), function(key) {

@@ -466,3 +469,3 @@ var path = self._get_path_(key);

for(; i<path.length-1;++i) {
console.log( indent + path[i] + ":" );
stream.write( indent + path[i] + ":\n" );
indent += " ";

@@ -484,3 +487,3 @@ }

}
console.log(out.join(''), source);
stream.write( out.join('') + source + "\n");
keys = path;

@@ -534,1 +537,21 @@ });

};
p.writeFile = function( filename, format ) {
var ws = fs.createWriteStream( filename );
var contents;
// TODO - default format from filename
if( ! format ) {
format = 'json';
}
if( _.isFunction(format) ) {
ws.write( format( this.toObject ) );
} else if( format == 'yaml' ) {
this.report(ws);
} else if( format == 'json' ) {
ws.write( JSON.stringify( this.toObject() ) );
}
ws.end();
}

@@ -18,3 +18,17 @@ var util = require('util'),

this._contents_ = [];
this._cache_ = {};
this.state( config.state() );
var self = this;
this.on('change', function(change) {
if( ! _.isUndefined( change.value ) ) {
self._cache_[change.name] = { value: change.value, source: change.source }
} else {
delete self._cache_[change.name];
}
});
this.addOverride( config );

@@ -44,10 +58,23 @@

p.get = function(name) {
var results;
// Find the first config on the stack with a defined value
// for the name, setting the value as a side effect.
_.find( this._contents_, function(c) {
results = c.get(name);
return( ! _.isUndefined( results ) );
});
return results;
// if name is an array, reassemble it
if(_.isArray(name) ) {
name = name.join(':');
}
var vs = this._cache_[name];
if( vs ) {
return vs.value;
} else {
return undefined;
}
//// Find the first config on the stack with a defined value
//// for the name, setting the value as a side effect.
//_.find( this._contents_, function(c) {
// results = c.get(name);
// return( ! _.isUndefined( results ) );
//});
//return results;
};

@@ -91,18 +118,24 @@

p.getValSource = function(name) {
var results;
// same pattern as get
_.find( this._contents_, function(c) {
results = c.getValSource(name);
return( ! _.isUndefined( results ) );
});
return results;
if( _.isArray(name) ) {
name = name.join(':');
}
return this._cache_[name];
// var results;
// // same pattern as get
// _.find( this._contents_, function(c) {
// results = c.getValSource(name);
// return( ! _.isUndefined( results ) );
// });
// return results;
};
p.keys = function() {
var keys = [];
//var keys = [];
// Take the union of all of the keys
_.each( this._contents_, function(c) {
keys = _.union( keys, c.keys() );
});
return keys;
//_.each( this._contents_, function(c) {
// keys = _.union( keys, c.keys() );
//});
//return keys;
return _.keys( this._cache_ );
};

@@ -109,0 +142,0 @@

@@ -6,2 +6,3 @@ var fs = require('fs'),

util = require('util'),
watchr = require('watchr'),
_ = require('underscore'); //jslint ignore

@@ -18,2 +19,16 @@ /*

function detectFormat(filename) {
var m = filename.match(/\.([^\.]+)$/);
if( ! m ) {
return undefined;
}
return {
ini: 'ini',
yaml: 'yaml',
js: 'json',
json: 'json'
}[ m[1].toLowerCase() ];
}
function readFile() {

@@ -66,30 +81,39 @@ var self = this;

options = _.extend( { initial_state: 'not ready' }, options );
options = _.extend( { format: 'auto', initial_state: 'not ready' }, options );
ConfigFile.super_.call(this,options);
format = this._options_.format;
if( format == 'auto' ) {
format = detectFormat( this._source_ );
}
if( format ) {
this._options_.parser = this._options_.parser || parsers[ format ];
if( format != 'raw' ) {
this._options_.parser = this._options_.parser || parsers[ format ];
} else {
this._options_parser = parsers[ 'rawFile' ];
}
}
//console.log( "options: ", this._options_ );
this.readFile();
if( this._options_.watch ) {
if( _.isFunction(fs.watch) ) { // modern node behavior
fs.watch( this._source_, {persistent: false}, function(event,f) {
if( event == 'change' ) {
self.readFile();
}
// TODO: deal with "rename"
if( this._options_.watch ) {
this._watchr_ = watchr.watch(
{ path: this._source_,
listener: function(event, file, stat, ostat) {
if( event == 'change' ) {
self.readFile();
} else if (event == 'unlink' ) {
self.state('invalid', 'backing file deleted;');
}
}
});
} else if( _.isFunction(fs.watchFile) ) { // older behavior
fs.watchFile( this._source_, {persistent: false, interval: 100}, function(curr,prev) {
self.readFile();
});
}
}
}
util.inherits(ConfigFile, config.Configuration);
ConfigFile.prototype.readFile = readFile;
exports.ConfigFile = ConfigFile;

@@ -0,1 +1,3 @@

var _=require('underscore');
function parseYaml(data) {

@@ -19,2 +21,22 @@ return require('js-yaml').load(data.toString());

function rawFile(data) {
return { contents: data.toString() }
}
// this one works for many standard, unstructures unix config files:
function config(data) {
var object = {};
var lines = data.toString().replace(/#.*\n/g, '\n').replace(/(\n)+/g,'\n').split(/\n/);
_.each( lines, function( string ) {
var match = string.match(/\s*(\w+)\s*=\s*(.*?)\s*$/);
if(match) {
object[ match[1] ] = match[2];
}
});
return object;
}
var parsers = {

@@ -25,5 +47,6 @@ 'json': JSON.parse,

'null': nullParse,
'raw': raw
'raw': raw,
'config': config
}
exports.parsers = parsers;

@@ -12,10 +12,22 @@ var parsers = require('./format').parsers,

this._reverse_ = undefined;
if( _.isFunction(this._options_.mapper) ) {
this._mapper_ = this._options_.mapper;
this._mapper_ = this._options_.mapper;
} else {
// Build a function to dereference an object
var m = this._options_.mapper;
this._mapper_ = function(old) { return m[old]; };
var rev = {};
_.each(m, function(value, index) {
rev[value] = index;
});
this._mapper_ = function(old) { return m[old]; };
this._reverse_ = function(nuw) { return rev[nuw]; };
}
if( _.isFunction( this._options_.reverse) ) {
this._reverse_ = this._options_.reverse;
}
var org = this._org_ = this._options_.config;

@@ -59,2 +71,5 @@ this._cache_ = {forward: {}, reverse: {}};

p.map_old_name = function(old_name) {
if( _.isArray( old_name ) )
old_name = old_name.join(':');
var forward = this._cache_.forward;

@@ -76,2 +91,6 @@ var reverse = this._cache_.reverse;

p.map_new_name = function(new_name) {
if( _.isArray( new_name ) )
new_name = new_name.join(':');
var forward = this._cache_.forward;
var reverse = this._cache_.reverse;

@@ -81,5 +100,11 @@

return reverse[new_name];
} else {
return undefined;
}
if( this._reverse_ ) {
var old_name = this._reverse_(new_name);
reverse[new_name] = old_name;
if( old_name ) {
forward[old_name] = new_name;
}
}
return undefined;
}

@@ -103,14 +128,11 @@

p.remove = function(name) {
// remapped configs are read only
// this._org_.remove( this.map_new_name(name));
this._org_.remove( this.map_new_name(name));
};
p.update = function(name,value,source) {
// remapped objects are read only
// this._org_.set( this.map_new_name(name), value, source);
this._org_.set( this.map_new_name(name), value, source);
};
p.set = function(name,value,source) {
// remapped objects are read only
// this._org_.set( this.map_new_name(name), value, source);
this._org_.set( this.map_new_name(name), value, source);
};

@@ -117,0 +139,0 @@

@@ -17,3 +17,5 @@

options = _.extend( { initial_state: 'not ready' }, options );
options = _.extend( { initial_state: 'not ready',
include_stat: false,
}, options );
ZookeeperConfig.super_.call(this, options);

@@ -78,2 +80,5 @@

self.set('data', object, self._source_ + "&version=" + stat.version );
if( self._options_.include_stat ) {
self.set('stat', stat, self._source_ );
}
// Tell the world we are ready

@@ -80,0 +85,0 @@ self.state('ready');

{
"name": "gestalt",
"version": "0.0.8",
"version": "0.0.9",
"author": "Chris Howe <chris@howeville.com>",

@@ -21,3 +21,4 @@ "description": "Event driven configuration management.",

"iniparser": ">= 1.0.0",
"optimist": ">= 0.3.4"
"optimist": ">= 0.3.4",
"watchr": ">= 2.1.0"
},

@@ -24,0 +25,0 @@ "devDependencies": {

@@ -350,6 +350,15 @@ # gestalt

- report( )
- report( stream )
Generates a detailed report (on console.log) of all of the names.
Generates a detailed report on the given stream (or stdout) of all of
the names and values in YAML format.
- writeFile( filename, format )
Attempts to open a writeStream on the filename and writes the contents
of the configuration object to the file in the specified format. If format
is left out, 'json' is assumed. Valid options are 'json', 'yaml' and a
function that takes a plain javascript object (the results of toObject() )
and returns a string or a buffer object.
- toObject()

@@ -448,5 +457,17 @@

Returns the value associated with the name of the highest priority object
containing a defined value for the name.
Returns the value associated with the name of the highest priority
object containing a defined value for the name. Actually, the real
contract is that the results of get will agree with the latest change
event for the object, which is effectively the highest priority
contained object's values, with the following caveat: unlike regular
Configuration objects, it is not well defined what will happen if you
get an intermediate Configuration value.
```javascript
var container = new gestalt.ConfigContainer();
container.set("a:b:c",7);
container.get("a:b:c") // returns 7
container.get("a:b") // unspecified
```
- set(name, value, source)

@@ -525,2 +546,13 @@

-reverse (optional)
Specifies how to map new names back into old names. The reverse
function is calculated automatically if a structure mapper was
used. Reverse and Mapper should be inverses of each other over the
range of kkeys that are not mapped out of the model. If the reverse
function is not given, set and update functions will only be able to
modify existing values in the structure. With the reverse function,
they will also be able to set new values and have them map back to
the original structure.
### Methods

@@ -590,4 +622,14 @@

Tells what format the file is in. Current options are 'json', 'yaml'
and 'ini'.
and 'ini'. By default, the format will be 'auto', which will try to guess
the format of the file based on its file extension. If you specify 'raw'
as a format, the contents of the file will be added as a string to the
'contents' name of the configuration object.
- parser
Normally, the format of the file determines what to use as a parser. This option
can be used to override exactly how to turn the contents of a file into
configuration. It should be a function that can accept the data from a file read
and convert it into a raw javascript object.
- source

@@ -603,1 +645,80 @@

### ZookeeperConfig
ZooKeeper (http://zookeeper.apache.org/) is "a centralized service for
maintaining configuration information, naming, providing distributed
synchronization, and providing group services". The ZookeeperConfig
object assists in integrating zookeeper services into an overall configuration
package.
ZookeeperConfig does not really assist with writing information to zookeeper,
only to reacting to information as it changes on the zookeeper servers.
Much like the ConfigFile object, the ZookeeperConfig object is just a standard
Configuration object with a couple of extra options and methods.
This configuration object relies on the 'zookeeper' npm package. This dependency
is not listed in the gestalt package dependencies, and is only required when
a ZookeeperConfig object is first instantiated.
Zookeeper has a slightly different idea about hierarchy from other
configuration systems: in general every node can have both a value and
children. ZookeeperConfig manages this by adding two sub-configuration
objects to a configuration representing a a zookeeper node - one
called 'data' and the other called 'children'. 'data' will contain
whatever comes back from parsing the data in the zookeeper
node. 'children' contains names corresponding to the relative names of the
zookeeper nodes children and values of more ZookeeperConfig objects
corresponding to the zookeeper child nodes.
- constructor ZookeeperConfig( options )
The options are the same as for Configuration with the following additions:
- source
The source should be a string of the form 'zk://host1:port1,host2:port2/path/to/config'.
- format
What format is the data stored in on zookeeper nodes. 'raw' means that
the data for a given node will be placed in a javascript string object and
stored under the name "data" for that node. Other options are 'json', 'ini',
and 'yaml'.
- parser
The same as for ConfigFile - you can provide your own parser.
- zookeeper
Use this option to hand off an existing zookeeper connection
- zookeeper_options
If the ZookeeperConfig object is to create its own zookeeper
connection, these options will be passed to the constructor for
ZooKeeper (from the npm package).
- create_paths
Boolean. If true, once connected to zookeeper, ZookeeperConfig will
create the path of the zookeeper node it is trying to listen to, if
it is not already there.
- include_stat
Boolean. If true, zookeeper nodes will include a 'stat' name in addition
to 'data' and 'children'. The stat object will contain the stats reported
by the node's data callback.
#### Methods
- zookeeper( function(zk) )
The callback will be called when a zookeeper connection becomes
available, or immediately if it is already available. This method can
be used to make zookeeper calls on the same connection that is being
used by the ZookeeperConfig object.

@@ -62,11 +62,17 @@ var vows = require('vows'),

assert.equal( config.get("structured:d:e") , "f" );
},
'can be converted to a plain object':
function(config) {
var objVal = { a: "a", b: "b", c: [1,2,3], d: {e: "f"} };
config.set("object", { a: "a", b: "b", c: [1,2,3], d: {e: "f"} });
var objConfig = config.get("object");
var obj = objConfig.toObject();
assert.deepEqual( obj, objVal );
}
}//,
// 'can be converted to a plain object':
// function(config) {
// var objVal = { a: "a", b: "b", c: [1,2,3], d: {e: "f"} };
// config.set('a','a');/
// config.set('b','b');
// config.set('c',[1,2,3]);
// config.set('d', {e:'f'} );
//
// // config.set( { a: "a", b: "b", c: [1,2,3], d: {e: "f"} });
// // var objConfig = config.get("object");
// // var obj = objConfig.toObject();
// var obj = config.toObject();
// assert.deepEqual( obj, objVal );
// }
},

@@ -73,0 +79,0 @@ 'configuration keys and each': {

@@ -5,4 +5,4 @@ var vows = require('vows'),

_ = require('underscore'),
util = require('util');
util = require('util'),
fs = require('fs');
var gestalt = require('../lib/gestalt'),

@@ -22,9 +22,9 @@ Configuration = gestalt.Configuration,

var config = new ConfigFile( {source: config_json, format: 'json'} );
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
return promise;

@@ -38,2 +38,23 @@ },

},
"json file, auto": {
topic: function() {
var promise = new EventEmitter();
var config_json = require.resolve('./files/config.json');
var config = new ConfigFile( {source: config_json } );
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
return promise;
},
'loads properly': function(config) {
assert.instanceOf( config, Configuration);
assert.instanceOf( config, ConfigFile );
assert.deepEqual( config.toObject(), { "test1": "a", "test2": "b", "test3": {"c": "d", "e": ["f","g","h"] } , "test4": "i" } );
}
},
"yaml file": {

@@ -44,9 +65,9 @@ topic: function() {

var config = new ConfigFile( {source: config_yaml, format: 'yaml'} );
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
return promise;

@@ -62,2 +83,25 @@ },

},
"yaml file, auto": {
topic: function() {
var promise = new EventEmitter();
var config_yaml = require.resolve('./files/config.yaml');
var config = new ConfigFile( {source: config_yaml } );
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
return promise;
},
'loads properly': function(config) {
assert.instanceOf( config, Configuration);
assert.instanceOf( config, ConfigFile );
var contents = { test: [ {one:1, two:2, three:3}, {four:2, five:5,six:6}, {a: 'b', c: 'asdfasd' } ],
aaa: { bbb: { ccc: 'a' } } };
assert.deepEqual( config.toObject(), contents );
}
},
"ini file": {

@@ -68,9 +112,9 @@ topic: function() {

var config = new ConfigFile( { source: config_ini, format: 'ini'} );
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});

@@ -86,4 +130,197 @@ //config.emit('loaded');

}
} //,
},
"ini file, auto": {
topic: function() {
var promise = new EventEmitter();
var config_ini = require.resolve('./files/config.ini');
var config = new ConfigFile( { source: config_ini } );
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
//config.emit('loaded');
return promise;
},
'loads properly': function(config) {
assert.instanceOf( config, Configuration);
assert.instanceOf( config, ConfigFile );
var contents = { top: 'level', one: {stuff: 'more stuff'}, two: {x: 'y'} };
assert.deepEqual( config.toObject(), contents );
}
},
"json file, custom parser": {
topic: function() {
var promise = new EventEmitter();
var config_json = require.resolve('./files/config.json');
var config = new ConfigFile( {source: config_json, parser: function(data) {
var value = JSON.parse(data);
value.extra = "extra";
delete value.test1;
return value;
} } );
config.on('state', function(state_change) {
if( state_change.state == 'invalid' ) {
promise.emit('failure', config);
} else if ( state_change.state == 'ready' ) {
promise.emit('success', config);
}
});
return promise;
},
'loads properly': function(config) {
assert.instanceOf( config, Configuration);
assert.instanceOf( config, ConfigFile );
assert.deepEqual( config.toObject(), { "extra":"extra", "test2": "b", "test3": {"c": "d", "e": ["f","g","h"] } , "test4": "i" } );
}
},
},
"JSON File Watch": {
teardown: function(topic) {
topic.config._watchr_.close();
delete topic.config._watchr_;
},
topic: function() {
// In this scenario, we are going to
// 1) write a file containing some json data.
// 2) set up a config file watching it.
// 3) Overwrite the file with different data.
// 4) Delete the file
// 5) wait for fallout
// We will check that the changes emitted are correct
// We will check that the state changes are correct
var promise = new EventEmitter();
var object1 = {a:1, b:2, c: [1,2,3], d: {e: 55, f: 66 } };
var object2 = {a:2, b:1, c: [3,2,1], d: {g: 55, h: 66 } };
var filename = "./test_watch_file.json";
var writeStream = fs.createWriteStream(filename);
var changes = [];
var del = false;
var results = { changes: [], states: [], invalids: 0 };
var result_function = function(change, state) {
if(change) {
results.changes.push(change);
}
if(state) {
results.states.push(state);
if( state.state == 'invalid') {
results.invalids++
}
}
}
writeStream.end( JSON.stringify( object1 ) );
writeStream.on('close', function() {
var config = new ConfigFile( {source: filename, format: 'json', watch: true} );
var ready = false
config.on('change', function(change) {
if( ready )
result_function( change );
});
results.config = config;
config.on('state', function(state) {
result_function(null, state );
if( state.state == 'ready' && ! ready) {
ready = true;
setTimeout( function() {
var ws2 = fs.createWriteStream(filename);
ws2.end( JSON.stringify(object2) );
}, 1000 );
} else if (state.state == 'ready' && ready ) {
// second ready state
setTimeout( function() {
fs.unlink(filename, function() { } );
}, 1000 );
} else if ( state.state == 'invalid' && ready ) {
// invalid after ready
promise.emit('success', results );
}
});
});
return promise;
},
"test changes" : function(results) {
var change_obj = {};
_.each( results.changes, function(change) {
change_obj[ change.name + "_" + change.value ] = change;
});
expected_results = {
"a_2": {
"name": "a",
"value": 2,
"source": "./test_watch_file.json",
"old_value": 1
},
"b_1": {
"name": "b",
"value": 1,
"source": "./test_watch_file.json",
"old_value": 2
},
"c:0_3": {
"name": "c:0",
"value": 3,
"source": "./test_watch_file.json",
"old_value": 1
},
"c:2_1": {
"name": "c:2",
"value": 1,
"source": "./test_watch_file.json",
"old_value": 3
},
"d:e_undefined": {
"name": "d:e",
"source": "./test_watch_file.json",
"old_value": 55,
"value": undefined
},
"d:f_undefined": {
"name": "d:f",
"source": "./test_watch_file.json",
"old_value": 66,
"value": undefined
},
"d:g_55": {
"name": "d:g",
"value": 55,
"source": "./test_watch_file.json",
"old_value": undefined
},
"d:h_66": {
"name": "d:h",
"value": 66,
"source": "./test_watch_file.json",
"old_value": undefined
}
};
_.each( expected_results, function(r,name) {
assert.deepEqual( change_obj[name], r );
});
},
"test states": function(results) {
var expected_state = "not ready";
assert.equal( results.states.length, 5 );
_.each( results.states, function( state ) {
assert.equal( state.old_state, expected_state );
expected_state = state.state;
});
assert.equal( expected_state, "invalid" );
}
}
}).export(module);

@@ -65,3 +65,3 @@ var vows = require('vows'),

},
'mapped config read only': {
'mapped config not read only': {
topic: function() {

@@ -79,8 +79,10 @@ var config = new Configuration({source: "basic config object"});

remap.set(["e","g"],0);
remap.remove("a");
return remap;
},
'writes were not effective': function(remap) {
assert.equal( remap.get("d"), 3 );
assert.equal( remap.get("e:f"), 1);
assert.equal( remap.get("e:g"), 2);
assert.equal( remap.get("d"), 0 );
assert.equal( remap.get("e:f"), 0);
assert.equal( remap.get("e:g"), 0);
assert.isUndefined( remap.get("a") );
},

@@ -87,0 +89,0 @@ },

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc