fwissr 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +384 -0
- data/Rakefile +50 -0
- data/bin/fwissr +40 -0
- data/lib/fwissr.rb +267 -0
- data/lib/fwissr/registry.rb +154 -0
- data/lib/fwissr/source.rb +60 -0
- data/lib/fwissr/source/file.rb +88 -0
- data/lib/fwissr/source/mongodb.rb +180 -0
- data/lib/fwissr/version.rb +3 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
data.tar.gz: 933e4bc006d16954c87cc4f7b1290ec7fde652a4
|
4
|
+
metadata.gz: 2213070acd5d61baf5ebb2e5ae1188547aafb402
|
5
|
+
SHA512:
|
6
|
+
data.tar.gz: 42223b4c419f6c65e4ff88f35abfd14d787448bc79fb3aebdd1ab40746764e7542a42d10a4730f70aa86212192af708220dd4c6344cbc860afb79e088bd12fae
|
7
|
+
metadata.gz: 7f223d8a1d5f91a943b0fb0ac51fcc862a448f4279fa5479db8319a7441d20c36bb558f6eb8b2bd841bb1c6a37d4d3d4c29bdfc603a0cc5cdeea73e94ce052dc
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Fotonauts
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,384 @@
|
|
1
|
+
Fwissr
|
2
|
+
======
|
3
|
+
|
4
|
+
A simple configuration registry tool by Fotonauts.
|
5
|
+
|
6
|
+
|
7
|
+
Install
|
8
|
+
=======
|
9
|
+
|
10
|
+
```bash
|
11
|
+
$ [sudo] gem install fwissr
|
12
|
+
```
|
13
|
+
|
14
|
+
Or add it to your `Gemfile`.
|
15
|
+
|
16
|
+
|
17
|
+
Usage
|
18
|
+
=====
|
19
|
+
|
20
|
+
Create the main `fwissr.json` configuration file in either `/etc/fwissr/` or `~/.fwissr/` directory:
|
21
|
+
|
22
|
+
```json
|
23
|
+
{
|
24
|
+
"foo" : "bar",
|
25
|
+
"horn" : { "loud" : true, "sounds": [ "TUuuUuuuu", "tiiiiiiIIiii" ] }
|
26
|
+
}
|
27
|
+
```
|
28
|
+
|
29
|
+
In your application, you can access `fwissr`'s global registry that way:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'fwissr'
|
33
|
+
|
34
|
+
Fwissr['/foo']
|
35
|
+
# => "bar"
|
36
|
+
|
37
|
+
Fwissr['/horn']
|
38
|
+
# => { "loud" => true, "sounds" => [ "TUuuUuuuu", "tiiiiiiIIiii" ] }
|
39
|
+
|
40
|
+
Fwissr['/horn/loud']
|
41
|
+
# => true
|
42
|
+
|
43
|
+
Fwissr['/horn/sounds']
|
44
|
+
# => [ "TUuuUuuuu", "tiiiiiiIIiii" ]
|
45
|
+
```
|
46
|
+
|
47
|
+
In bash you can call the `fwissr` tool:
|
48
|
+
|
49
|
+
```bash
|
50
|
+
$ fwissr /foo
|
51
|
+
bar
|
52
|
+
|
53
|
+
# json output
|
54
|
+
$ fwissr -j /horn
|
55
|
+
{ "loud" : true, "sounds": [ "TUuuUuuuu", "tiiiiiiIIiii" ] }
|
56
|
+
|
57
|
+
# pretty print json output
|
58
|
+
$ fwissr -j -p /horn
|
59
|
+
{
|
60
|
+
"loud": true,
|
61
|
+
"sound": [
|
62
|
+
"TUuuUuuuu",
|
63
|
+
"tiiiiiiIIiii"
|
64
|
+
]
|
65
|
+
}
|
66
|
+
|
67
|
+
# dump registry with pretty print json output
|
68
|
+
# NOTE: yes, that's the same as 'fwissr -jp /'
|
69
|
+
$ fwissr --dump -jp
|
70
|
+
{
|
71
|
+
"horn": {
|
72
|
+
"loud": true,
|
73
|
+
"sound": [
|
74
|
+
"TUuuUuuuu",
|
75
|
+
"tiiiiiiIIiii"
|
76
|
+
]
|
77
|
+
}
|
78
|
+
}
|
79
|
+
```
|
80
|
+
|
81
|
+
|
82
|
+
Additional configuration file
|
83
|
+
=============================
|
84
|
+
|
85
|
+
In addition to the main `fwissr.json` configuration file, all files in `/etc/fwissr/` and `~/.fwissr/` directories are automatically loaded. The settings for these additional configurations are prefixed with the file name.
|
86
|
+
|
87
|
+
You can provide more configuration file locations with the `fwissr_sources` setting in `fwissr.json`:
|
88
|
+
|
89
|
+
```json
|
90
|
+
{
|
91
|
+
"fwissr_sources": [
|
92
|
+
{ "filepath": "/etc/my_app.json" }
|
93
|
+
]
|
94
|
+
}
|
95
|
+
```
|
96
|
+
|
97
|
+
For example, with that `/etc/my_app.json`:
|
98
|
+
|
99
|
+
```json
|
100
|
+
{ "foo": "bar", "bar": "baz" }
|
101
|
+
```
|
102
|
+
|
103
|
+
settings are accessed that way:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
require 'fwissr'
|
107
|
+
|
108
|
+
Fwissr['/my_app']
|
109
|
+
# => { "foo" => "bar", "bar" => "baz" }
|
110
|
+
|
111
|
+
Fwissr['/my_app/foo']
|
112
|
+
# => "bar"
|
113
|
+
|
114
|
+
Fwissr['/my_app/bar']
|
115
|
+
# => "baz"
|
116
|
+
```
|
117
|
+
|
118
|
+
You can bypass that behaviour with the `top_level` setting:
|
119
|
+
|
120
|
+
```json
|
121
|
+
{
|
122
|
+
"fwissr_sources": [
|
123
|
+
{ "filepath": "/etc/my_app.json", "top_level": true }
|
124
|
+
]
|
125
|
+
}
|
126
|
+
```
|
127
|
+
|
128
|
+
With the `top_level` setting activated the configuration settings are added to registry root:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
require 'fwissr'
|
132
|
+
|
133
|
+
Fwissr['/']
|
134
|
+
# => { "foo" => "bar", "bar" => "baz" }
|
135
|
+
|
136
|
+
Fwissr['/foo']
|
137
|
+
# => "bar"
|
138
|
+
|
139
|
+
Fwissr['/bar']
|
140
|
+
# => "baz"
|
141
|
+
```
|
142
|
+
|
143
|
+
Fwissr supports `.json` and `.yaml` configuration files.
|
144
|
+
|
145
|
+
|
146
|
+
Directory of configuration files
|
147
|
+
================================
|
148
|
+
|
149
|
+
If the `filepath` setting is a directory then all the configuration files in that directory (but NOT in subdirectories) are imported:
|
150
|
+
|
151
|
+
```json
|
152
|
+
{
|
153
|
+
"fwissr_sources": [
|
154
|
+
{ "filepath": "/mnt/my_app/conf/" },
|
155
|
+
],
|
156
|
+
}
|
157
|
+
```
|
158
|
+
|
159
|
+
With `/mnt/my_app/conf/database.yaml`:
|
160
|
+
|
161
|
+
```yaml
|
162
|
+
production:
|
163
|
+
adapter: mysql2
|
164
|
+
encoding: utf8
|
165
|
+
database: my_app_db
|
166
|
+
username: my_app_user
|
167
|
+
password: my_app_pass
|
168
|
+
host: db.my_app.com
|
169
|
+
```
|
170
|
+
|
171
|
+
and `/mnt/my_app/conf/credentials.json`:
|
172
|
+
|
173
|
+
```json
|
174
|
+
{ "key": "i5qw64816c", "code": "448e4wef161" }
|
175
|
+
```
|
176
|
+
|
177
|
+
settings are accessed that way:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
require 'fwissr'
|
181
|
+
|
182
|
+
Fwissr['/database']
|
183
|
+
# => { "production" => { "adapter" => "mysql2", "encoding" => "utf8", "database" => "my_app_db", "username" => "my_app_user", "password" => "my_app_pass", "host" => "db.my_app.com" } }
|
184
|
+
|
185
|
+
Fwissr['/database/production/host']
|
186
|
+
# => "db.my_app.com"
|
187
|
+
|
188
|
+
Fwissr['/credentials']
|
189
|
+
# => { "key" => "i5qw64816c", "code" => "448e4wef161" }
|
190
|
+
|
191
|
+
Fwissr['/credentials/key']
|
192
|
+
# => "i5qw64816c"
|
193
|
+
```
|
194
|
+
|
195
|
+
|
196
|
+
File name mapping to setting path
|
197
|
+
=================================
|
198
|
+
|
199
|
+
Use dots in file name to define a path for configuration settings.
|
200
|
+
|
201
|
+
For example:
|
202
|
+
|
203
|
+
```json
|
204
|
+
{
|
205
|
+
"fwissr_sources": [
|
206
|
+
{ "filepath": "/etc/my_app.database.slave.json" }
|
207
|
+
]
|
208
|
+
}
|
209
|
+
```
|
210
|
+
|
211
|
+
with that `/etc/my_app.database.slave.json`:
|
212
|
+
|
213
|
+
```json
|
214
|
+
{ "host": "db.my_app.com", "port": "1337" }
|
215
|
+
```
|
216
|
+
|
217
|
+
settings are accessed that way:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
require 'fwissr'
|
221
|
+
|
222
|
+
Fwissr['/my_app/database/slave/host']
|
223
|
+
# => "db.my_app.com"
|
224
|
+
|
225
|
+
Fwissr['/my_app/database/slave/port']
|
226
|
+
# => "1337"
|
227
|
+
```
|
228
|
+
|
229
|
+
|
230
|
+
Mongodb source
|
231
|
+
==============
|
232
|
+
|
233
|
+
You can define a mongob collection as a configuration source:
|
234
|
+
|
235
|
+
```json
|
236
|
+
{
|
237
|
+
"fwissr_sources": [
|
238
|
+
{ "mongodb": "mongodb://db1.example.net/my_app", "collection": "config" }
|
239
|
+
]
|
240
|
+
}
|
241
|
+
```
|
242
|
+
|
243
|
+
Each document in the collection is a setting for that configuration.
|
244
|
+
|
245
|
+
The `_id` document field is the setting key, and the `value` document field is the setting value.
|
246
|
+
|
247
|
+
For example:
|
248
|
+
|
249
|
+
```
|
250
|
+
> db["my_app.stuff"].find()
|
251
|
+
{ "_id" : "foo", "value" : "bar" }
|
252
|
+
{ "_id" : "database", "value" : { "host": "db.my_app.com", "port": "1337" } }
|
253
|
+
```
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
require 'mongo'
|
257
|
+
require 'fwissr'
|
258
|
+
|
259
|
+
Fwissr['/my_app/stuff/foo']
|
260
|
+
# => "bar"
|
261
|
+
|
262
|
+
Fwissr['/my_app/stuff/database']
|
263
|
+
# => { "host": "db.my_app.com", "port": "1337" }
|
264
|
+
|
265
|
+
Fwissr['/my_app/stuff/database/port']
|
266
|
+
# => "1337"
|
267
|
+
```
|
268
|
+
|
269
|
+
As with configuration files you can use dots in collection name to define a path for configuration settings. The `top_level` setting is also supported to bypass that behaviour. Note that the `fwissr` collection is by default a `top_level` configuration.
|
270
|
+
|
271
|
+
Fwissr supports both the official `mongo` ruby driver and the `mongoid`'s `moped` driver. Don't forget to require one of these gems.
|
272
|
+
|
273
|
+
|
274
|
+
Refreshing registry
|
275
|
+
===================
|
276
|
+
|
277
|
+
Enable registry auto-update with the `refresh` source setting.
|
278
|
+
|
279
|
+
For example:
|
280
|
+
|
281
|
+
```json
|
282
|
+
{
|
283
|
+
"fwissr_sources": [
|
284
|
+
{ "filepath": "/etc/my_app/my_app.json" },
|
285
|
+
{ "filepath": "/etc/my_app/stuff.json", "refresh": true },
|
286
|
+
{ "mongodb": "mongodb://db1.example.net/my_app", "collection": "production" },
|
287
|
+
{ "mongodb": "mongodb://db1.example.net/my_app", "collection": "config", "refresh": true }
|
288
|
+
]
|
289
|
+
}
|
290
|
+
```
|
291
|
+
|
292
|
+
The `/etc/my_app/my_app.json` configuration file and the `production` mongodb collection are read once, whereas the settings holded by the `/etc/my_app/stuff.json` configuration file and the `config` mongodb collection expire periodically and re-fetched.
|
293
|
+
|
294
|
+
The default freshness is 30 seconds, but you can change it with the `fwissr_refresh_period` setting:
|
295
|
+
|
296
|
+
```json
|
297
|
+
{
|
298
|
+
"fwissr_sources": [
|
299
|
+
{ "filepath": "/etc/my_app/my_app.json" },
|
300
|
+
{ "filepath": "/etc/my_app/stuff.json", "refresh": true },
|
301
|
+
{ "mongodb": "mongodb://db1.example.net/my_app", "collection": "production" },
|
302
|
+
{ "mongodb": "mongodb://db1.example.net/my_app", "collection": "config", "refresh": true }
|
303
|
+
],
|
304
|
+
"fwissr_refresh_period": 60
|
305
|
+
}
|
306
|
+
```
|
307
|
+
|
308
|
+
The refresh is done periodically in a thread:
|
309
|
+
|
310
|
+
```ruby
|
311
|
+
require 'fwissr'
|
312
|
+
|
313
|
+
Fwissr['/stuff/foo']
|
314
|
+
# => "bar"
|
315
|
+
|
316
|
+
# > Change '/etc/my_app/stuff.json' file by setting: {"foo":"baz"}
|
317
|
+
|
318
|
+
# Wait 2 minutes
|
319
|
+
sleep(120)
|
320
|
+
|
321
|
+
# The new value is now in the registry
|
322
|
+
Fwissr['/stuff/foo']
|
323
|
+
# => "baz"
|
324
|
+
```
|
325
|
+
|
326
|
+
|
327
|
+
Create a custom registry
|
328
|
+
========================
|
329
|
+
|
330
|
+
`fwissr` is intended to be easy to setup: just create a configuration file and that configuration is accessible via the global registry. But if you need to, you can create your own custom registry.
|
331
|
+
|
332
|
+
```ruby
|
333
|
+
require 'fwissr'
|
334
|
+
|
335
|
+
# create a custom registry
|
336
|
+
registry = Fwissr::Registry.new('refresh_period' => 20)
|
337
|
+
|
338
|
+
# add configuration sources to registry
|
339
|
+
registry.add_source(Fwissr::Source.from_settings({ 'filepath': '/etc/my_app/my_app.json' }))
|
340
|
+
registry.add_source(Fwissr::Source.from_settings({ 'filepath': '/etc/my_app/stuff.json', 'refresh': true }))
|
341
|
+
registry.add_source(Fwissr::Source.from_settings({ 'mongodb': 'mongodb://db1.example.net/my_app', 'collection': 'production' }))
|
342
|
+
registry.add_source(Fwissr::Source.from_settings({ 'mongodb': 'mongodb://db1.example.net/my_app', 'collection': 'config', 'refresh': true }))
|
343
|
+
|
344
|
+
registry['/stuff/foo']
|
345
|
+
# => 'bar'
|
346
|
+
```
|
347
|
+
|
348
|
+
|
349
|
+
Create a custom source
|
350
|
+
======================
|
351
|
+
|
352
|
+
Currently `Fwissr::Source::File` and `Fwissr::Source::Mongodb` are the two kinds of possible registry sources, but you can define your own source:
|
353
|
+
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
class MyFwissrSource < Fwissr::Source
|
357
|
+
|
358
|
+
def initialize(db_handler, options = { })
|
359
|
+
super(options)
|
360
|
+
|
361
|
+
@db_handler = db_handler
|
362
|
+
end
|
363
|
+
|
364
|
+
def fetch_conf
|
365
|
+
@db_handler.find('my_conf').to_hash
|
366
|
+
# => { 'foo' => [ 'bar', 'baz' ] }
|
367
|
+
end
|
368
|
+
|
369
|
+
end # class MyFwissrSource
|
370
|
+
|
371
|
+
registry = Fwissr::Registry.new('refresh_period' => 20)
|
372
|
+
registry.add_source(MyFwissrSource.new(my_db_handler, 'refresh' => true))
|
373
|
+
|
374
|
+
registry['/foo']
|
375
|
+
# => [ 'bar', 'baz' ]
|
376
|
+
```
|
377
|
+
|
378
|
+
|
379
|
+
Credits
|
380
|
+
=======
|
381
|
+
|
382
|
+
The Fotonauts team: http://www.fotopedia.com
|
383
|
+
|
384
|
+
Copyright (c) 2013 Fotonauts released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/./lib")
|
4
|
+
require 'fwissr'
|
5
|
+
|
6
|
+
task :default => :list
|
7
|
+
task :list do
|
8
|
+
system 'rake -T'
|
9
|
+
end
|
10
|
+
|
11
|
+
##############################################################################
|
12
|
+
# SYNTAX CHECKING
|
13
|
+
##############################################################################
|
14
|
+
desc 'Check code syntax'
|
15
|
+
task :check_syntax do
|
16
|
+
`find . -name "*.rb" |xargs -n1 ruby -c |grep -v "Syntax OK"`
|
17
|
+
puts "* Done"
|
18
|
+
end
|
19
|
+
|
20
|
+
##############################################################################
|
21
|
+
# Stats
|
22
|
+
##############################################################################
|
23
|
+
desc 'Show some stats about the code'
|
24
|
+
task :stats do
|
25
|
+
line_count = proc do |path|
|
26
|
+
Dir[path].collect { |f| File.open(f).readlines.reject { |l| l =~ /(^\s*(\#|\/\*))|^\s*$/ }.size }.inject(0){ |sum,n| sum += n }
|
27
|
+
end
|
28
|
+
comment_count = proc do |path|
|
29
|
+
Dir[path].collect { |f| File.open(f).readlines.select { |l| l =~ /^\s*\#/ }.size }.inject(0) { |sum,n| sum += n }
|
30
|
+
end
|
31
|
+
lib = line_count['lib/**/*.rb']
|
32
|
+
comment = comment_count['lib/**/*.rb']
|
33
|
+
ext = line_count['ext/**/*.{c,h}']
|
34
|
+
spec = line_count['spec/**/*.rb']
|
35
|
+
|
36
|
+
comment_ratio = '%1.2f' % (comment.to_f / lib.to_f)
|
37
|
+
spec_ratio = '%1.2f' % (spec.to_f / lib.to_f)
|
38
|
+
|
39
|
+
puts '/======================\\'
|
40
|
+
puts '| Part LOC |'
|
41
|
+
puts '|======================|'
|
42
|
+
puts "| lib #{lib.to_s.ljust(5)}|"
|
43
|
+
puts "| lib comments #{comment.to_s.ljust(5)}|"
|
44
|
+
puts "| ext #{ext.to_s.ljust(5)}|"
|
45
|
+
puts "| spec #{spec.to_s.ljust(5)}|"
|
46
|
+
puts '| ratios: |'
|
47
|
+
puts "| lib/comment #{comment_ratio.to_s.ljust(5)}|"
|
48
|
+
puts "| lib/spec #{spec_ratio.to_s.ljust(5)}|"
|
49
|
+
puts '\======================/'
|
50
|
+
end
|
data/bin/fwissr
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
|
7
|
+
require 'fwissr'
|
8
|
+
|
9
|
+
begin
|
10
|
+
# parse arguments
|
11
|
+
args = Fwissr.parse_args!(ARGV)
|
12
|
+
|
13
|
+
# get value
|
14
|
+
result = if args[:dump]
|
15
|
+
Fwissr.dump
|
16
|
+
else
|
17
|
+
Fwissr.get(args[:key])
|
18
|
+
end
|
19
|
+
|
20
|
+
# display result
|
21
|
+
if args[:json]
|
22
|
+
if FWISSR_USE_YAJL
|
23
|
+
yajl_options = args[:pretty] ? { :pretty => true, :indent => " " } : { }
|
24
|
+
puts Yajl::Encoder.encode(result, yajl_options)
|
25
|
+
else
|
26
|
+
puts args[:pretty] ? JSON.pretty_generate(result) : result.to_json
|
27
|
+
end
|
28
|
+
elsif args[:inspect]
|
29
|
+
if args[:pretty]
|
30
|
+
pp result
|
31
|
+
else
|
32
|
+
puts result.inspect
|
33
|
+
end
|
34
|
+
else
|
35
|
+
puts result
|
36
|
+
end
|
37
|
+
rescue => ex
|
38
|
+
puts "#{ex.message}"
|
39
|
+
raise ex
|
40
|
+
end
|
data/lib/fwissr.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
FWISSR_USE_YAJL = true
|
7
|
+
|
8
|
+
if FWISSR_USE_YAJL
|
9
|
+
require 'yajl'
|
10
|
+
else
|
11
|
+
require 'json'
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
require 'fwissr/version'
|
16
|
+
require 'fwissr/source'
|
17
|
+
require 'fwissr/registry'
|
18
|
+
|
19
|
+
module Fwissr
|
20
|
+
|
21
|
+
# default path where main conf file is located
|
22
|
+
DEFAULT_MAIN_CONF_PATH = "/etc/fwissr"
|
23
|
+
|
24
|
+
# default directory (relative to current user's home) where user's main conf file is located
|
25
|
+
DEFAULT_MAIN_USER_CONF_DIR = ".fwissr"
|
26
|
+
|
27
|
+
# main conf file
|
28
|
+
MAIN_CONF_FILE = "fwissr.json"
|
29
|
+
|
30
|
+
class << self
|
31
|
+
attr_writer :main_conf_path, :main_user_conf_path
|
32
|
+
|
33
|
+
# Get config files directory
|
34
|
+
def main_conf_path
|
35
|
+
@main_conf_path ||= DEFAULT_MAIN_CONF_PATH
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get user's specific config files directory
|
39
|
+
def main_user_conf_path
|
40
|
+
@main_user_conf_path ||= File.join(Fwissr.find_home, DEFAULT_MAIN_USER_CONF_DIR)
|
41
|
+
end
|
42
|
+
|
43
|
+
# finds the user's home directory
|
44
|
+
#
|
45
|
+
# Borrowed from rubygems
|
46
|
+
def find_home
|
47
|
+
['HOME', 'USERPROFILE'].each do |homekey|
|
48
|
+
return ENV[homekey] if ENV[homekey]
|
49
|
+
end
|
50
|
+
|
51
|
+
if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
|
52
|
+
return "#{ENV['HOMEDRIVE']}:#{ENV['HOMEPATH']}"
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
File.expand_path("~")
|
57
|
+
rescue
|
58
|
+
if File::ALT_SEPARATOR then
|
59
|
+
"C:/"
|
60
|
+
else
|
61
|
+
"/"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Parse command line arguments
|
67
|
+
def parse_args!(argv)
|
68
|
+
args = {
|
69
|
+
:inspect => false,
|
70
|
+
:json => false,
|
71
|
+
:dump => false,
|
72
|
+
:pretty => false,
|
73
|
+
}
|
74
|
+
|
75
|
+
# define parser
|
76
|
+
opt_parser = OptionParser.new do |opts|
|
77
|
+
opts.banner = "Usage: fwissr [-ijph] <key>\nWith key:\n\t#{Fwissr.keys.sort.join("\n\t")}\n\n"
|
78
|
+
|
79
|
+
opts.define_head "The configuration registry."
|
80
|
+
|
81
|
+
opts.on("-i", "--inspect", "Returns 'inspected' result") do
|
82
|
+
args[:inspect] = true
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on("-j", "--json", "Returns result in json") do
|
86
|
+
args[:json] = true
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on("--dump", "Dump all keys and values") do
|
90
|
+
args[:dump] = true
|
91
|
+
end
|
92
|
+
|
93
|
+
opts.on("-p", "--pretty", "Pretty output") do
|
94
|
+
args[:pretty] = true
|
95
|
+
end
|
96
|
+
|
97
|
+
opts.on("-?", "-h", "--help", "Show this help message") do
|
98
|
+
puts opts
|
99
|
+
exit
|
100
|
+
end
|
101
|
+
|
102
|
+
opts.on_tail("--version", "Show version") do
|
103
|
+
puts Fwissr::VERSION
|
104
|
+
exit
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
# parse what we have on the command line
|
110
|
+
opt_parser.parse!(argv)
|
111
|
+
|
112
|
+
# get key
|
113
|
+
if argv.empty? && !args[:dump]
|
114
|
+
puts "Please specify the key, e.g. 'fwissr /fqdn'"
|
115
|
+
puts opt_parser
|
116
|
+
exit
|
117
|
+
end
|
118
|
+
|
119
|
+
args[:key] = argv.first unless argv.empty?
|
120
|
+
|
121
|
+
args
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
#
|
126
|
+
# Global Registry
|
127
|
+
#
|
128
|
+
#
|
129
|
+
# NOTE: Parses main conf files (/etc/fwissr/fwissr.json and ~/.fwissr/fwissr.json) then uses 'fwissr_sources' setting to setup additional sources
|
130
|
+
#
|
131
|
+
# Example of /etc/fwissr/fwissr.json file:
|
132
|
+
#
|
133
|
+
# {
|
134
|
+
# 'fwissr_sources': [
|
135
|
+
# { 'filepath': '/mnt/my_app/conf/' },
|
136
|
+
# { 'filepath': '/etc/my_app.json' },
|
137
|
+
# { 'mongodb': 'mongodb://db1.example.net/my_app', 'collection': 'config', 'refresh': true },
|
138
|
+
# ],
|
139
|
+
# 'fwissr_refresh_period': 30,
|
140
|
+
# }
|
141
|
+
#
|
142
|
+
|
143
|
+
# access global registry with Fwissr['/foo/bar']
|
144
|
+
def global_registry
|
145
|
+
@global_registry ||= begin
|
146
|
+
result = Fwissr::Registry.new('refresh_period' => self.main_conf['fwissr_refresh_period'])
|
147
|
+
|
148
|
+
# check main conf files
|
149
|
+
if File.exists?(self.main_conf_path) || File.exists?(self.main_user_conf_path)
|
150
|
+
# setup main conf files sources
|
151
|
+
if File.exists?(self.main_conf_path)
|
152
|
+
result.add_source(Fwissr::Source.from_settings({ 'filepath' => self.main_conf_path }))
|
153
|
+
end
|
154
|
+
|
155
|
+
if File.exists?(self.main_user_conf_path)
|
156
|
+
result.add_source(Fwissr::Source.from_settings({ 'filepath' => self.main_user_conf_path }))
|
157
|
+
end
|
158
|
+
|
159
|
+
# setup additional sources
|
160
|
+
if !self.main_conf['fwissr_sources'].nil?
|
161
|
+
self.main_conf['fwissr_sources'].each do |source_setting|
|
162
|
+
result.add_source(Fwissr::Source.from_settings(source_setting))
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
result
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# fetch main fwissr conf
|
172
|
+
def main_conf
|
173
|
+
@main_conf ||= begin
|
174
|
+
result = { }
|
175
|
+
|
176
|
+
if File.exists?(self.main_conf_file)
|
177
|
+
result = self.merge_conf!(result, self.parse_conf_file(self.main_conf_file))
|
178
|
+
end
|
179
|
+
|
180
|
+
if File.exists?(self.main_user_conf_file)
|
181
|
+
result = self.merge_conf!(result, self.parse_conf_file(self.main_user_conf_file))
|
182
|
+
end
|
183
|
+
|
184
|
+
result
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def main_conf_file
|
189
|
+
@main_conf_file ||= File.join(self.main_conf_path, MAIN_CONF_FILE)
|
190
|
+
end
|
191
|
+
|
192
|
+
def main_user_conf_file
|
193
|
+
@main_user_conf_file ||= File.join(self.main_user_conf_path, MAIN_CONF_FILE)
|
194
|
+
end
|
195
|
+
|
196
|
+
# delegate to global registry
|
197
|
+
[ :[], :get ].each do |meth_name|
|
198
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
199
|
+
def #{meth_name}(key)
|
200
|
+
self.global_registry[key]
|
201
|
+
end
|
202
|
+
EOS
|
203
|
+
end
|
204
|
+
|
205
|
+
[ :keys, :dump ].each do |meth_name|
|
206
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
207
|
+
def #{meth_name}
|
208
|
+
self.global_registry.__send__('#{meth_name}')
|
209
|
+
end
|
210
|
+
EOS
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
#
|
215
|
+
# Utils
|
216
|
+
#
|
217
|
+
|
218
|
+
def parse_conf_file(conf_file_path)
|
219
|
+
conf_file_ext = File.extname(conf_file_path)
|
220
|
+
|
221
|
+
case conf_file_ext
|
222
|
+
when ".json"
|
223
|
+
# json file
|
224
|
+
if FWISSR_USE_YAJL
|
225
|
+
Yajl::Parser.parse(File.read(conf_file_path), :check_utf8 => false)
|
226
|
+
else
|
227
|
+
JSON.parse(File.read(conf_file_path))
|
228
|
+
end
|
229
|
+
when ".yaml", ".yml"
|
230
|
+
# yaml file
|
231
|
+
YAML.load_file(conf_file_path)
|
232
|
+
else
|
233
|
+
raise "Unsupported conf file kind: #{conf_file_path}"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# borrowed from rails
|
238
|
+
def merge_conf(to_hash, other_hash)
|
239
|
+
self.merge_conf!(to_hash.dup, other_hash)
|
240
|
+
end
|
241
|
+
|
242
|
+
# borrowed from rails
|
243
|
+
def merge_conf!(to_hash, other_hash)
|
244
|
+
other_hash.each_pair do |k,v|
|
245
|
+
tv = to_hash[k]
|
246
|
+
to_hash[k] = (tv.is_a?(Hash) && v.is_a?(Hash)) ? self.merge_conf(tv, v) : v
|
247
|
+
end
|
248
|
+
to_hash
|
249
|
+
end
|
250
|
+
|
251
|
+
# simple deep freezer
|
252
|
+
def deep_freeze(obj)
|
253
|
+
if obj.is_a?(Hash)
|
254
|
+
obj.each do |k, v|
|
255
|
+
self.deep_freeze(v)
|
256
|
+
end
|
257
|
+
elsif obj.is_a?(Array)
|
258
|
+
obj.each do |v|
|
259
|
+
self.deep_freeze(v)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
obj.freeze
|
264
|
+
end
|
265
|
+
end # class << self
|
266
|
+
|
267
|
+
end # module Fwissr
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Fwissr
|
4
|
+
|
5
|
+
class Registry
|
6
|
+
|
7
|
+
# refresh period in seconds
|
8
|
+
DEFAULT_REFRESH_PERIOD = 30
|
9
|
+
|
10
|
+
#
|
11
|
+
# API
|
12
|
+
#
|
13
|
+
|
14
|
+
attr_reader :refresh_period
|
15
|
+
|
16
|
+
def initialize(options = { })
|
17
|
+
@refresh_period = options['refresh_period'] || DEFAULT_REFRESH_PERIOD
|
18
|
+
|
19
|
+
@registry = { }
|
20
|
+
@sources = [ ]
|
21
|
+
|
22
|
+
# mutex for @registry and @sources
|
23
|
+
@semaphore = Mutex.new
|
24
|
+
|
25
|
+
@refresh_thread = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_source(source)
|
29
|
+
@semaphore.synchronize do
|
30
|
+
@sources << source
|
31
|
+
end
|
32
|
+
|
33
|
+
if @registry.frozen?
|
34
|
+
# already frozen, must reload everything
|
35
|
+
self.reload!
|
36
|
+
else
|
37
|
+
@semaphore.synchronize do
|
38
|
+
Fwissr.merge_conf!(@registry, source.get_conf)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
self.ensure_refresh_thread
|
43
|
+
end
|
44
|
+
|
45
|
+
def reload!
|
46
|
+
self.reset!
|
47
|
+
self.load!
|
48
|
+
end
|
49
|
+
|
50
|
+
def get(key)
|
51
|
+
# split key
|
52
|
+
key_ary = key.split('/')
|
53
|
+
|
54
|
+
# remove first empty part
|
55
|
+
key_ary.shift if (key_ary.first == '')
|
56
|
+
|
57
|
+
cur_hash = self.registry
|
58
|
+
key_ary.each do |key_part|
|
59
|
+
cur_hash = cur_hash[key_part]
|
60
|
+
return nil if cur_hash.nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
cur_hash
|
64
|
+
end
|
65
|
+
|
66
|
+
alias :[] :get
|
67
|
+
|
68
|
+
def keys
|
69
|
+
result = [ ]
|
70
|
+
_keys(result, [ ], self.registry)
|
71
|
+
result.sort
|
72
|
+
end
|
73
|
+
|
74
|
+
def dump
|
75
|
+
self.registry
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
#
|
80
|
+
# PRIVATE
|
81
|
+
#
|
82
|
+
|
83
|
+
def refresh_thread
|
84
|
+
@refresh_thread
|
85
|
+
end
|
86
|
+
|
87
|
+
def have_refreshable_source?
|
88
|
+
@semaphore.synchronize do
|
89
|
+
!@sources.find { |source| source.can_refresh? }.nil?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def ensure_refresh_thread
|
94
|
+
# check refresh thread state
|
95
|
+
if ((@refresh_period > 0) && self.have_refreshable_source?) && (!@refresh_thread || !@refresh_thread.alive?)
|
96
|
+
# (re)start refresh thread
|
97
|
+
@refresh_thread = Thread.new do
|
98
|
+
while(true) do
|
99
|
+
sleep(@refresh_period)
|
100
|
+
self.load!
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def ensure_frozen
|
107
|
+
if !@registry.frozen?
|
108
|
+
@semaphore.synchronize do
|
109
|
+
Fwissr.deep_freeze(@registry)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def reset!
|
115
|
+
@semaphore.synchronize do
|
116
|
+
@registry = { }
|
117
|
+
|
118
|
+
@sources.each do |source|
|
119
|
+
source.reset!
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def load!
|
125
|
+
@semaphore.synchronize do
|
126
|
+
@registry = { }
|
127
|
+
|
128
|
+
@sources.each do |source|
|
129
|
+
source_conf = source.get_conf
|
130
|
+
Fwissr.merge_conf!(@registry, source_conf)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def registry
|
136
|
+
self.ensure_refresh_thread
|
137
|
+
self.ensure_frozen
|
138
|
+
|
139
|
+
@registry
|
140
|
+
end
|
141
|
+
|
142
|
+
# helper for #keys
|
143
|
+
def _keys(result, key_ary, hash)
|
144
|
+
hash.each do |key, value|
|
145
|
+
key_ary << key
|
146
|
+
result << "/#{key_ary.join('/')}"
|
147
|
+
_keys(result, key_ary, value) if value.is_a?(Hash)
|
148
|
+
key_ary.pop
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end # module Registry
|
153
|
+
|
154
|
+
end # module Fwissr
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class Fwissr::Source
|
2
|
+
|
3
|
+
autoload :File, 'fwissr/source/file'
|
4
|
+
autoload :Mongodb, 'fwissr/source/mongodb'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def from_settings(settings)
|
8
|
+
raise "Unexpected source settings class: #{settings.inspect}" unless settings.is_a?(Hash)
|
9
|
+
|
10
|
+
if settings['filepath']
|
11
|
+
Fwissr::Source::File.from_settings(settings)
|
12
|
+
elsif settings['mongodb']
|
13
|
+
Fwissr::Source::Mongodb.from_settings(settings)
|
14
|
+
else
|
15
|
+
raise "Unexpected source settings kind: #{settings.inspect}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end # class << self
|
19
|
+
|
20
|
+
|
21
|
+
#
|
22
|
+
# API
|
23
|
+
#
|
24
|
+
|
25
|
+
attr_reader :options
|
26
|
+
|
27
|
+
def initialize(options = { })
|
28
|
+
@options = options
|
29
|
+
|
30
|
+
@conf = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# reset source
|
34
|
+
def reset!
|
35
|
+
@conf = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# source can be refreshed ?
|
39
|
+
def can_refresh?
|
40
|
+
@options && (@options['refresh'] == true)
|
41
|
+
end
|
42
|
+
|
43
|
+
# get conf
|
44
|
+
def get_conf
|
45
|
+
if (@conf && !self.can_refresh?)
|
46
|
+
# return already fetched conf if refresh is not allowed
|
47
|
+
@conf
|
48
|
+
else
|
49
|
+
# fetch conf
|
50
|
+
@conf = self.fetch_conf
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# fetch conf from source
|
55
|
+
def fetch_conf
|
56
|
+
# MUST be implemented by child class
|
57
|
+
raise "not implemented"
|
58
|
+
end
|
59
|
+
|
60
|
+
end # class Fwissr::Source
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class Fwissr::Source::File < Fwissr::Source
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def from_path(path, options = { })
|
6
|
+
if path.nil? || (path == '')
|
7
|
+
raise "Unexpected file source path: #{path.inspect}"
|
8
|
+
end
|
9
|
+
|
10
|
+
self.new(path, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def from_settings(settings)
|
14
|
+
options = settings.dup
|
15
|
+
options.delete('filepath')
|
16
|
+
|
17
|
+
self.from_path(settings['filepath'], options)
|
18
|
+
end
|
19
|
+
|
20
|
+
end # class << self
|
21
|
+
|
22
|
+
|
23
|
+
TOP_LEVEL_CONF_FILES = [ 'fwissr' ].freeze
|
24
|
+
|
25
|
+
attr_reader :path
|
26
|
+
|
27
|
+
#
|
28
|
+
# API
|
29
|
+
#
|
30
|
+
|
31
|
+
def initialize(path, options = { })
|
32
|
+
super(options)
|
33
|
+
|
34
|
+
raise "File not found: #{path}" if !::File.exists?(path)
|
35
|
+
|
36
|
+
@path = path
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_conf
|
40
|
+
result = { }
|
41
|
+
|
42
|
+
conf_files = if ::File.directory?(@path)
|
43
|
+
Dir[@path + "/*.{json,yml}"].sort
|
44
|
+
else
|
45
|
+
[ @path ]
|
46
|
+
end
|
47
|
+
|
48
|
+
conf_files.each do |conf_file_path|
|
49
|
+
next unless ::File.file?(conf_file_path)
|
50
|
+
|
51
|
+
self.merge_conf_file!(result, conf_file_path)
|
52
|
+
end
|
53
|
+
|
54
|
+
result
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
#
|
59
|
+
# PRIVATE
|
60
|
+
#
|
61
|
+
|
62
|
+
def merge_conf_file!(result, conf_file_path)
|
63
|
+
# parse conf file
|
64
|
+
conf = Fwissr.parse_conf_file(conf_file_path)
|
65
|
+
if conf
|
66
|
+
conf_file_name = ::File.basename(conf_file_path, ::File.extname(conf_file_path))
|
67
|
+
|
68
|
+
result_part = result
|
69
|
+
|
70
|
+
unless TOP_LEVEL_CONF_FILES.include?(conf_file_name) || @options['top_level']
|
71
|
+
# merge conf at the correct place in registry
|
72
|
+
#
|
73
|
+
# eg: my_app.json => /my_app
|
74
|
+
# my_app.database.yml => /my_app/database
|
75
|
+
# my_app.database.slave.yml => /my_app/database/slave
|
76
|
+
key_ary = conf_file_name.split('.')
|
77
|
+
key_ary.each do |key_part|
|
78
|
+
result_part = (result_part[key_part] ||= { })
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
Fwissr.merge_conf!(result_part, conf)
|
83
|
+
else
|
84
|
+
raise "Failed to parse conf file: #{conf_file_path}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end # class Fwissr::Source::File
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'moped'
|
5
|
+
rescue LoadError
|
6
|
+
begin
|
7
|
+
require 'mongo'
|
8
|
+
rescue LoadError
|
9
|
+
raise "[fwissr] Can't find any suitable mongodb driver: please install 'mongo' or 'moped' gem"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Fwissr::Source::Mongodb < Fwissr::Source
|
14
|
+
|
15
|
+
class Connection
|
16
|
+
|
17
|
+
attr_reader :db_name
|
18
|
+
|
19
|
+
# init
|
20
|
+
def initialize(uri)
|
21
|
+
raise "URI is missing: #{uri}" if (uri.nil? || uri == '')
|
22
|
+
|
23
|
+
@uri = uri
|
24
|
+
@collections = { }
|
25
|
+
|
26
|
+
@kind = if defined?(::Moped)
|
27
|
+
# moped driver
|
28
|
+
:moped
|
29
|
+
elsif defined?(::Mongo::MongoClient)
|
30
|
+
# mongo ruby driver < 2.0.0
|
31
|
+
:mongo
|
32
|
+
elsif defined?(::Mongo::Client)
|
33
|
+
raise "Sorry, mongo gem >= 2.0 is not supported yet"
|
34
|
+
else
|
35
|
+
raise "Can't find any suitable mongodb driver: please install 'mongo' or 'moped' gem"
|
36
|
+
end
|
37
|
+
|
38
|
+
# parse URI
|
39
|
+
parsed_uri = URI.parse(@uri)
|
40
|
+
@db_name = parsed_uri.path[1..-1]
|
41
|
+
|
42
|
+
if @db_name.nil? || (@db_name == '')
|
43
|
+
raise "Missing database in mongodb settings: #{settings['mongodb'].inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def conn
|
48
|
+
@conn ||= begin
|
49
|
+
case @kind
|
50
|
+
when :moped
|
51
|
+
::Moped::Session.connect(@uri)
|
52
|
+
when :mongo
|
53
|
+
::Mongo::MongoClient.from_uri(@uri)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def collection(col_name)
|
59
|
+
@collections[col_name] ||= begin
|
60
|
+
case @kind
|
61
|
+
when :moped
|
62
|
+
self.conn[col_name]
|
63
|
+
when :mongo
|
64
|
+
self.conn.db(@db_name).collection(col_name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# returns an Enumerator for all documents from given collection
|
70
|
+
def fetch(col_name)
|
71
|
+
case @kind
|
72
|
+
when :moped, :mongo
|
73
|
+
self.collection(col_name).find()
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# insert document in collection
|
78
|
+
def insert(col_name, doc)
|
79
|
+
case @kind
|
80
|
+
when :moped, :mongo
|
81
|
+
self.collection(col_name).insert(doc)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# create a collection
|
86
|
+
def create_collection(col_name)
|
87
|
+
case @kind
|
88
|
+
when :moped
|
89
|
+
# NOOP
|
90
|
+
when :mongo
|
91
|
+
self.conn.db(@db_name).create_collection(col_name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# drop database
|
96
|
+
def drop_database(db_name)
|
97
|
+
case @kind
|
98
|
+
when :moped
|
99
|
+
self.conn.drop
|
100
|
+
when :mongo
|
101
|
+
self.conn.drop_database(db_name)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end # class Connection
|
105
|
+
|
106
|
+
class << self
|
107
|
+
|
108
|
+
def from_settings(settings)
|
109
|
+
if settings['mongodb'].nil? || (settings['mongodb'] == '') || settings['collection'].nil? || (settings['collection'] == '')
|
110
|
+
raise "Erroneous mongodb settings: #{settings.inspect}"
|
111
|
+
end
|
112
|
+
|
113
|
+
conn = self.connection_for_uri(settings['mongodb'])
|
114
|
+
|
115
|
+
options = settings.dup
|
116
|
+
options.delete('mongodb')
|
117
|
+
options.delete('collection')
|
118
|
+
|
119
|
+
self.new(conn, settings['collection'], options)
|
120
|
+
end
|
121
|
+
|
122
|
+
def connection_for_uri(uri)
|
123
|
+
@connections ||= { }
|
124
|
+
@connections[uri] ||= Fwissr::Source::Mongodb::Connection.new(uri)
|
125
|
+
end
|
126
|
+
|
127
|
+
end # class << self
|
128
|
+
|
129
|
+
|
130
|
+
TOP_LEVEL_COLLECTIONS = [ 'fwissr' ].freeze
|
131
|
+
|
132
|
+
attr_reader :conn, :collection_name
|
133
|
+
|
134
|
+
#
|
135
|
+
# API
|
136
|
+
#
|
137
|
+
|
138
|
+
def initialize(conn, collection_name, options = { })
|
139
|
+
super(options)
|
140
|
+
|
141
|
+
@conn = conn
|
142
|
+
@collection_name = collection_name
|
143
|
+
end
|
144
|
+
|
145
|
+
def fetch_conf
|
146
|
+
result = { }
|
147
|
+
result_part = result
|
148
|
+
|
149
|
+
unless TOP_LEVEL_COLLECTIONS.include?(@collection_name) || @options['top_level']
|
150
|
+
# merge conf at the correct place in registry
|
151
|
+
#
|
152
|
+
# eg: m_app => /my_app
|
153
|
+
# my_app.database => /my_app/database
|
154
|
+
# my_app.database.slave => /my_app/database/slave
|
155
|
+
key_ary = @collection_name.split('.')
|
156
|
+
key_ary.each do |key_part|
|
157
|
+
result_part = (result_part[key_part] ||= { })
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# build conf hash from collection's documents
|
162
|
+
conf = { }
|
163
|
+
self.conn.fetch(@collection_name).each do |doc|
|
164
|
+
key = doc['_id']
|
165
|
+
value = if doc['value'].nil?
|
166
|
+
doc.delete('_id')
|
167
|
+
doc
|
168
|
+
else
|
169
|
+
doc['value']
|
170
|
+
end
|
171
|
+
|
172
|
+
conf[key] = value
|
173
|
+
end
|
174
|
+
|
175
|
+
Fwissr.merge_conf!(result_part, conf)
|
176
|
+
|
177
|
+
result
|
178
|
+
end
|
179
|
+
|
180
|
+
end # class Fwissr::Source::Mongodb
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fwissr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Fotonauts Team
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2013-12-03 00:00:00 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yajl-ruby
|
16
|
+
prerelease: false
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- &id002
|
20
|
+
- ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
type: :runtime
|
24
|
+
version_requirements: *id001
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
prerelease: false
|
28
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- *id002
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id003
|
33
|
+
description: " A simple configuration registry tool by Fotonauts.\n"
|
34
|
+
email:
|
35
|
+
- aymerick@fotonauts.com
|
36
|
+
- oct@fotonauts.com
|
37
|
+
executables:
|
38
|
+
- fwissr
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- LICENSE
|
45
|
+
- Rakefile
|
46
|
+
- README.md
|
47
|
+
- bin/fwissr
|
48
|
+
- lib/fwissr/registry.rb
|
49
|
+
- lib/fwissr/source/file.rb
|
50
|
+
- lib/fwissr/source/mongodb.rb
|
51
|
+
- lib/fwissr/source.rb
|
52
|
+
- lib/fwissr/version.rb
|
53
|
+
- lib/fwissr.rb
|
54
|
+
homepage: https://github.com/fotonauts/fwissr
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
metadata: {}
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- *id002
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- *id002
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 2.1.11
|
74
|
+
signing_key:
|
75
|
+
specification_version: 4
|
76
|
+
summary: Fwissr
|
77
|
+
test_files: []
|
78
|
+
|