confuse 0.1.8 → 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.
- 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
|