fwissr 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|