Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

first working commit

  • Loading branch information...
commit acdaeced5c9ec6b3372ecf7fba06b445e7677c71 0 parents
Mario Gutierrez authored
20 .gitignore
@@ -0,0 +1,20 @@
+*~
+*.bak
+.DS_Store
+*.swo
+*.swp
+.*.swp
+.*.swo
+.*.bak
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+pids
+logs
+result
+!.gitignore
27 Makefile
@@ -0,0 +1,27 @@
+ROOT_DIR ?= $(realpath .)
+SRC_DIR=src
+BUILD_DIR=$(ROOT_DIR)
+
+# compute coffee dependencies
+COFFEE := $(shell find $(SRC_DIR) -name '*.coffee')
+GEN_JS = $(COFFEE:$(SRC_DIR)/%.coffee=$(BUILD_DIR)/%.js)
+
+.PHONY: all clean
+
+all: generate
+
+generate: $(GEN_JS)
+
+test: generate
+ @expresso test/settings.test.js
+
+clean:
+ @rm -f $(GEN_JS)
+
+$(BUILD_DIR)/%.js: $(SRC_DIR)/%.coffee
+ @mkdir -p $(dir $@)
+ @cat res/GENERATED_JS_HEADER > $@
+ @echo // Original file: $< >> $@
+ @coffee -b -p -c $< >> $@
+
+# vim: noexpandtab ts=4 :
137 README.md
@@ -0,0 +1,137 @@
+# node-settings
+
+Simple, hierarchical environment-based app settings.
+
+## Installation
+
+ npm install node-settings
+
+## Usage
+
+Configuration file `config/environment.js`
+
+ exports.common = {
+ storage: {
+ host: 'localhost',
+ database: 'server_dev',
+ user: 'qirogami_user',
+ password: 'password'
+ }
+ };
+
+ // Rest of environments are deep merged over `common`.
+
+ exports.development = {};
+
+ exports.test = {
+ storage: {
+ database: 'server_test',
+ password: 'foo'
+ }
+ };
+
+ exports.production = {
+ storage: {
+ password: 'secret'
+ }
+ };
+
+Application file
+
+ var Settings = require('settings');
+ var file = __dirname + '/config/environment.js';
+ var settings = new Settings(file).getEnvironment('test');
+
+ // inherited from common
+ assert.equal(settings.storage.host, 'localhost');
+
+ // specific to test
+ assert.equal(settings.storage.password, 'foo');
+
+
+### Environments
+
+The environment to use is based on (highest precedence first):
+
+1. `forceEnv` property in settings file
+
+ ## config/environment.js
+
+ exports.forceEnv = 'production'
+
+2. `$NODE_ENV` environment variable
+
+ NODE_ENV=production node app.js
+
+3. Environment argument passed to `Settings#getEnvironment()`
+
+ # the environment argument is ignored if either of the above is set
+ settings.getEnvironment('production')
+
+### Install Globally
+
+If `globalKey` option is present, the environment settings are installed
+into global space.
+
+ var settings = new Settings(file, { globalKey: '$settings' }).
+ getEnvironment();
+
+ assert.equal($settings.storage.host, 'localhost');
+
+
+### Application Defaults
+
+Settings may be preset in code. These settings may be overridden
+by the external configuration file.
+
+ var settings = new Settings(file, {
+ globalKey: '$settings',
+ defaults: {
+ framework: {
+ views: 'app/views'
+ }
+ }
+ }).getEnvironment();
+
+
+ assert.equal($settings.framework.views, 'app/views');
+
+
+## Testing
+
+ npm install expresso
+ make test
+
+## Notes
+
+js files are generated by Coffee.
+
+
+## Credits
+
+[shimondoodkin's merge and clone functions](https://github.com/shimondoodkin/nodejs-clone-extend.git)
+is one few libraries that performs a object deep merge (extend) suitable for configuration files.
+
+
+## License
+
+Copyright (C) 2010 by Mario L. Gutierrez <mario@mgutz.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
8 index.js
@@ -0,0 +1,8 @@
+/*============================================================================
+ Copyright(c) 2010 Mario L Gutierrez <mario@mgutz.com>
+ MIT Licensed
+
+ AUTO-GENERATED. DO NOT EDIT.
+============================================================================*/
+// Original file: src/index.coffee
+module.exports = require('./lib');
8 lib/index.js
@@ -0,0 +1,8 @@
+/*============================================================================
+ Copyright(c) 2010 Mario L Gutierrez <mario@mgutz.com>
+ MIT Licensed
+
+ AUTO-GENERATED. DO NOT EDIT.
+============================================================================*/
+// Original file: src/lib/index.coffee
+module.exports = require('./settings');
1,195 lib/mootools.js
@@ -0,0 +1,1195 @@
+/*
+---
+MooTools: the javascript framework
+
+web build:
+ - http://mootools.net/core/dce97d7a88c57a1b0474a9a90f0687e1
+
+packager build:
+ - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Class Core/Class.Extras Core/JSON
+
+/*
+---
+
+name: Core
+
+description: The heart of MooTools.
+
+license: MIT-style license.
+
+copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/).
+
+authors: The MooTools production team (http://mootools.net/developers/)
+
+inspiration:
+ - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
+ - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)
+
+provides: [Core, MooTools, Type, typeOf, instanceOf, Native]
+
+...
+*/
+
+(function(){
+
+this.MooTools = {
+ version: '1.3',
+ build: 'a3eed692dd85050d80168ec2c708efe901bb7db3'
+};
+
+// typeOf, instanceOf
+
+var typeOf = this.typeOf = function(item){
+ if (item == null) return 'null';
+ if (item.$family) return item.$family();
+
+ if (item.nodeName){
+ if (item.nodeType == 1) return 'element';
+ if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
+ } else if (typeof item.length == 'number'){
+ if (item.callee) return 'arguments';
+ if ('item' in item) return 'collection';
+ }
+
+ return typeof item;
+};
+
+var instanceOf = this.instanceOf = function(item, object){
+ if (item == null) return false;
+ var constructor = item.$constructor || item.constructor;
+ while (constructor){
+ if (constructor === object) return true;
+ constructor = constructor.parent;
+ }
+ return item instanceof object;
+};
+
+// Function overloading
+
+var Function = this.Function;
+
+var enumerables = true;
+for (var i in {toString: 1}) enumerables = null;
+if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
+
+Function.prototype.overloadSetter = function(usePlural){
+ var self = this;
+ return function(a, b){
+ if (a == null) return this;
+ if (usePlural || typeof a != 'string'){
+ for (var k in a) self.call(this, k, a[k]);
+ if (enumerables) for (var i = enumerables.length; i--;){
+ k = enumerables[i];
+ if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
+ }
+ } else {
+ self.call(this, a, b);
+ }
+ return this;
+ };
+};
+
+Function.prototype.overloadGetter = function(usePlural){
+ var self = this;
+ return function(a){
+ var args, result;
+ if (usePlural || typeof a != 'string') args = a;
+ else if (arguments.length > 1) args = arguments;
+ if (args){
+ result = {};
+ for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
+ } else {
+ result = self.call(this, a);
+ }
+ return result;
+ };
+};
+
+Function.prototype.extend = function(key, value){
+ this[key] = value;
+}.overloadSetter();
+
+Function.prototype.implement = function(key, value){
+ this.prototype[key] = value;
+}.overloadSetter();
+
+// From
+
+var slice = Array.prototype.slice;
+
+Function.from = function(item){
+ return (typeOf(item) == 'function') ? item : function(){
+ return item;
+ };
+};
+
+Array.from = function(item){
+ if (item == null) return [];
+ return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
+};
+
+Number.from = function(item){
+ var number = parseFloat(item);
+ return isFinite(number) ? number : null;
+};
+
+String.from = function(item){
+ return item + '';
+};
+
+// hide, protect
+
+Function.implement({
+
+ hide: function(){
+ this.$hidden = true;
+ return this;
+ },
+
+ protect: function(){
+ this.$protected = true;
+ return this;
+ }
+
+});
+
+// Type
+
+var Type = this.Type = function(name, object){
+ if (name){
+ var lower = name.toLowerCase();
+ var typeCheck = function(item){
+ return (typeOf(item) == lower);
+ };
+
+ Type['is' + name] = typeCheck;
+ if (object != null){
+ object.prototype.$family = (function(){
+ return lower;
+ }).hide();
+
+ }
+ }
+
+ if (object == null) return null;
+
+ object.extend(this);
+ object.$constructor = Type;
+ object.prototype.$constructor = object;
+
+ return object;
+};
+
+var toString = Object.prototype.toString;
+
+Type.isEnumerable = function(item){
+ return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]' );
+};
+
+var hooks = {};
+
+var hooksOf = function(object){
+ var type = typeOf(object.prototype);
+ return hooks[type] || (hooks[type] = []);
+};
+
+var implement = function(name, method){
+ if (method && method.$hidden) return this;
+
+ var hooks = hooksOf(this);
+
+ for (var i = 0; i < hooks.length; i++){
+ var hook = hooks[i];
+ if (typeOf(hook) == 'type') implement.call(hook, name, method);
+ else hook.call(this, name, method);
+ }
+
+ var previous = this.prototype[name];
+ if (previous == null || !previous.$protected) this.prototype[name] = method;
+
+ if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item){
+ return method.apply(item, slice.call(arguments, 1));
+ });
+
+ return this;
+};
+
+var extend = function(name, method){
+ if (method && method.$hidden) return this;
+ var previous = this[name];
+ if (previous == null || !previous.$protected) this[name] = method;
+ return this;
+};
+
+Type.implement({
+
+ implement: implement.overloadSetter(),
+
+ extend: extend.overloadSetter(),
+
+ alias: function(name, existing){
+ implement.call(this, name, this.prototype[existing]);
+ }.overloadSetter(),
+
+ mirror: function(hook){
+ hooksOf(this).push(hook);
+ return this;
+ }
+
+});
+
+new Type('Type', Type);
+
+// Default Types
+
+var force = function(name, object, methods){
+ var isType = (object != Object),
+ prototype = object.prototype;
+
+ if (isType) object = new Type(name, object);
+
+ for (var i = 0, l = methods.length; i < l; i++){
+ var key = methods[i],
+ generic = object[key],
+ proto = prototype[key];
+
+ if (generic) generic.protect();
+
+ if (isType && proto){
+ delete prototype[key];
+ prototype[key] = proto.protect();
+ }
+ }
+
+ if (isType) object.implement(prototype);
+
+ return force;
+};
+
+force('String', String, [
+ 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search',
+ 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase'
+])('Array', Array, [
+ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice',
+ 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'
+])('Number', Number, [
+ 'toExponential', 'toFixed', 'toLocaleString', 'toPrecision'
+])('Function', Function, [
+ 'apply', 'call', 'bind'
+])('RegExp', RegExp, [
+ 'exec', 'test'
+])('Object', Object, [
+ 'create', 'defineProperty', 'defineProperties', 'keys',
+ 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames',
+ 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'
+])('Date', Date, ['now']);
+
+Object.extend = extend.overloadSetter();
+
+Date.extend('now', function(){
+ return +(new Date);
+});
+
+new Type('Boolean', Boolean);
+
+// fixes NaN returning as Number
+
+Number.prototype.$family = function(){
+ return isFinite(this) ? 'number' : 'null';
+}.hide();
+
+// Number.random
+
+Number.extend('random', function(min, max){
+ return Math.floor(Math.random() * (max - min + 1) + min);
+});
+
+// forEach, each
+
+Object.extend('forEach', function(object, fn, bind){
+ for (var key in object){
+ if (object.hasOwnProperty(key)) fn.call(bind, object[key], key, object);
+ }
+});
+
+Object.each = Object.forEach;
+
+Array.implement({
+
+ forEach: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) fn.call(bind, this[i], i, this);
+ }
+ },
+
+ each: function(fn, bind){
+ Array.forEach(this, fn, bind);
+ return this;
+ }
+
+});
+
+// Array & Object cloning, Object merging and appending
+
+var cloneOf = function(item){
+ switch (typeOf(item)){
+ case 'array': return item.clone();
+ case 'object': return Object.clone(item);
+ default: return item;
+ }
+};
+
+Array.implement('clone', function(){
+ var i = this.length, clone = new Array(i);
+ while (i--) clone[i] = cloneOf(this[i]);
+ return clone;
+});
+
+var mergeOne = function(source, key, current){
+ switch (typeOf(current)){
+ case 'object':
+ if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
+ else source[key] = Object.clone(current);
+ break;
+ case 'array': source[key] = current.clone(); break;
+ default: source[key] = current;
+ }
+ return source;
+};
+
+Object.extend({
+
+ merge: function(source, k, v){
+ if (typeOf(k) == 'string') return mergeOne(source, k, v);
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var object = arguments[i];
+ for (var key in object) mergeOne(source, key, object[key]);
+ }
+ return source;
+ },
+
+ clone: function(object){
+ var clone = {};
+ for (var key in object) clone[key] = cloneOf(object[key]);
+ return clone;
+ },
+
+ append: function(original){
+ for (var i = 1, l = arguments.length; i < l; i++){
+ var extended = arguments[i] || {};
+ for (var key in extended) original[key] = extended[key];
+ }
+ return original;
+ }
+
+});
+
+// Object-less types
+
+['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name){
+ new Type(name);
+});
+
+// Unique ID
+
+var UID = Date.now();
+
+String.extend('uniqueID', function(){
+ return (UID++).toString(36);
+});
+
+
+
+})();
+
+
+/*
+---
+
+name: Array
+
+description: Contains Array Prototypes like each, contains, and erase.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Array
+
+...
+*/
+
+Array.implement({
+
+ invoke: function(methodName){
+ var args = Array.slice(arguments, 1);
+ return this.map(function(item){
+ return item[methodName].apply(item, args);
+ });
+ },
+
+ every: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
+ }
+ return true;
+ },
+
+ filter: function(fn, bind){
+ var results = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]);
+ }
+ return results;
+ },
+
+ clean: function(){
+ return this.filter(function(item){
+ return item != null;
+ });
+ },
+
+ indexOf: function(item, from){
+ var len = this.length;
+ for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
+ if (this[i] === item) return i;
+ }
+ return -1;
+ },
+
+ map: function(fn, bind){
+ var results = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ if (i in this) results[i] = fn.call(bind, this[i], i, this);
+ }
+ return results;
+ },
+
+ some: function(fn, bind){
+ for (var i = 0, l = this.length; i < l; i++){
+ if ((i in this) && fn.call(bind, this[i], i, this)) return true;
+ }
+ return false;
+ },
+
+ associate: function(keys){
+ var obj = {}, length = Math.min(this.length, keys.length);
+ for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
+ return obj;
+ },
+
+ link: function(object){
+ var result = {};
+ for (var i = 0, l = this.length; i < l; i++){
+ for (var key in object){
+ if (object[key](this[i])){
+ result[key] = this[i];
+ delete object[key];
+ break;
+ }
+ }
+ }
+ return result;
+ },
+
+ contains: function(item, from){
+ return this.indexOf(item, from) != -1;
+ },
+
+ append: function(array){
+ this.push.apply(this, array);
+ return this;
+ },
+
+ getLast: function(){
+ return (this.length) ? this[this.length - 1] : null;
+ },
+
+ getRandom: function(){
+ return (this.length) ? this[Number.random(0, this.length - 1)] : null;
+ },
+
+ include: function(item){
+ if (!this.contains(item)) this.push(item);
+ return this;
+ },
+
+ combine: function(array){
+ for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
+ return this;
+ },
+
+ erase: function(item){
+ for (var i = this.length; i--;){
+ if (this[i] === item) this.splice(i, 1);
+ }
+ return this;
+ },
+
+ empty: function(){
+ this.length = 0;
+ return this;
+ },
+
+ flatten: function(){
+ var array = [];
+ for (var i = 0, l = this.length; i < l; i++){
+ var type = typeOf(this[i]);
+ if (type == 'null') continue;
+ array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
+ }
+ return array;
+ },
+
+ pick: function(){
+ for (var i = 0, l = this.length; i < l; i++){
+ if (this[i] != null) return this[i];
+ }
+ return null;
+ },
+
+ hexToRgb: function(array){
+ if (this.length != 3) return null;
+ var rgb = this.map(function(value){
+ if (value.length == 1) value += value;
+ return value.toInt(16);
+ });
+ return (array) ? rgb : 'rgb(' + rgb + ')';
+ },
+
+ rgbToHex: function(array){
+ if (this.length < 3) return null;
+ if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
+ var hex = [];
+ for (var i = 0; i < 3; i++){
+ var bit = (this[i] - 0).toString(16);
+ hex.push((bit.length == 1) ? '0' + bit : bit);
+ }
+ return (array) ? hex : '#' + hex.join('');
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: String
+
+description: Contains String Prototypes like camelCase, capitalize, test, and toInt.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: String
+
+...
+*/
+
+String.implement({
+
+ test: function(regex, params){
+ return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
+ },
+
+ contains: function(string, separator){
+ return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
+ },
+
+ trim: function(){
+ return this.replace(/^\s+|\s+$/g, '');
+ },
+
+ clean: function(){
+ return this.replace(/\s+/g, ' ').trim();
+ },
+
+ camelCase: function(){
+ return this.replace(/-\D/g, function(match){
+ return match.charAt(1).toUpperCase();
+ });
+ },
+
+ hyphenate: function(){
+ return this.replace(/[A-Z]/g, function(match){
+ return ('-' + match.charAt(0).toLowerCase());
+ });
+ },
+
+ capitalize: function(){
+ return this.replace(/\b[a-z]/g, function(match){
+ return match.toUpperCase();
+ });
+ },
+
+ escapeRegExp: function(){
+ return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ hexToRgb: function(array){
+ var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+ return (hex) ? hex.slice(1).hexToRgb(array) : null;
+ },
+
+ rgbToHex: function(array){
+ var rgb = this.match(/\d{1,3}/g);
+ return (rgb) ? rgb.rgbToHex(array) : null;
+ },
+
+ substitute: function(object, regexp){
+ return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){
+ if (match.charAt(0) == '\\') return match.slice(1);
+ return (object[name] != null) ? object[name] : '';
+ });
+ }
+
+});
+
+
+/*
+---
+
+name: Number
+
+description: Contains Number Prototypes like limit, round, times, and ceil.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Number
+
+...
+*/
+
+Number.implement({
+
+ limit: function(min, max){
+ return Math.min(max, Math.max(min, this));
+ },
+
+ round: function(precision){
+ precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
+ return Math.round(this * precision) / precision;
+ },
+
+ times: function(fn, bind){
+ for (var i = 0; i < this; i++) fn.call(bind, i, this);
+ },
+
+ toFloat: function(){
+ return parseFloat(this);
+ },
+
+ toInt: function(base){
+ return parseInt(this, base || 10);
+ }
+
+});
+
+Number.alias('each', 'times');
+
+(function(math){
+ var methods = {};
+ math.each(function(name){
+ if (!Number[name]) methods[name] = function(){
+ return Math[name].apply(null, [this].concat(Array.from(arguments)));
+ };
+ });
+ Number.implement(methods);
+})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);
+
+
+/*
+---
+
+name: Function
+
+description: Contains Function Prototypes like create, bind, pass, and delay.
+
+license: MIT-style license.
+
+requires: Type
+
+provides: Function
+
+...
+*/
+
+Function.extend({
+
+ attempt: function(){
+ for (var i = 0, l = arguments.length; i < l; i++){
+ try {
+ return arguments[i]();
+ } catch (e){}
+ }
+ return null;
+ }
+
+});
+
+Function.implement({
+
+ attempt: function(args, bind){
+ try {
+ return this.apply(bind, Array.from(args));
+ } catch (e){}
+
+ return null;
+ },
+
+ bind: function(bind){
+ var self = this,
+ args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;
+
+ return function(){
+ if (!args && !arguments.length) return self.call(bind);
+ if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments)));
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ pass: function(args, bind){
+ var self = this;
+ if (args != null) args = Array.from(args);
+ return function(){
+ return self.apply(bind, args || arguments);
+ };
+ },
+
+ delay: function(delay, bind, args){
+ return setTimeout(this.pass(args, bind), delay);
+ },
+
+ periodical: function(periodical, bind, args){
+ return setInterval(this.pass(args, bind), periodical);
+ }
+
+});
+
+
+
+
+/*
+---
+
+name: Object
+
+description: Object generic methods
+
+license: MIT-style license.
+
+requires: Type
+
+provides: [Object, Hash]
+
+...
+*/
+
+
+Object.extend({
+
+ subset: function(object, keys){
+ var results = {};
+ for (var i = 0, l = keys.length; i < l; i++){
+ var k = keys[i];
+ results[k] = object[k];
+ }
+ return results;
+ },
+
+ map: function(object, fn, bind){
+ var results = {};
+ for (var key in object){
+ if (object.hasOwnProperty(key)) results[key] = fn.call(bind, object[key], key, object);
+ }
+ return results;
+ },
+
+ filter: function(object, fn, bind){
+ var results = {};
+ Object.each(object, function(value, key){
+ if (fn.call(bind, value, key, object)) results[key] = value;
+ });
+ return results;
+ },
+
+ every: function(object, fn, bind){
+ for (var key in object){
+ if (object.hasOwnProperty(key) && !fn.call(bind, object[key], key)) return false;
+ }
+ return true;
+ },
+
+ some: function(object, fn, bind){
+ for (var key in object){
+ if (object.hasOwnProperty(key) && fn.call(bind, object[key], key)) return true;
+ }
+ return false;
+ },
+
+ keys: function(object){
+ var keys = [];
+ for (var key in object){
+ if (object.hasOwnProperty(key)) keys.push(key);
+ }
+ return keys;
+ },
+
+ values: function(object){
+ var values = [];
+ for (var key in object){
+ if (object.hasOwnProperty(key)) values.push(object[key]);
+ }
+ return values;
+ },
+
+ getLength: function(object){
+ return Object.keys(object).length;
+ },
+
+ keyOf: function(object, value){
+ for (var key in object){
+ if (object.hasOwnProperty(key) && object[key] === value) return key;
+ }
+ return null;
+ },
+
+ contains: function(object, value){
+ return Object.keyOf(object, value) != null;
+ },
+
+ toQueryString: function(object, base){
+ var queryString = [];
+
+ Object.each(object, function(value, key){
+ if (base) key = base + '[' + key + ']';
+ var result;
+ switch (typeOf(value)){
+ case 'object': result = Object.toQueryString(value, key); break;
+ case 'array':
+ var qs = {};
+ value.each(function(val, i){
+ qs[i] = val;
+ });
+ result = Object.toQueryString(qs, key);
+ break;
+ default: result = key + '=' + encodeURIComponent(value);
+ }
+ if (value != null) queryString.push(result);
+ });
+
+ return queryString.join('&');
+ }
+
+});
+
+
+
+
+
+/*
+---
+
+name: Class
+
+description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.
+
+license: MIT-style license.
+
+requires: [Array, String, Function, Number]
+
+provides: Class
+
+...
+*/
+
+(function(){
+
+var Class = this.Class = new Type('Class', function(params){
+ if (instanceOf(params, Function)) params = {initialize: params};
+
+ var newClass = function(){
+ reset(this);
+ if (newClass.$prototyping) return this;
+ this.$caller = null;
+ var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
+ this.$caller = this.caller = null;
+ return value;
+ }.extend(this).implement(params);
+
+ newClass.$constructor = Class;
+ newClass.prototype.$constructor = newClass;
+ newClass.prototype.parent = parent;
+
+ return newClass;
+});
+
+var parent = function(){
+ if (!this.$caller) throw new Error('The method "parent" cannot be called.');
+ var name = this.$caller.$name,
+ parent = this.$caller.$owner.parent,
+ previous = (parent) ? parent.prototype[name] : null;
+ if (!previous) throw new Error('The method "' + name + '" has no parent.');
+ return previous.apply(this, arguments);
+};
+
+var reset = function(object){
+ for (var key in object){
+ var value = object[key];
+ switch (typeOf(value)){
+ case 'object':
+ var F = function(){};
+ F.prototype = value;
+ object[key] = reset(new F);
+ break;
+ case 'array': object[key] = value.clone(); break;
+ }
+ }
+ return object;
+};
+
+var wrap = function(self, key, method){
+ if (method.$origin) method = method.$origin;
+ var wrapper = function(){
+ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
+ var caller = this.caller, current = this.$caller;
+ this.caller = current; this.$caller = wrapper;
+ var result = method.apply(this, arguments);
+ this.$caller = current; this.caller = caller;
+ return result;
+ }.extend({$owner: self, $origin: method, $name: key});
+ return wrapper;
+};
+
+var implement = function(key, value, retain){
+ if (Class.Mutators.hasOwnProperty(key)){
+ value = Class.Mutators[key].call(this, value);
+ if (value == null) return this;
+ }
+
+ if (typeOf(value) == 'function'){
+ if (value.$hidden) return this;
+ this.prototype[key] = (retain) ? value : wrap(this, key, value);
+ } else {
+ Object.merge(this.prototype, key, value);
+ }
+
+ return this;
+};
+
+var getInstance = function(klass){
+ klass.$prototyping = true;
+ var proto = new klass;
+ delete klass.$prototyping;
+ return proto;
+};
+
+Class.implement('implement', implement.overloadSetter());
+
+Class.Mutators = {
+
+ Extends: function(parent){
+ this.parent = parent;
+ this.prototype = getInstance(parent);
+ },
+
+ Implements: function(items){
+ Array.from(items).each(function(item){
+ var instance = new item;
+ for (var key in instance) implement.call(this, key, instance[key], true);
+ }, this);
+ }
+};
+
+})();
+
+
+/*
+---
+
+name: Class.Extras
+
+description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.
+
+license: MIT-style license.
+
+requires: Class
+
+provides: [Class.Extras, Chain, Events, Options]
+
+...
+*/
+
+(function(){
+
+this.Chain = new Class({
+
+ $chain: [],
+
+ chain: function(){
+ this.$chain.append(Array.flatten(arguments));
+ return this;
+ },
+
+ callChain: function(){
+ return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
+ },
+
+ clearChain: function(){
+ this.$chain.empty();
+ return this;
+ }
+
+});
+
+var removeOn = function(string){
+ return string.replace(/^on([A-Z])/, function(full, first){
+ return first.toLowerCase();
+ });
+};
+
+this.Events = new Class({
+
+ $events: {},
+
+ addEvent: function(type, fn, internal){
+ type = removeOn(type);
+
+
+
+ this.$events[type] = (this.$events[type] || []).include(fn);
+ if (internal) fn.internal = true;
+ return this;
+ },
+
+ addEvents: function(events){
+ for (var type in events) this.addEvent(type, events[type]);
+ return this;
+ },
+
+ fireEvent: function(type, args, delay){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (!events) return this;
+ args = Array.from(args);
+ events.each(function(fn){
+ if (delay) fn.delay(delay, this, args);
+ else fn.apply(this, args);
+ }, this);
+ return this;
+ },
+
+ removeEvent: function(type, fn){
+ type = removeOn(type);
+ var events = this.$events[type];
+ if (events && !fn.internal){
+ var index = events.indexOf(fn);
+ if (index != -1) delete events[index];
+ }
+ return this;
+ },
+
+ removeEvents: function(events){
+ var type;
+ if (typeOf(events) == 'object'){
+ for (type in events) this.removeEvent(type, events[type]);
+ return this;
+ }
+ if (events) events = removeOn(events);
+ for (type in this.$events){
+ if (events && events != type) continue;
+ var fns = this.$events[type];
+ for (var i = fns.length; i--;) this.removeEvent(type, fns[i]);
+ }
+ return this;
+ }
+
+});
+
+this.Options = new Class({
+
+ setOptions: function(){
+ var options = this.options = Object.merge.apply(null, [{}, this.options].append(arguments));
+ if (!this.addEvent) return this;
+ for (var option in options){
+ if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
+ this.addEvent(option, options[option]);
+ delete options[option];
+ }
+ return this;
+ }
+
+});
+
+})();
+
+
+/*
+---
+
+name: JSON
+
+description: JSON encoder and decoder.
+
+license: MIT-style license.
+
+See Also: <http://www.json.org/>
+
+requires: [Array, String, Number, Function]
+
+provides: JSON
+
+...
+*/
+
+if (!this.JSON) this.JSON = {};
+
+
+
+Object.append(JSON, {
+
+ $specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},
+
+ $replaceChars: function(chr){
+ return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
+ },
+
+ encode: function(obj){
+ switch (typeOf(obj)){
+ case 'string':
+ return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
+ case 'array':
+ return '[' + String(obj.map(JSON.encode).clean()) + ']';
+ case 'object': case 'hash':
+ var string = [];
+ Object.each(obj, function(value, key){
+ var json = JSON.encode(value);
+ if (json) string.push(JSON.encode(key) + ':' + json);
+ });
+ return '{' + string + '}';
+ case 'number': case 'boolean': return String(obj);
+ case 'null': return 'null';
+ }
+ return null;
+ },
+
+ decode: function(string, secure){
+ if (typeOf(string) != 'string' || !string.length) return null;
+ if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
+ return eval('(' + string + ')');
+ }
+
+});
44 lib/settings.js
@@ -0,0 +1,44 @@
+/*============================================================================
+ Copyright(c) 2010 Mario L Gutierrez <mario@mgutz.com>
+ MIT Licensed
+
+ AUTO-GENERATED. DO NOT EDIT.
+============================================================================*/
+// Original file: src/lib/settings.coffee
+var Settings, assert, merger;
+assert = require('assert');
+merger = require('../support/merger');
+Settings = function(pathOrModule, options) {
+ var self;
+ this.options = options != null ? options : {};
+ self = this;
+ if (typeof pathOrModule === 'string') {
+ this.path = pathOrModule;
+ this.environments = require(pathOrModule);
+ } else {
+ this.environments = pathOrModule;
+ }
+ if (this.options.globalKey != null) {
+ global.__defineGetter__(this.options.globalKey, function() {
+ return self.environments[self.env];
+ });
+ }
+ return this;
+};
+Settings.prototype.getEnvironment = function(environ) {
+ var common;
+ this.env = this.environments.forceEnv || environ || process.env.NODE_ENV || 'common';
+ assert.ok(this.environments.common, 'Environment common not found in: ' + this.path);
+ assert.ok(this.environments[this.env], 'Environment `' + this.env + '` not found in: ' + this.path);
+ if (this.options.defaults != null) {
+ common = merger.cloneextend(this.options.defaults, this.environments.common);
+ } else {
+ common = merger.clone(this.environments.common);
+ }
+ if (this.env === 'common') {
+ return common;
+ } else {
+ return merger.extend(common, this.environments[this.env]);
+ }
+};
+module.exports = Settings;
5 package.json
@@ -0,0 +1,5 @@
+{ "name": "node-settings",
+ "version": "0.0.1",
+ "description": "Simple, hierarchical environment-based app settings",
+ "author": "Mario Gutierrez <mario@mgutz.com>"
+}
6 res/GENERATED_JS_HEADER
@@ -0,0 +1,6 @@
+/*============================================================================
+ Copyright(c) 2010 Mario L Gutierrez <mario@mgutz.com>
+ MIT Licensed
+
+ AUTO-GENERATED. DO NOT EDIT.
+============================================================================*/
1  src/index.coffee
@@ -0,0 +1 @@
+module.exports = require('./lib')
1  src/lib/index.coffee
@@ -0,0 +1 @@
+module.exports = require('./settings')
72 src/lib/settings.coffee
@@ -0,0 +1,72 @@
+#
+# Copyright(c) 2010 Mario L Gutierrez <mario@mgutz.com>
+# MIT Licensed
+#
+
+assert = require('assert')
+merger = require('../support/merger')
+
+# TODO - add watcher on settings file
+
+# Provides settings from an environment file or an object.
+#
+# The settings module must export, at a minimum, a `common` property.
+#
+# Other environments are deep merged into `common`.
+#
+# @param {String | Object} pathOrModule The file to load or an object.
+#
+# @example
+#
+# exports.common = {connectionString: 'mysql_dev'};
+#
+# exports.development = {};
+# exports.test = {connectionString: 'mysql_test'};
+#
+# development.connectionString === 'mysql_dev';
+# test.connectionString === 'mysql_test';
+#
+Settings = (pathOrModule, @options = {}) ->
+ self = this
+
+ if typeof pathOrModule == 'string'
+ @path = pathOrModule
+ @environments = require(pathOrModule)
+ else
+ @environments = pathOrModule
+
+ if @options.globalKey?
+ global.__defineGetter__ @options.globalKey, ->
+ self.environments[self.env]
+
+ this
+
+
+# Get settings for a specific environment.
+#
+# @param {String} environ [optional] The environment to retrieve.
+#
+# If `environ` is not passed, an environment is selected in this order
+#
+# 1. Module's `forceEnv` property
+# 2. $NODE_ENV environment variable
+# 3. `common` environment
+#
+Settings.prototype.getEnvironment = (environ) ->
+ @env = @environments.forceEnv || environ || process.env.NODE_ENV || 'common'
+
+ assert.ok @environments.common, 'Environment common not found in: ' + @path
+ assert.ok @environments[@env], 'Environment `' + @env + '` not found in: ' + @path
+
+ if @options.defaults?
+ common = merger.cloneextend(@options.defaults, @environments.common)
+ else
+ common = merger.clone(@environments.common)
+
+ if @env == 'common'
+ common
+ else
+ merger.extend common, @environments[@env]
+
+
+module.exports = Settings
19 src/test/config/environment.coffee
@@ -0,0 +1,19 @@
+# define all common settings here
+exports.common =
+ storage:
+ host: 'localhost',
+ database: 'server_dev',
+ user: 'qirogami_user',
+ password: 'password'
+
+
+# deep merges over common
+exports.development = {}
+
+exports.test =
+ storage:
+ database: 'server_test'
+
+exports.production =
+ storage:
+ database: 'server_production'
114 src/test/settings.test.coffee
@@ -0,0 +1,114 @@
+assert = require('assert')
+Settings = require('../lib')
+
+_settings = new Settings(__dirname + '/config/environment')
+
+module.exports =
+
+
+ "should be global installable using custom key": ->
+ environments =
+ 'common':
+ foo: 'boo'
+ 'development':
+ foo: 'bar'
+
+ settings = new Settings(environments, { globalKey: '$settings' }).getEnvironment('development')
+ assert.equal 'bar', $settings.foo
+
+
+ "should get specific environment": ->
+ settings = _settings.getEnvironment('development')
+ assert.equal 'server_dev', settings.storage.database
+
+ # default should be 'development'
+ settings = _settings.getEnvironment()
+ assert.equal 'server_dev', settings.storage.database
+
+ settings = _settings.getEnvironment('test')
+ assert.equal 'server_test', settings.storage.database
+
+ settings = _settings.getEnvironment('production')
+ assert.equal 'server_production', settings.storage.database
+
+
+ "should get value from ancestor if key is not found": ->
+ settings = _settings.getEnvironment('test')
+ assert.equal 'password', settings.storage.password
+
+
+ "should have a forceEnv property to force all settings through an environment": ->
+ environments =
+ 'common':
+ foo: 'boo'
+ 'development':
+ foo: 'bar'
+ 'test':
+ foo: 'bah'
+ 'prod':
+ foo: 'baz'
+ forceEnv: 'development'
+
+ settings = new Settings(environments)
+ set = settings.getEnvironment('development')
+ assert.equal 'bar', set.foo
+
+ set = settings.getEnvironment('test')
+ assert.equal 'bar', set.foo
+
+ set = settings.getEnvironment('prod')
+ assert.equal 'bar', set.foo
+
+
+ "should replace array values, not merge them": ->
+ environments =
+ 'common':
+ arr: [1, 2, 3]
+ 'development':
+ arr: [4, 5, 6]
+
+ settings = new Settings(environments).getEnvironment('development')
+ assert.eql [4, 5, 6], settings.arr
+
+
+ "should do a deep merge": ->
+ environments =
+ 'common': { a: { b: { c: { arr: [1, 2, 3], bah: 'baz' }, bar: 'bar' } } }
+ 'development': { a: { b: { c: { arr: [4, 5, 6], fu: 'bot' } } } }
+
+ settings = new Settings(environments).getEnvironment('development')
+ assert.eql [4, 5, 6], settings.a.b.c.arr
+ assert.eql 'baz', settings.a.b.c.bah
+ assert.eql 'bar', settings.a.b.bar
+ assert.eql 'bot', settings.a.b.c.fu
+
+
+ "should say which environment is current": ->
+ settings = _settings.getEnvironment('development')
+ assert.equal 'development', _settings.env
+
+ settings = _settings.getEnvironment('test')
+ assert.equal 'test', _settings.env
+
+
+ "should accept defaults": ->
+ environments =
+ 'common':
+ foo: 'boo'
+ 'development':
+ foo: 'bar'
+
+ options =
+ globalKey: '$settings'
+
+ defaults:
+ framework:
+ views: 'app/views'
+ models: 'app/models'
+
+ settings = new Settings(environments, options).getEnvironment('development')
+ assert.equal 'app/views', settings.framework.views
+
+
+
+
1  support/m.js
@@ -0,0 +1 @@
+
278 support/merger.js
@@ -0,0 +1,278 @@
+/*============================================================================
+ CREDIT: https://github.com/shimondoodkin/nodejs-clone-extend.git
+============================================================================*/
+
+function replace(a, b) {
+ if (!b) {
+ return a;
+ }
+ var key;
+ for (key in b) {
+ if (b.hasOwnProperty(key)) {
+ a[key] = b[key];
+ }
+ }
+
+ return a;
+}
+this.replace = replace;
+
+function add(a, b) {
+ if (!b) {
+ return a;
+ }
+ var key;
+ for (key in b) {
+ if (b.hasOwnProperty(key)) {
+ if (typeof a[key] === 'undefined' || a[key] === null) {
+ a[key] = b[key];
+ }
+ }
+ }
+ return a;
+}
+this.add = add;
+
+
+function extend(a, b, context, newobjs, aparent, aname, haveaparent) // context is anti circular references mechanism
+{
+ if (a == b) {
+ return a;
+ }
+ if (!b) {
+ return a;
+ }
+
+ var key, clean_context = false,
+ return_sublevel = false,
+ b_pos;
+ if (!haveaparent) {
+ aparent = {
+ 'a': a
+ };
+ aname = 'a';
+ }
+ if (!context) {
+ clean_context = true;
+ context = [];
+ newobjs = [];
+ }
+ b_pos = context.indexOf(b);
+ if (b_pos == -1) {
+ context.push(b);
+ newobjs.push([aparent, aname]);
+ } else {
+ return newobjs[b_pos][0][newobjs[b_pos][1]];
+ }
+
+ for (key in b) {
+ if (b.hasOwnProperty(key)) {
+ if (typeof a[key] === 'undefined') {
+ if (typeof b[key] === 'object') {
+ if (b[key] instanceof Array) // http://javascript.crockford.com/remedial.html
+ {
+ a[key] = extend([], b[key], context, newobjs, a, key, true);
+ } else if (b[key] === null) {
+ a[key] = null;
+ } else {
+ a[key] = extend({}, b[key], context, newobjs, a, key, true); /*a[key].constructor = b[key].constructor; a[key].prototype = b[key].prototype;*/
+ }
+ } else {
+ a[key] = b[key];
+ }
+ } else if (typeof a[key] === 'object' && a[key] !== null) {
+ a[key] = extend(a[key], b[key], context, newobjs, a, key, true); /*a[key].constructor = b[key].constructor; a[key].prototype = b[key].prototype;*/
+ } else {
+ a[key] = b[key];
+ }
+ }
+ }
+ if (clean_context) {
+ context = null;
+ newobjs = null;
+ }
+ if (!haveaparent) {
+ aparent = null;
+ return a;
+ }
+ if (typeof a === 'object' && !(a instanceof Array)) {
+/*a.constructor = b.constructor;
+ a.prototype = b.prototype*/
+ ;
+ }
+ return a;
+}
+this.extend = extend;
+
+function extenduptolevel(a, b, levels, context, newobjs, aparent, aname, haveaparent) {
+ if (a == b) {
+ return a;
+ }
+ if (!b) {
+ return a;
+ }
+
+ var key, clean_context = false,
+ return_sublevel = false;
+ if (!haveaparent) {
+ aparent = {
+ 'a': a
+ };
+ aname = 'a';
+ }
+ if (!context) {
+ clean_context = true;
+ context = [];
+ newobjs = [];
+ }
+ b_pos = context.indexOf(b);
+ if (b_pos == -1) {
+ context.push(b);
+ newobjs.push([aparent, aname]);
+ } else {
+ return newobjs[b_pos][0][newobjs[b_pos][1]];
+ }
+
+ for (key in b) {
+ if (b.hasOwnProperty(key)) {
+ if (typeof a[key] === 'undefined') {
+ if (typeof b[key] === 'object' && levels > 0) {
+ if (b[key] instanceof Array) // http://javascript.crockford.com/remedial.html
+ {
+ a[key] = extenduptolevel([], b[key], levels - 1, context, newobjs, a, key, true);
+ } else if (b[key] === null) {
+ a[key] = null;
+ } else {
+ a[key] = extenduptolevel({}, b[key], levels - 1, context, newobjs, a, key, true);
+ }
+ } else {
+ a[key] = b[key];
+ }
+ } else if (typeof a[key] === 'object' && a[key] !== null && levels > 0) {
+ a[key] = extenduptolevel(a[key], b[key], levels - 1, context, newobjs, a, key, true);
+ } else {
+ a[key] = b[key];
+ }
+ }
+ }
+ if (clean_context) {
+ context = null;
+ newobjs = null;
+ }
+
+ if (!haveaparent) {
+ aparent = null;
+ return a;
+ }
+ if (typeof a === 'object' && !(a instanceof Array)) {
+/*a.constructor = b.constructor;
+ a.prototype = b.prototype;*/
+ }
+ return a;
+}
+this.extenduptolevel = extenduptolevel;
+
+function clone(obj) {
+ if (typeof obj === 'object') {
+ if (obj === null) {
+ return null;
+ }
+ if (obj instanceof Array) {
+ return extend([], obj);
+ } else {
+ return extend({}, obj);
+ }
+ }
+ return obj;
+}
+this.clone = clone;
+
+function cloneextend(obj, exteddata) {
+ if (typeof obj === 'object') {
+ if (obj === null) {
+ return null;
+ }
+ return extend(clone(obj), exteddata);
+ }
+ return obj;
+}
+this.cloneextend = cloneextend;
+
+
+function cloneuptolevel(obj, level) // clone only numlevels levels other levels leave references
+{
+ if (typeof obj === 'object') {
+ if (obj === null) {
+ return null;
+ }
+ if (obj instanceof Array) {
+ return extenduptolevel([], obj, level);
+ }
+ return extenduptolevel({}, obj, level);
+ }
+ return obj;
+}
+this.cloneuptolevel = cloneuptolevel;
+
+
+function ObjforEach(object, block, context) {
+ for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ block.call(context, object[key], key, object);
+ }
+ }
+}
+
+function foreach(object, block, context) {
+ if (object) {
+ if (typeof object === "object" && object instanceof Array) return object.forEach(object, block, context)
+ else //if (typeof object === "object") // or (object instanceof Function)...
+ {
+ if (object) for (var key in object) {
+ if (object.hasOwnProperty(key)) {
+ block.call(context, object[key], key, object);
+ }
+ }
+ }
+ }
+}
+this.foreach = foreach;
+
+/*
+ hasbugs and useless yet interesting maybe developed layer for dot pathed object transformation:
+
+ {
+ 'foo.bar':'bluebar',
+ 'foo.color':'blue'
+ }
+
+ transforemed to
+
+ {
+ foo:
+ {
+ bar:'bluebar',
+ color:'blue'
+ }
+ }
+ //
+ function dotpath(data,dotkeys,create)
+ {
+ if(!create){var create=false;}
+ if(create) if(!data) data={};
+ var keys= dotkeys.split("."),value=data;
+ for (var i=0;i<keys.length;i++)
+ {
+ if(!value[keys[i]]) //should you create one?
+ {
+ if(create)
+ value[keys[i]]={};
+ else
+ return undefined;
+ }
+ value = value[keys[i]];
+ }
+ return value;
+ }
+ */
+
26 test/config/environment.js
@@ -0,0 +1,26 @@
+/*============================================================================
+ Copyright(c) 2010 Mario L Gutierrez <mario@mgutz.com>
+ MIT Licensed
+
+ AUTO-GENERATED. DO NOT EDIT.
+============================================================================*/
+// Original file: src/test/config/environment.coffee
+exports.common = {
+ storage: {
+ host: 'localhost',
+ database: 'server_dev',
+ user: 'qirogami_user',
+ password: 'password'
+ }
+};
+exports.development = {};
+exports.test = {
+ storage: {
+ database: 'server_test'
+ }
+};
+exports.production = {
+ storage: {
+ database: 'server_production'
+ }
+};
142 test/settings.test.js
@@ -0,0 +1,142 @@
+/*============================================================================
+ Copyright(c) 2010 Mario L Gutierrez <mario@mgutz.com>
+ MIT Licensed
+
+ AUTO-GENERATED. DO NOT EDIT.
+============================================================================*/
+// Original file: src/test/settings.test.coffee
+var Settings, assert, _settings;
+assert = require('assert');
+Settings = require('../lib');
+_settings = new Settings(__dirname + '/config/environment');
+module.exports = {
+ "should be global installable using custom key": function() {
+ var environments, settings;
+ environments = {
+ 'common': {
+ foo: 'boo'
+ },
+ 'development': {
+ foo: 'bar'
+ }
+ };
+ settings = new Settings(environments, {
+ globalKey: '$settings'
+ }).getEnvironment('development');
+ return assert.equal('bar', $settings.foo);
+ },
+ "should get specific environment": function() {
+ var settings;
+ settings = _settings.getEnvironment('development');
+ assert.equal('server_dev', settings.storage.database);
+ settings = _settings.getEnvironment();
+ assert.equal('server_dev', settings.storage.database);
+ settings = _settings.getEnvironment('test');
+ assert.equal('server_test', settings.storage.database);
+ settings = _settings.getEnvironment('production');
+ return assert.equal('server_production', settings.storage.database);
+ },
+ "should get value from ancestor if key is not found": function() {
+ var settings;
+ settings = _settings.getEnvironment('test');
+ return assert.equal('password', settings.storage.password);
+ },
+ "should have a forceEnv property to force all settings through an environment": function() {
+ var environments, set, settings;
+ environments = {
+ 'common': {
+ foo: 'boo'
+ },
+ 'development': {
+ foo: 'bar'
+ },
+ 'test': {
+ foo: 'bah'
+ },
+ 'prod': {
+ foo: 'baz'
+ },
+ forceEnv: 'development'
+ };
+ settings = new Settings(environments);
+ set = settings.getEnvironment('development');
+ assert.equal('bar', set.foo);
+ set = settings.getEnvironment('test');
+ assert.equal('bar', set.foo);
+ set = settings.getEnvironment('prod');
+ return assert.equal('bar', set.foo);
+ },
+ "should replace array values, not merge them": function() {
+ var environments, settings;
+ environments = {
+ 'common': {
+ arr: [1, 2, 3]
+ },
+ 'development': {
+ arr: [4, 5, 6]
+ }
+ };
+ settings = new Settings(environments).getEnvironment('development');
+ return assert.eql([4, 5, 6], settings.arr);
+ },
+ "should do a deep merge": function() {
+ var environments, settings;
+ environments = {
+ 'common': {
+ a: {
+ b: {
+ c: {
+ arr: [1, 2, 3],
+ bah: 'baz'
+ },
+ bar: 'bar'
+ }
+ }
+ },
+ 'development': {
+ a: {
+ b: {
+ c: {
+ arr: [4, 5, 6],
+ fu: 'bot'
+ }
+ }
+ }
+ }
+ };
+ settings = new Settings(environments).getEnvironment('development');
+ assert.eql([4, 5, 6], settings.a.b.c.arr);
+ assert.eql('baz', settings.a.b.c.bah);
+ assert.eql('bar', settings.a.b.bar);
+ return assert.eql('bot', settings.a.b.c.fu);
+ },
+ "should say which environment is current": function() {
+ var settings;
+ settings = _settings.getEnvironment('development');
+ assert.equal('development', _settings.env);
+ settings = _settings.getEnvironment('test');
+ return assert.equal('test', _settings.env);
+ },
+ "should accept defaults": function() {
+ var environments, options, settings;
+ environments = {
+ 'common': {
+ foo: 'boo'
+ },
+ 'development': {
+ foo: 'bar'
+ }
+ };
+ options = {
+ globalKey: '$settings',
+ defaults: {
+ framework: {
+ views: 'app/views',
+ models: 'app/models'
+ }
+ }
+ };
+ settings = new Settings(environments, options).getEnvironment('development');
+ return assert.equal('app/views', settings.framework.views);
+ }
+};
Please sign in to comment.
Something went wrong with that request. Please try again.