-
Notifications
You must be signed in to change notification settings - Fork 1
/
config.js
236 lines (208 loc) · 5.35 KB
/
config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/**
* ## config module
* manages configuration and the `config` object used by most modules.
*
* there are multiple ways to configure, from lowest to highest priority..
*
* * defaults - written in code
* * ini file - defined in `config.ini` or user specified ini
* * passed in from command line
*
* just to complicate things, the plugin structure needs to allow configuration
* to occur within the plugin file.. so configuration needs to be able to happen
* in a decentralised way.
*
* `lib/log.js` is a fairly good example of how to use configuration
*/
/**
* require the good stuff
*/
var _ = require('./underscore.js');
var ini = require('ini');
var path = require('path');
var cli = require('cli');
// This is the real, unwrapped one, avoids circular require
var fs = require('fs');
/**
* declaration
*/
var config;
var getCommandLine;
var getIni;
var filterOptions;
var options = {};
var validationFns = [];
var initFns = [];
/**
* ## filterOptions
*
* this is a convenience function to deal with the fact that readIni and
* commander both return an object with various methods and properties which
* are not actually part of this app's configuration. so when you run readIni
* and commander, you can run the result through this function to strip out
* *most* of the nonsense.
*
* @param {object} list settings object
*/
filterOptions = function(list) {
return _.omit(list, function(value, key) {
return (
(_.isFunction(value)) ||
(!_.has(list, key))
);
});
};
/**
* ## getCommandLine
*
* use a [cli](https://www.npmjs.com/package/cli) instance to
* handle fancy cli stuff. Also attach options defined in other modules using
* `config.push`
*/
getCommandLine = function() {
cli.enable('version', 'help');
cli.parse(options);
return cli.options;
};
/*
command.option(
'--err-no-match [action]',
'action where movie cannot be identified',
/^(leave|delete)$/i
);
command.option(
'--err-vol-num [action]',
'action where multiple volumes are not numbered',
/^(leave|delete)$/i
);
command.option(
'--err-target-exists [action]',
'action where target directory / movie already exists',
/^(leave|delete)$/i
);
*/
/**
* ## getIni
*
* load config from ini file
*
* @param {string} [configFile=config.ini] path to ini file
*/
getIni = function(configFile) {
var iniFile;
// Find config file
if (configFile) {
if (!fs.existsSync(configFile)) {
throw 'cant find config: ' + configFile;
}
}
iniFile = fs.readFileSync(configFile, 'utf-8');
iniFile = ini.parse(iniFile);
iniFile = filterOptions(iniFile);
return iniFile;
}
/**
* ## init
*
* defaults doesn't work how it might appear, it's not really defaults or extend
* or whatever.
*
* `cli` module doesn't really allow you to have a config file. you want
* precedence to run like:
*
* command line > ini file > defaults
*
* the only way I could find to do this was to collect options in `options` var,
* then strip the defaults defined therein. Then if you run `cli.parse`,
* options with no default will be unset. then you can just `_.extend`
* everything into a single `config` object.
*
* @param {Object} modOptions
* @param {Boolean} modOptions.fromIni load config from ini
* @param {Boolean} modOptions.fromCli load config from cli
*
*/
init = function(modOptions) {
var cliOptions = {};
var iniOptions = {};
var defaults = {};
// populate options
push({
options: {
configFile: [
'c',
'specify config file',
'file',
path.join(path.resolve(__dirname, '..'), 'config.ini')
]
}
});
push(require('./configShared'));
// remove the defaults from options to be passed to cli
_.each(options, function(value, key) {
if (value.length == 4) {
defaults[key] = value.pop();
}
});
if (modOptions.fromCli) {
// get options from cli, purge null values
cliOptions = _.compactObject(getCommandLine());
};
if (modOptions.fromIni) {
// get options from ini
iniOptions = getIni(cliOptions.configFile || defaults.configFile);
}
// amalgamate, right most has precedence
_.extend(config, defaults, iniOptions, cliOptions, modOptions);
_.each(validationFns, function(fn) {
fn(config);
});
_.each(initFns, function(fn) {
fn(config);
});
};
/**
* ## push
*
* see lib/fsItems.js for example usage.
*
* @param {Object} definition
* @param {Object} definition.options cli style arrays
* @param {Function} [definition.validation] validation function
* @param {Function} [definition.init] initialisation function
*
* ### initialisation functions
* will be passed completed config object
*
* ```
* function(config) {
* // initialise module
* }
* ```
*/
push = function(definition) {
_.extend(options, definition.options);
if (definition.validation) {
validationFns.push(definition.validation);
}
if (definition.init) {
initFns.push(definition.init);
}
};
/**
* ## push.keys
* here for reference, convenience
*/
push.keys = [ 'options', 'validation', 'init' ];
/**
* ## config
*
* initialize the object here.. this ensures the `init` and `push` methods
* are always present even after calling `init` to populate it.
*/
config = {
init: init,
push: push,
options: options
};
module.exports = config;