corrade-nucleus-nucleons – Blame information for rev 20

Subversion Repositories:
Rev:
Rev Author Line No. Line
20 office 1 var ProtoList = require('proto-list')
2 , path = require('path')
3 , fs = require('fs')
4 , ini = require('ini')
5 , EE = require('events').EventEmitter
6 , url = require('url')
7 , http = require('http')
8  
9 var exports = module.exports = function () {
10 var args = [].slice.call(arguments)
11 , conf = new ConfigChain()
12  
13 while(args.length) {
14 var a = args.shift()
15 if(a) conf.push
16 ( 'string' === typeof a
17 ? json(a)
18 : a )
19 }
20  
21 return conf
22 }
23  
24 //recursively find a file...
25  
26 var find = exports.find = function () {
27 var rel = path.join.apply(null, [].slice.call(arguments))
28  
29 function find(start, rel) {
30 var file = path.join(start, rel)
31 try {
32 fs.statSync(file)
33 return file
34 } catch (err) {
35 if(path.dirname(start) !== start) // root
36 return find(path.dirname(start), rel)
37 }
38 }
39 return find(__dirname, rel)
40 }
41  
42 var parse = exports.parse = function (content, file, type) {
43 content = '' + content
44 // if we don't know what it is, try json and fall back to ini
45 // if we know what it is, then it must be that.
46 if (!type) {
47 try { return JSON.parse(content) }
48 catch (er) { return ini.parse(content) }
49 } else if (type === 'json') {
50 if (this.emit) {
51 try { return JSON.parse(content) }
52 catch (er) { this.emit('error', er) }
53 } else {
54 return JSON.parse(content)
55 }
56 } else {
57 return ini.parse(content)
58 }
59 }
60  
61 var json = exports.json = function () {
62 var args = [].slice.call(arguments).filter(function (arg) { return arg != null })
63 var file = path.join.apply(null, args)
64 var content
65 try {
66 content = fs.readFileSync(file,'utf-8')
67 } catch (err) {
68 return
69 }
70 return parse(content, file, 'json')
71 }
72  
73 var env = exports.env = function (prefix, env) {
74 env = env || process.env
75 var obj = {}
76 var l = prefix.length
77 for(var k in env) {
78 if(k.indexOf(prefix) === 0)
79 obj[k.substring(l)] = env[k]
80 }
81  
82 return obj
83 }
84  
85 exports.ConfigChain = ConfigChain
86 function ConfigChain () {
87 EE.apply(this)
88 ProtoList.apply(this, arguments)
89 this._awaiting = 0
90 this._saving = 0
91 this.sources = {}
92 }
93  
94 // multi-inheritance-ish
95 var extras = {
96 constructor: { value: ConfigChain }
97 }
98 Object.keys(EE.prototype).forEach(function (k) {
99 extras[k] = Object.getOwnPropertyDescriptor(EE.prototype, k)
100 })
101 ConfigChain.prototype = Object.create(ProtoList.prototype, extras)
102  
103 ConfigChain.prototype.del = function (key, where) {
104 // if not specified where, then delete from the whole chain, scorched
105 // earth style
106 if (where) {
107 var target = this.sources[where]
108 target = target && target.data
109 if (!target) {
110 return this.emit('error', new Error('not found '+where))
111 }
112 delete target[key]
113 } else {
114 for (var i = 0, l = this.list.length; i < l; i ++) {
115 delete this.list[i][key]
116 }
117 }
118 return this
119 }
120  
121 ConfigChain.prototype.set = function (key, value, where) {
122 var target
123  
124 if (where) {
125 target = this.sources[where]
126 target = target && target.data
127 if (!target) {
128 return this.emit('error', new Error('not found '+where))
129 }
130 } else {
131 target = this.list[0]
132 if (!target) {
133 return this.emit('error', new Error('cannot set, no confs!'))
134 }
135 }
136 target[key] = value
137 return this
138 }
139  
140 ConfigChain.prototype.get = function (key, where) {
141 if (where) {
142 where = this.sources[where]
143 if (where) where = where.data
144 if (where && Object.hasOwnProperty.call(where, key)) return where[key]
145 return undefined
146 }
147 return this.list[0][key]
148 }
149  
150 ConfigChain.prototype.save = function (where, type, cb) {
151 if (typeof type === 'function') cb = type, type = null
152 var target = this.sources[where]
153 if (!target || !(target.path || target.source) || !target.data) {
154 // TODO: maybe save() to a url target could be a PUT or something?
155 // would be easy to swap out with a reddis type thing, too
156 return this.emit('error', new Error('bad save target: '+where))
157 }
158  
159 if (target.source) {
160 var pref = target.prefix || ''
161 Object.keys(target.data).forEach(function (k) {
162 target.source[pref + k] = target.data[k]
163 })
164 return this
165 }
166  
167 var type = type || target.type
168 var data = target.data
169 if (target.type === 'json') {
170 data = JSON.stringify(data)
171 } else {
172 data = ini.stringify(data)
173 }
174  
175 this._saving ++
176 fs.writeFile(target.path, data, 'utf8', function (er) {
177 this._saving --
178 if (er) {
179 if (cb) return cb(er)
180 else return this.emit('error', er)
181 }
182 if (this._saving === 0) {
183 if (cb) cb()
184 this.emit('save')
185 }
186 }.bind(this))
187 return this
188 }
189  
190 ConfigChain.prototype.addFile = function (file, type, name) {
191 name = name || file
192 var marker = {__source__:name}
193 this.sources[name] = { path: file, type: type }
194 this.push(marker)
195 this._await()
196 fs.readFile(file, 'utf8', function (er, data) {
197 if (er) this.emit('error', er)
198 this.addString(data, file, type, marker)
199 }.bind(this))
200 return this
201 }
202  
203 ConfigChain.prototype.addEnv = function (prefix, env, name) {
204 name = name || 'env'
205 var data = exports.env(prefix, env)
206 this.sources[name] = { data: data, source: env, prefix: prefix }
207 return this.add(data, name)
208 }
209  
210 ConfigChain.prototype.addUrl = function (req, type, name) {
211 this._await()
212 var href = url.format(req)
213 name = name || href
214 var marker = {__source__:name}
215 this.sources[name] = { href: href, type: type }
216 this.push(marker)
217 http.request(req, function (res) {
218 var c = []
219 var ct = res.headers['content-type']
220 if (!type) {
221 type = ct.indexOf('json') !== -1 ? 'json'
222 : ct.indexOf('ini') !== -1 ? 'ini'
223 : href.match(/\.json$/) ? 'json'
224 : href.match(/\.ini$/) ? 'ini'
225 : null
226 marker.type = type
227 }
228  
229 res.on('data', c.push.bind(c))
230 .on('end', function () {
231 this.addString(Buffer.concat(c), href, type, marker)
232 }.bind(this))
233 .on('error', this.emit.bind(this, 'error'))
234  
235 }.bind(this))
236 .on('error', this.emit.bind(this, 'error'))
237 .end()
238  
239 return this
240 }
241  
242 ConfigChain.prototype.addString = function (data, file, type, marker) {
243 data = this.parse(data, file, type)
244 this.add(data, marker)
245 return this
246 }
247  
248 ConfigChain.prototype.add = function (data, marker) {
249 if (marker && typeof marker === 'object') {
250 var i = this.list.indexOf(marker)
251 if (i === -1) {
252 return this.emit('error', new Error('bad marker'))
253 }
254 this.splice(i, 1, data)
255 marker = marker.__source__
256 this.sources[marker] = this.sources[marker] || {}
257 this.sources[marker].data = data
258 // we were waiting for this. maybe emit 'load'
259 this._resolve()
260 } else {
261 if (typeof marker === 'string') {
262 this.sources[marker] = this.sources[marker] || {}
263 this.sources[marker].data = data
264 }
265 // trigger the load event if nothing was already going to do so.
266 this._await()
267 this.push(data)
268 process.nextTick(this._resolve.bind(this))
269 }
270 return this
271 }
272  
273 ConfigChain.prototype.parse = exports.parse
274  
275 ConfigChain.prototype._await = function () {
276 this._awaiting++
277 }
278  
279 ConfigChain.prototype._resolve = function () {
280 this._awaiting--
281 if (this._awaiting === 0) this.emit('load', this)
282 }