confuse 0.1.8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +112 -4
- data/Rakefile +7 -0
- data/confuse.gemspec +1 -0
- data/example/env_example.rb +10 -0
- data/example/example.ini +1 -0
- data/example/example.yaml +1 -0
- data/example/ini_example.rb +10 -0
- data/example/yaml_example.rb +11 -0
- data/lib/confuse.rb +23 -4
- data/lib/confuse/config.rb +22 -33
- data/lib/confuse/definition.rb +52 -0
- data/lib/confuse/errors.rb +12 -0
- data/lib/confuse/item.rb +20 -0
- data/lib/confuse/key_splitter.rb +40 -0
- data/lib/confuse/namespace.rb +6 -59
- data/lib/confuse/source.rb +34 -0
- data/lib/confuse/source/env.rb +28 -0
- data/lib/confuse/source/ini.rb +26 -0
- data/lib/confuse/source/yaml.rb +20 -0
- data/lib/confuse/version.rb +4 -1
- data/test/test_config.rb +27 -44
- data/test/test_confuse.rb +13 -0
- data/test/test_definition.rb +32 -0
- data/test/test_item.rb +21 -0
- data/test/test_key_splitter.rb +53 -0
- data/test/test_namespace.rb +7 -16
- metadata +40 -17
- data/example/001_simple_example.ini +0 -3
- data/example/001_simple_example.rb +0 -18
- data/example/002_namespace_example.rb +0 -29
- data/example/003_config_subclass_instance.ini +0 -2
- data/example/003_config_subclass_instance_example.rb +0 -9
- data/example/004_configured_by.rb +0 -26
- data/lib/confuse/config_item.rb +0 -38
- data/lib/confuse/config_mixin.rb +0 -135
- data/lib/confuse/configurable.rb +0 -15
- data/lib/confuse/dsl.rb +0 -68
- data/test/test_config_base.rb +0 -37
data/README.md
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
#
|
1
|
+
# Confuse
|
2
2
|
|
3
|
-
|
3
|
+
This gem is used to simplify specifying and reading configuration.
|
4
|
+
|
5
|
+
You can define what you expect to be configured, and this can be read from ini
|
6
|
+
files, yaml files or environment variables.
|
4
7
|
|
5
8
|
## Installation
|
6
9
|
|
7
10
|
Add this line to your application's Gemfile:
|
8
11
|
|
9
|
-
gem '
|
12
|
+
gem 'confuse'
|
10
13
|
|
11
14
|
And then execute:
|
12
15
|
|
@@ -18,7 +21,112 @@ Or install it yourself as:
|
|
18
21
|
|
19
22
|
## Usage
|
20
23
|
|
21
|
-
|
24
|
+
Basic usage:
|
25
|
+
|
26
|
+
config = Confuse.config do |conf|
|
27
|
+
conf.add_item :foo, :description => 'Foo', :default => 1
|
28
|
+
end
|
29
|
+
|
30
|
+
config[:foo]
|
31
|
+
config.foo
|
32
|
+
|
33
|
+
Types:
|
34
|
+
|
35
|
+
You can choose where and your config is stored by passing a :path variable to
|
36
|
+
Confuse.config:
|
37
|
+
|
38
|
+
config = Confuse.config :path => 'config.ini' do |conf|
|
39
|
+
conf.add_item :foo, :description => 'Foo', :default => 1
|
40
|
+
end
|
41
|
+
|
42
|
+
Confuse will attempt to work out the type of file from the extension (Supports
|
43
|
+
.ini for ini files, and .yml or .yaml for yaml files). You can override this by
|
44
|
+
passing in a :type option:
|
45
|
+
|
46
|
+
config = Confuse.config :path => 'foo.conf' :type => :yaml do |conf|
|
47
|
+
conf.add_item :foo, :description => 'Foo', :default => 1
|
48
|
+
end
|
49
|
+
|
50
|
+
If no type or path is given, it will default to environment variables.
|
51
|
+
|
52
|
+
Environment variables have one extra feature, which is the ability to provide a
|
53
|
+
prefix:
|
54
|
+
|
55
|
+
|
56
|
+
config = Confuse.config :prefix => 'FOO' :type => :yaml do |conf|
|
57
|
+
conf.add_item :foo, :description => 'Foo', :default => 1
|
58
|
+
end
|
59
|
+
|
60
|
+
This means Confuse will lookup environment varialbles with that prefix with an
|
61
|
+
underscore followed by the configuration name. If none is given, it will lookup
|
62
|
+
environment variables as is.
|
63
|
+
|
64
|
+
Namespaces:
|
65
|
+
|
66
|
+
You can separate your configuration into different namespaces:
|
67
|
+
|
68
|
+
config = Confuse.config :type => :env do |conf|
|
69
|
+
conf.add_namespace :foo do |ns|
|
70
|
+
ns.add_item :foo, :description => 'Foo', :default => 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
For simplicity, you can fetch these items using a single []:
|
75
|
+
|
76
|
+
config[:foo_bar]
|
77
|
+
|
78
|
+
Or a method call:
|
79
|
+
|
80
|
+
config.foo_bar
|
81
|
+
|
82
|
+
However, beware of adding an item at the top level with the same name.
|
83
|
+
|
84
|
+
Check:
|
85
|
+
|
86
|
+
If you want to make sure all your configuration items are in place before
|
87
|
+
running the program you can call .check on it.
|
88
|
+
|
89
|
+
config = Confuse.config do |conf|
|
90
|
+
conf.add_item :foo, :description => 'Foo', :default => 1
|
91
|
+
end
|
92
|
+
|
93
|
+
config.check
|
94
|
+
|
95
|
+
If any of the items haven't been set, and don't have a default value, an
|
96
|
+
exception will be thrown. If you don't care about certain variables, you can
|
97
|
+
pass :required => false to them (or give them a default value).
|
98
|
+
|
99
|
+
Use a different source:
|
100
|
+
|
101
|
+
If you want to use a source that isn't an ini file or yaml file, or environment
|
102
|
+
variable. It is very easy to create your own one. It simply needs to be a class
|
103
|
+
that has an initialize that takes a hash, and a [] method that takes a
|
104
|
+
namespace and a key argument.
|
105
|
+
|
106
|
+
class MyOwnSource
|
107
|
+
def initialize(options = {})
|
108
|
+
...
|
109
|
+
end
|
110
|
+
|
111
|
+
def [](namespace, key)
|
112
|
+
...
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
Confuse::Source.register(:foo, MyOwnSource)
|
117
|
+
|
118
|
+
definition = Confuse.config, :type => :foo do |conf|
|
119
|
+
conf.add_item :foo
|
120
|
+
end
|
121
|
+
|
122
|
+
config = Confuse::Config.new(definition, my_own_source)
|
123
|
+
|
124
|
+
If your class takes a :path variable, it will autodetect the extension for
|
125
|
+
whatever you register. You can register more than one extension if you wish.
|
126
|
+
|
127
|
+
You could use this to store data in whatever fancy file format you like, or
|
128
|
+
perhaps even a database table, or one of those fancy NoSQL datastores, you
|
129
|
+
could even write your config in Ruby code.
|
22
130
|
|
23
131
|
## Contributing
|
24
132
|
|
data/Rakefile
CHANGED
data/confuse.gemspec
CHANGED
data/example/example.ini
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
foo = 10
|
@@ -0,0 +1 @@
|
|
1
|
+
:foo: 10
|
data/lib/confuse.rb
CHANGED
@@ -1,11 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
1
3
|
require 'confuse/version'
|
2
4
|
|
5
|
+
require 'confuse/key_splitter'
|
3
6
|
require 'confuse/config'
|
4
|
-
require 'confuse/
|
5
|
-
require 'confuse/
|
6
|
-
require 'confuse/dsl'
|
7
|
+
require 'confuse/item'
|
8
|
+
require 'confuse/definition'
|
7
9
|
require 'confuse/namespace'
|
10
|
+
require 'confuse/source'
|
11
|
+
require 'confuse/errors'
|
8
12
|
|
13
|
+
# Top level namespace for confuse gem
|
9
14
|
module Confuse
|
10
|
-
|
15
|
+
class << self
|
16
|
+
def define(&block)
|
17
|
+
Definition.new(&block)
|
18
|
+
end
|
11
19
|
|
20
|
+
def source(options = {})
|
21
|
+
Source.create(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def config(options = {}, &block)
|
25
|
+
definition = Definition.new(&block)
|
26
|
+
source = Source.create(options)
|
27
|
+
Config.new(definition, source)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/confuse/config.rb
CHANGED
@@ -1,48 +1,37 @@
|
|
1
|
-
|
2
|
-
require 'confuse/dsl'
|
1
|
+
# coding: utf-8
|
3
2
|
|
4
3
|
module Confuse
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
class Config
|
5
|
+
def initialize(definition, source)
|
6
|
+
@definition = definition
|
7
|
+
@source = source
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
extend DSL
|
10
|
+
def respond_to?(name)
|
11
|
+
@definition.defines?(name) || super
|
12
|
+
end
|
14
13
|
|
15
|
-
def
|
16
|
-
|
14
|
+
def method_missing(name, *_args)
|
15
|
+
self[name] if respond_to?(name)
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
if paths.flatten.empty?
|
23
|
-
read_files(self.class.config_path.flatten)
|
24
|
-
else
|
25
|
-
read_files(paths.flatten)
|
26
|
-
end
|
27
|
-
options[:conf].tap { |conf| conf && mixin_config!(conf) }
|
18
|
+
def [](name)
|
19
|
+
namespace, key = @definition.namespace_and_key(name)
|
20
|
+
lookup(namespace, key)
|
28
21
|
end
|
29
22
|
|
30
|
-
def
|
31
|
-
|
23
|
+
def lookup(namespace, key)
|
24
|
+
@source[namespace, key] || @definition.default(namespace, key)
|
32
25
|
end
|
33
26
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
:doc => item.description,
|
41
|
-
:default => item.default_value }
|
27
|
+
# check items have a value. Will raise Undefined error if a required item
|
28
|
+
# has no value.
|
29
|
+
def check
|
30
|
+
@definition.namespaces.each do |(namespace, ns)|
|
31
|
+
ns.items.each do |key, _|
|
32
|
+
lookup(namespace, key)
|
42
33
|
end
|
43
|
-
memo
|
44
34
|
end
|
45
35
|
end
|
46
|
-
|
47
36
|
end
|
48
37
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Confuse
|
4
|
+
class Definition
|
5
|
+
def initialize(&block)
|
6
|
+
@default_namespace = :default
|
7
|
+
block.call(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
def defines?(name)
|
11
|
+
!!find_item_by_name(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def default(namespace, key)
|
15
|
+
(item = find_item(namespace, key)) && item.default
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_namespace(name, &block)
|
19
|
+
new_namespace = Namespace.new(&block)
|
20
|
+
namespaces[name.to_sym] = new_namespace
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_item(name, opts = {})
|
24
|
+
namespaces[@default_namespace].add_item(name, opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
def namespaces
|
28
|
+
@namespaces ||= {
|
29
|
+
@default_namespace => Namespace.new(&(Proc.new {}))
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def namespace_and_key(name)
|
34
|
+
KeySplitter.new(name).split.find { |(ns, _)| namespaces[ns] } ||
|
35
|
+
[nil, name]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def find_item_by_name(name)
|
41
|
+
namespace, key = namespace_and_key(name)
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_item(namespace, key)
|
45
|
+
(ns = find_namespace(namespace)) && ns[key]
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_namespace(name)
|
49
|
+
namespaces[name || @default_namespace]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/confuse/item.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Confuse
|
4
|
+
# An {Item} is a class for storing a description piece of config.
|
5
|
+
class Item
|
6
|
+
def initialize(key, opts = {})
|
7
|
+
@key = key
|
8
|
+
@default, @description = opts.values_at(:default, :description)
|
9
|
+
@required = opts.key?(:required) ? opts[:required] : true
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :description, :required
|
13
|
+
|
14
|
+
def default
|
15
|
+
res = @default
|
16
|
+
raise Errors::Undefined.new(@key) if @required && !res
|
17
|
+
res
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Confuse
|
4
|
+
# This class encapsulates the code required to support searching for items
|
5
|
+
# with a single key item even for nested items.
|
6
|
+
# config[:foo_bar] instead of config[:foo][:bar]
|
7
|
+
class KeySplitter
|
8
|
+
def initialize(key)
|
9
|
+
@key = key
|
10
|
+
end
|
11
|
+
|
12
|
+
def split
|
13
|
+
possible_namespaces.map do |ns|
|
14
|
+
[ns, rest_of_key(ns)]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns an array of possible namespaces based on splitting the key at
|
19
|
+
# every underscore.
|
20
|
+
def possible_namespaces
|
21
|
+
namespaces = []
|
22
|
+
key = @key.to_s
|
23
|
+
while (index = key.rindex('_'))
|
24
|
+
key = key[0, index]
|
25
|
+
namespaces << key.to_sym
|
26
|
+
end
|
27
|
+
namespaces
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the rest of the key for a given namespace
|
31
|
+
def rest_of_key(namespace)
|
32
|
+
return nil if @key == namespace
|
33
|
+
|
34
|
+
key = @key.to_s
|
35
|
+
|
36
|
+
index = key.index(namespace.to_s) && (namespace.to_s.length + 1)
|
37
|
+
key[index, key.length].to_sym if index
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/confuse/namespace.rb
CHANGED
@@ -1,75 +1,22 @@
|
|
1
|
-
|
1
|
+
# coding: utf-8
|
2
2
|
|
3
3
|
module Confuse
|
4
4
|
# A {Namespace} is a container to keep configuration data seperate from the
|
5
5
|
# rest of the config.
|
6
6
|
class Namespace
|
7
|
-
|
8
|
-
attr_reader :items, :supress_warnings_flag, :strict_flag
|
7
|
+
attr_reader :items
|
9
8
|
|
10
9
|
def initialize(&block)
|
11
10
|
@items = {}
|
12
|
-
|
13
|
-
@strict_flag = false
|
14
|
-
instance_eval(&block)
|
15
|
-
end
|
16
|
-
|
17
|
-
def define(name, &block)
|
18
|
-
@items[name] = ConfigItem.new(name, &block)
|
19
|
-
end
|
20
|
-
|
21
|
-
def supress_warnings
|
22
|
-
@supress_warnings_flag = true
|
23
|
-
end
|
24
|
-
|
25
|
-
def strict
|
26
|
-
@strict_flag = true
|
27
|
-
end
|
28
|
-
|
29
|
-
def [](key, config = nil)
|
30
|
-
value = (i = get_item(key)) && i.value
|
31
|
-
if value.respond_to?(:call) && !config.nil?
|
32
|
-
value.call(config)
|
33
|
-
else
|
34
|
-
value
|
35
|
-
end
|
11
|
+
block.call(self) if block_given?
|
36
12
|
end
|
37
13
|
|
38
|
-
def
|
39
|
-
|
40
|
-
item && item.value = value
|
14
|
+
def add_item(name, opts = {})
|
15
|
+
@items[name] = Item.new(name, opts)
|
41
16
|
end
|
42
17
|
|
43
|
-
def
|
44
|
-
@items.keys
|
45
|
-
end
|
46
|
-
|
47
|
-
def create_new_key(key, value)
|
48
|
-
if @supress_warnings_flag
|
49
|
-
puts "Warning: config includes unknown option '#{key}'"
|
50
|
-
end
|
51
|
-
@items[key] = ConfigItem.new(key, &nil) unless @strict_flag
|
52
|
-
end
|
53
|
-
|
54
|
-
def get_item(key)
|
18
|
+
def [](key)
|
55
19
|
@items[key]
|
56
20
|
end
|
57
|
-
|
58
|
-
def merge!(namespace)
|
59
|
-
@strict_flag = namespace.strict
|
60
|
-
@supress_warnings_flag = namespace.supress_warnings
|
61
|
-
@items.merge! namespace.clone.items
|
62
|
-
end
|
63
|
-
|
64
|
-
def clone
|
65
|
-
c = super
|
66
|
-
items = @items.reduce({}) do |m, (k, v)|
|
67
|
-
m[k] = v.clone
|
68
|
-
m
|
69
|
-
end
|
70
|
-
c.instance_variable_set(:"@items", items)
|
71
|
-
c
|
72
|
-
end
|
73
|
-
|
74
21
|
end
|
75
22
|
end
|