confuse 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -21,22 +21,25 @@ Or install it yourself as:
21
21
 
22
22
  ## Usage
23
23
 
24
- Basic usage:
24
+ # Basic usage
25
25
 
26
26
  config = Confuse.config do |conf|
27
- conf.add_item :foo, :description => 'Foo', :default => 1
27
+ conf.add_item :foo
28
28
  end
29
29
 
30
30
  config[:foo]
31
+
32
+ or
33
+
31
34
  config.foo
32
35
 
33
- Types:
36
+ # Sources
34
37
 
35
38
  You can choose where and your config is stored by passing a :path variable to
36
39
  Confuse.config:
37
40
 
38
41
  config = Confuse.config :path => 'config.ini' do |conf|
39
- conf.add_item :foo, :description => 'Foo', :default => 1
42
+ conf.add_item :foo
40
43
  end
41
44
 
42
45
  Confuse will attempt to work out the type of file from the extension (Supports
@@ -44,30 +47,45 @@ Confuse will attempt to work out the type of file from the extension (Supports
44
47
  passing in a :type option:
45
48
 
46
49
  config = Confuse.config :path => 'foo.conf' :type => :yaml do |conf|
47
- conf.add_item :foo, :description => 'Foo', :default => 1
50
+ conf.add_item :foo
48
51
  end
49
52
 
50
53
  If no type or path is given, it will default to environment variables.
54
+ Environment variables have an an extra option, which is the ability to provide
55
+ a prefix:
51
56
 
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
57
+ config = Confuse.config :prefix => 'FOO' do |conf|
58
+ conf.add_item :foo
58
59
  end
59
60
 
60
61
  This means Confuse will lookup environment varialbles with that prefix with an
61
62
  underscore followed by the configuration name. If none is given, it will lookup
62
63
  environment variables as is.
63
64
 
64
- Namespaces:
65
+ # Defaults
66
+
67
+ You can give a config item a default value, which will be used in cases where
68
+ none is set in the source.
69
+
70
+ config = Confuse.config do |conf|
71
+ conf.add_item :foo, :default => 1
72
+ end
73
+
74
+ This can also be a proc that takes the config as an argument. This allows you
75
+ to set a value based on what another item is set to.
76
+
77
+ config = Confuse.config do |conf|
78
+ conf.add_item :foo, :default => 'foo'
79
+ conf.add_item :bar, :default => proc { |c| "#{c.foo}_bar" }
80
+ end
81
+
82
+ # Namespaces
65
83
 
66
84
  You can separate your configuration into different namespaces:
67
85
 
68
86
  config = Confuse.config :type => :env do |conf|
69
87
  conf.add_namespace :foo do |ns|
70
- ns.add_item :foo, :description => 'Foo', :default => 1
88
+ ns.add_item :foo
71
89
  end
72
90
  end
73
91
 
@@ -81,22 +99,58 @@ Or a method call:
81
99
 
82
100
  However, beware of adding an item at the top level with the same name.
83
101
 
84
- Check:
102
+ # Types
103
+
104
+ You can specify the type of a configuration item.
105
+
106
+ config = Confuse.config do |conf|
107
+ conf.add_item :foo, :type => Fixnum
108
+ end
109
+
110
+ This will ensure that any value is converted to the correct class when asked
111
+ for, or an error raised if it is the wrong type and it can't be converted.
112
+
113
+ If a default is given, the type will be derived from the class of the default,
114
+ unless the default is a Proc.
115
+
116
+ The following types are supported:
117
+ - String
118
+ - Fixnum
119
+ - Float
120
+ - Array
121
+ - :bool (a special case, because TrueClass and FalseClass do not share a unique
122
+ common ancestor.)
123
+
124
+ Arrays can be used in sources that don't support arrays by supplying a comma
125
+ separated list.
126
+
127
+ If you wish to add support for another type, or even custom types. You can
128
+ register your own types if you wish:
129
+
130
+ Confuse::Converter.register <type> do |item|
131
+ ...
132
+ end
133
+
134
+ The type can be anything, but it can only be derived from the default if a
135
+ class is used.
136
+
137
+ # Check
85
138
 
86
139
  If you want to make sure all your configuration items are in place before
87
140
  running the program you can call .check on it.
88
141
 
89
142
  config = Confuse.config do |conf|
90
- conf.add_item :foo, :description => 'Foo', :default => 1
143
+ conf.add_item :foo
91
144
  end
92
145
 
93
146
  config.check
94
147
 
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).
148
+ If any of the items haven't been set, and don't have a default value, or if an
149
+ item is the incorrect type, an exception will be thrown. If you don't care about
150
+ certain variables, you can pass :required => false to them (or give them a
151
+ default value).
98
152
 
99
- Use a different source:
153
+ # Use a different source
100
154
 
101
155
  If you want to use a source that isn't an ini file or yaml file, or environment
102
156
  variable. It is very easy to create your own one. It simply needs to be a class
@@ -8,6 +8,7 @@ require 'confuse/item'
8
8
  require 'confuse/definition'
9
9
  require 'confuse/namespace'
10
10
  require 'confuse/source'
11
+ require 'confuse/converter'
11
12
  require 'confuse/errors'
12
13
 
13
14
  # Top level namespace for confuse gem
@@ -21,7 +21,13 @@ module Confuse
21
21
  end
22
22
 
23
23
  def lookup(namespace, key)
24
- @source[namespace, key] || @definition.default(namespace, key)
24
+ unless (item = @definition.find_item(namespace, key))
25
+ raise Errors::Undefined.new(key)
26
+ end
27
+
28
+ value = @source[namespace, key] || item.default(self)
29
+
30
+ item.convert(value)
25
31
  end
26
32
 
27
33
  # check items have a value. Will raise Undefined error if a required item
@@ -33,5 +39,12 @@ module Confuse
33
39
  end
34
40
  end
35
41
  end
42
+
43
+ def to_hash
44
+ @definition.to_hash.reduce({}) do |a, (k, v)|
45
+ value_added = v.merge(:value => self[k])
46
+ a.merge({ k => value_added })
47
+ end
48
+ end
36
49
  end
37
50
  end
@@ -0,0 +1,46 @@
1
+ # coding: utf-8
2
+
3
+ module Confuse
4
+ module Converter
5
+ class << self
6
+ def converters
7
+ @converters ||= { }
8
+ end
9
+
10
+ def register(key, &block)
11
+ converters[key] = block
12
+ end
13
+
14
+ def [](key)
15
+ converters[key]
16
+ end
17
+ end
18
+
19
+ register(String) { |item| item.to_s }
20
+
21
+ register Fixnum do |item|
22
+ raise Errors::Invalid unless /^\d*$/.match item.to_s
23
+ item.to_i
24
+ end
25
+
26
+ register Float do |item|
27
+ raise Errors::Invalid unless /^\d*(\.\d*)?$/.match item.to_s
28
+ item.to_f
29
+ end
30
+
31
+ register :bool do |item|
32
+ raise Errors::Invalid unless %w(true false).include? item.to_s
33
+ item.to_s == 'true'
34
+ end
35
+
36
+ register Array do |item|
37
+ if item.respond_to? :to_ary
38
+ item.to_ary
39
+ elsif item.include? ','
40
+ item.split(',')
41
+ else
42
+ raise Errors::Invalid
43
+ end
44
+ end
45
+ end
46
+ end
@@ -11,12 +11,8 @@ module Confuse
11
11
  !!find_item_by_name(name)
12
12
  end
13
13
 
14
- def default(namespace, key)
15
- (item = find_item(namespace, key)) && item.default
16
- end
17
-
18
14
  def add_namespace(name, &block)
19
- new_namespace = Namespace.new(&block)
15
+ new_namespace = Namespace.new(name, &block)
20
16
  namespaces[name.to_sym] = new_namespace
21
17
  end
22
18
 
@@ -26,7 +22,7 @@ module Confuse
26
22
 
27
23
  def namespaces
28
24
  @namespaces ||= {
29
- @default_namespace => Namespace.new(&(Proc.new {}))
25
+ @default_namespace => Namespace.new(nil, &(Proc.new {}))
30
26
  }
31
27
  end
32
28
 
@@ -35,16 +31,20 @@ module Confuse
35
31
  [nil, name]
36
32
  end
37
33
 
34
+ def find_item(namespace, key)
35
+ (ns = find_namespace(namespace)) && ns[key]
36
+ end
37
+
38
+ def to_hash
39
+ @namespaces.reduce({}) { |a, (_k, v)| a.merge(v) }
40
+ end
41
+
38
42
  private
39
43
 
40
44
  def find_item_by_name(name)
41
45
  namespace, key = namespace_and_key(name)
42
46
  end
43
47
 
44
- def find_item(namespace, key)
45
- (ns = find_namespace(namespace)) && ns[key]
46
- end
47
-
48
48
  def find_namespace(name)
49
49
  namespaces[name || @default_namespace]
50
50
  end
@@ -8,5 +8,7 @@ module Confuse
8
8
  super
9
9
  end
10
10
  end
11
+
12
+ class Invalid < StandardError; end
11
13
  end
12
14
  end
@@ -6,15 +6,67 @@ module Confuse
6
6
  def initialize(key, opts = {})
7
7
  @key = key
8
8
  @default, @description = opts.values_at(:default, :description)
9
+
10
+ @converter = opts[:converter]
11
+ @type = opts[:type]
12
+
9
13
  @required = opts.key?(:required) ? opts[:required] : true
10
14
  end
11
15
 
12
- attr_reader :description, :required
16
+ attr_reader :description, :required, :converter
17
+
18
+ def convert(value)
19
+ converter.call(value)
20
+ end
21
+
22
+ def default(config)
23
+ raise Errors::Undefined.new(@key) if @required && !@default
24
+
25
+ res = if @default.respond_to?(:call)
26
+ @default.call(config)
27
+ else
28
+ @default
29
+ end
30
+ end
31
+
32
+ def to_s
33
+ default = if @default && !@default.respond_to?(:call)
34
+ "default: #{@default}"
35
+ else
36
+ ''
37
+ end
38
+ "#{@key}:\t#{description} #{default}"
39
+ end
40
+
41
+ def to_hash
42
+ {
43
+ :description => @description,
44
+ :default => @default
45
+ }
46
+ end
47
+
48
+ private
49
+
50
+ def type
51
+ @type || type_from_default || String
52
+ end
53
+
54
+ def type_from_default
55
+ if @default
56
+ klass = @default.class
57
+ case @default
58
+ when TrueClass, FalseClass
59
+ :bool
60
+ when Proc
61
+ String
62
+ else
63
+ @default.class
64
+ end
65
+ end
66
+ end
13
67
 
14
- def default
15
- res = @default
16
- raise Errors::Undefined.new(@key) if @required && !res
17
- res
68
+ def converter
69
+ @converter ||= Converter[type]
18
70
  end
19
71
  end
20
72
  end
@@ -6,7 +6,8 @@ module Confuse
6
6
  class Namespace
7
7
  attr_reader :items
8
8
 
9
- def initialize(&block)
9
+ def initialize(name, &block)
10
+ @name = name
10
11
  @items = {}
11
12
  block.call(self) if block_given?
12
13
  end
@@ -18,5 +19,12 @@ module Confuse
18
19
  def [](key)
19
20
  @items[key]
20
21
  end
22
+
23
+ def to_hash
24
+ @items.reduce({}) do |a, (k,v)|
25
+ key = @name ? :"#{@name}_#{k}" : k
26
+ a.merge({ key => v.to_hash })
27
+ end
28
+ end
21
29
  end
22
30
  end
@@ -8,7 +8,7 @@ module Confuse
8
8
  end
9
9
 
10
10
  def [](namespace, key)
11
- lookup = key
11
+ lookup = key.to_s
12
12
  lookup = prepend(namespace, lookup) if namespace
13
13
  lookup = prepend(@prefix, lookup) if @prefix
14
14
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Confuse version
4
4
  module Confuse
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.0'
6
6
  end
@@ -35,11 +35,25 @@ class TestConfig < MiniTest::Unit::TestCase
35
35
 
36
36
  # raises undefined if an item hasn't been set, and has no default
37
37
  def test_get_default_from_definition
38
- assert_nil @config.buz
38
+ assert_raises(Confuse::Errors::Undefined) { @config.buz }
39
39
  end
40
40
 
41
41
  # raises an error if any items are required and don't have defaults
42
42
  def test_check
43
43
  assert_raises(Confuse::Errors::Undefined) { @config.check }
44
44
  end
45
+
46
+ def test_to_hash
47
+ config = Confuse.config do |conf|
48
+ conf.add_item :foo, :description => 'foo', :default => 1
49
+ conf.add_namespace :bar do |ns|
50
+ ns.add_item :baz, :description => 'bar_baz', :default => 2
51
+ end
52
+ end
53
+
54
+ assert_equal(
55
+ { :foo => { :description => 'foo', :default => 1, :value => 1 },
56
+ :bar_baz => { :description => 'bar_baz', :default => 2, :value => 2 } },
57
+ config.to_hash)
58
+ end
45
59
  end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+
3
+ class TestConverter < MiniTest::Unit::TestCase
4
+ def test_string
5
+ converter = Confuse::Converter[String]
6
+
7
+ assert_equal '1', converter.call(1)
8
+ assert_equal '1.0', converter.call(1.0)
9
+ assert_equal 'true', converter.call(true)
10
+ assert_equal 'false', converter.call(false)
11
+ assert_equal 'foo', converter.call('foo')
12
+ end
13
+
14
+ def test_fixnum
15
+ converter = Confuse::Converter[Fixnum]
16
+
17
+ assert_equal 1, converter.call(1)
18
+ assert_equal 1, converter.call('1')
19
+ assert_raises(Confuse::Errors::Invalid) { converter.call('1.0') }
20
+ assert_raises(Confuse::Errors::Invalid) { converter.call('1.0.0') }
21
+ assert_raises(Confuse::Errors::Invalid) { converter.call('foo') }
22
+ end
23
+
24
+ def test_float
25
+ converter = Confuse::Converter[Float]
26
+
27
+ assert_equal 1.0, converter.call(1.0)
28
+ assert_equal 1.0, converter.call(1)
29
+ assert_equal 1.0, converter.call('1')
30
+ assert_equal 1.0, converter.call('1.0')
31
+ assert_raises(Confuse::Errors::Invalid) { converter.call('1.0.0') }
32
+ assert_raises(Confuse::Errors::Invalid) { converter.call('foo') }
33
+ end
34
+
35
+ def test_bool
36
+ converter = Confuse::Converter[:bool]
37
+
38
+ assert_equal true, converter.call(true)
39
+ assert_equal false, converter.call(false)
40
+ assert_equal true, converter.call('true')
41
+ assert_equal false, converter.call('false')
42
+ assert_raises(Confuse::Errors::Invalid) { converter.call('foo') }
43
+ end
44
+
45
+ def test_array
46
+ converter = Confuse::Converter[Array]
47
+
48
+ assert_equal %w(foo bar), converter.call(%w(foo bar))
49
+ assert_equal %w(foo bar), converter.call('foo,bar')
50
+ assert_raises(Confuse::Errors::Invalid) { converter.call('foo') }
51
+ end
52
+ end
@@ -11,6 +11,14 @@ module Confuse
11
11
  end
12
12
  end
13
13
 
14
+ def source
15
+ @source ||= Confuse.source
16
+ end
17
+
18
+ def config
19
+ @config ||= Confuse::Config.new(@definition, source)
20
+ end
21
+
14
22
  # can define a configuration item
15
23
  def test_define_item
16
24
  assert @definition.defines? :foo
@@ -21,12 +29,11 @@ module Confuse
21
29
  assert @definition.defines? :bar_foo
22
30
  end
23
31
 
24
- def test_default
25
- assert_equal 1, @definition.default(nil, :foo)
26
- end
27
-
28
- def test_default_for_namespaced_item
29
- assert_equal 1, @definition.default(:bar, :foo)
32
+ def test_to_hash
33
+ assert_equal(
34
+ { :foo => { :description => nil, :default => 1 },
35
+ :bar_foo => { :description => 'test', :default => 1 } },
36
+ @definition.to_hash)
30
37
  end
31
38
  end
32
39
  end
@@ -2,11 +2,12 @@
2
2
 
3
3
  class TestItem < MiniTest::Unit::TestCase
4
4
  def setup
5
- @item = Confuse::Item.new(:foo, :default => 1, :description => 'Description')
5
+ @item = Confuse::Item.new(:foo, :default => 1,
6
+ :description => 'Description')
6
7
  end
7
8
 
8
9
  def test_sets_the_default_value
9
- assert_equal 1, @item.default
10
+ assert_equal 1, @item.default(nil)
10
11
  end
11
12
 
12
13
  def test_sets_the_description
@@ -16,6 +17,23 @@ class TestItem < MiniTest::Unit::TestCase
16
17
  def test_required_item
17
18
  item = Confuse::Item.new(:foo, :description => 'required!',
18
19
  :required => true)
19
- assert_raises(Confuse::Errors::Undefined) { item.default }
20
+ assert_raises(Confuse::Errors::Undefined) { item.default(nil) }
21
+ end
22
+
23
+ def test_proc_as_default
24
+ mock_conf = Class.new do
25
+ def bar
26
+ 'bar'
27
+ end
28
+ end.new
29
+
30
+ item = Confuse::Item.new(:foo, :default => proc { |c| c.bar })
31
+
32
+ assert_equal 'bar', item.default(mock_conf)
33
+ end
34
+
35
+ def test_to_hash
36
+ assert_equal({ :description => 'Description', :default => 1 },
37
+ @item.to_hash)
20
38
  end
21
39
  end
@@ -6,11 +6,35 @@ require 'confuse'
6
6
  # Test {Confuse::Namespace}
7
7
  class TestNamespace < MiniTest::Unit::TestCase
8
8
  def setup
9
- @namespace = Confuse::Namespace.new
9
+ @namespace = Confuse::Namespace.new(nil)
10
10
  end
11
11
 
12
12
  def test_add_an_item
13
13
  @namespace.add_item(:foo, :default => :foo, :description => 'Test')
14
14
  refute_nil @namespace[:foo]
15
15
  end
16
+
17
+ def test_to_hash
18
+ namespace = Confuse::Namespace.new(nil) do |ns|
19
+ ns.add_item :foo, :description => 'how many foos?', :default => 1
20
+ ns.add_item :bar, :description => 'how many bars?', :default => 2
21
+ end
22
+
23
+ assert_equal(
24
+ { :foo => { :description => 'how many foos?', :default => 1 },
25
+ :bar => { :description => 'how many bars?', :default => 2 } },
26
+ namespace.to_hash)
27
+ end
28
+
29
+ def test_to_hash_named_namespace
30
+ namespace = Confuse::Namespace.new(:foo) do |ns|
31
+ ns.add_item :foo, :description => 'how many foos?', :default => 1
32
+ ns.add_item :bar, :description => 'how many bars?', :default => 2
33
+ end
34
+
35
+ assert_equal(
36
+ { :foo_foo => { :description => 'how many foos?', :default => 1 },
37
+ :foo_bar => { :description => 'how many bars?', :default => 2 } },
38
+ namespace.to_hash)
39
+ end
16
40
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: confuse
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 1.0.0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tom Chipchase
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2014-09-15 00:00:00 Z
18
+ date: 2014-09-16 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: inifile
@@ -98,6 +98,7 @@ files:
98
98
  - example/yaml_example.rb
99
99
  - lib/confuse.rb
100
100
  - lib/confuse/config.rb
101
+ - lib/confuse/converter.rb
101
102
  - lib/confuse/definition.rb
102
103
  - lib/confuse/errors.rb
103
104
  - lib/confuse/item.rb
@@ -110,6 +111,7 @@ files:
110
111
  - lib/confuse/version.rb
111
112
  - test/test_config.rb
112
113
  - test/test_confuse.rb
114
+ - test/test_converter.rb
113
115
  - test/test_definition.rb
114
116
  - test/test_item.rb
115
117
  - test/test_key_splitter.rb
@@ -150,6 +152,7 @@ summary: A DSL for defining configuration options in classes
150
152
  test_files:
151
153
  - test/test_config.rb
152
154
  - test/test_confuse.rb
155
+ - test/test_converter.rb
153
156
  - test/test_definition.rb
154
157
  - test/test_item.rb
155
158
  - test/test_key_splitter.rb