configru 0.5.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -3
- data/.rspec +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +4 -4
- data/Gemfile.lock +32 -0
- data/README.md +6 -182
- data/Rakefile +6 -10
- data/configru.gemspec +1 -1
- data/lib/configru/dsl.rb +24 -84
- data/lib/configru/option.rb +3 -0
- data/lib/configru/structhash.rb +7 -0
- data/lib/configru/version.rb +1 -1
- data/lib/configru.rb +82 -60
- data/spec/configru_spec.rb +116 -0
- data/spec/dsl_spec.rb +111 -0
- data/spec/example_files/example_a.yml +1 -0
- data/spec/example_files/example_b.yml +1 -0
- data/spec/example_files/example_c.yml +2 -0
- data/spec/example_files/example_d.yml +2 -0
- data/spec/example_files/example_e.yml +0 -0
- data/spec/example_files/example_f.yml +2 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/structhash_spec.rb +18 -0
- metadata +35 -13
- data/lib/configru/confighash.rb +0 -33
- data/test/confighash_test.rb +0 -63
- data/test/hashdsl_test.rb +0 -83
- data/test/loaddsl_test.rb +0 -24
- data/test/teststrap.rb +0 -11
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --require spec_helper
|
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
source
|
1
|
+
source :rubygems
|
2
2
|
|
3
3
|
group :development do
|
4
|
-
gem
|
5
|
-
gem
|
4
|
+
gem 'rake', '>= 0'
|
5
|
+
gem 'rspec', '~> 2.8.0'
|
6
|
+
gem 'simplecov', '~> 0.6.0'
|
6
7
|
end
|
7
8
|
|
8
|
-
# Specify your gem's dependencies in configru.gemspec
|
9
9
|
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
configru (2.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
multi_json (1.3.6)
|
11
|
+
rake (0.9.2.2)
|
12
|
+
rspec (2.8.0)
|
13
|
+
rspec-core (~> 2.8.0)
|
14
|
+
rspec-expectations (~> 2.8.0)
|
15
|
+
rspec-mocks (~> 2.8.0)
|
16
|
+
rspec-core (2.8.0)
|
17
|
+
rspec-expectations (2.8.0)
|
18
|
+
diff-lcs (~> 1.1.2)
|
19
|
+
rspec-mocks (2.8.0)
|
20
|
+
simplecov (0.6.4)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
simplecov-html (~> 0.5.3)
|
23
|
+
simplecov-html (0.5.3)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
configru!
|
30
|
+
rake
|
31
|
+
rspec (~> 2.8.0)
|
32
|
+
simplecov (~> 0.6.0)
|
data/README.md
CHANGED
@@ -1,195 +1,19 @@
|
|
1
1
|
# Configru
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://secure.travis-ci.org/programble/configru.png?branch=master)](http://travis-ci.org/programble/configru)
|
4
4
|
|
5
|
-
|
5
|
+
YAML configuration file loader
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
# Usage
|
10
|
-
|
11
|
-
Configru loads YAML configuration files and provides a simple way to access
|
12
|
-
configuration options.
|
13
|
-
|
14
|
-
## Loading Configuration Files
|
15
|
-
|
16
|
-
Configru provides a DSL for loading configuration files.
|
17
|
-
|
18
|
-
```ruby
|
19
|
-
require 'configru'
|
20
|
-
|
21
|
-
Configru.load do
|
22
|
-
# Things
|
23
|
-
end
|
24
|
-
```
|
25
|
-
|
26
|
-
At the very least, the block passed to `Configru.load` must tell Configru which
|
27
|
-
files it should load. There are two different methods of loading configuration
|
28
|
-
files available.
|
29
|
-
|
30
|
-
### Just load a file already!
|
31
|
-
|
32
|
-
This is the simplest method of loading. It just loads a file.
|
33
|
-
|
34
|
-
```ruby
|
35
|
-
Configru.load do
|
36
|
-
just 'foo.yml'
|
37
|
-
end
|
38
|
-
```
|
39
|
-
|
40
|
-
### First-of Loading
|
41
|
-
|
42
|
-
This method loads the first file that exists, ignoring all other files.
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
Configru.load do
|
46
|
-
first_of 'foo.yml', '~/foo.yml', '/etc/foo.yml'
|
47
|
-
end
|
48
|
-
```
|
49
|
-
|
50
|
-
### Cascading Loading
|
51
|
-
|
52
|
-
This method loads every file that exists in reverse order. Files listed first
|
53
|
-
overwrite the values from files listed later. (Files are listed in high to low
|
54
|
-
cascade priority)
|
55
|
-
|
56
|
-
```ruby
|
57
|
-
Configru.load do
|
58
|
-
cascade '~/foo.yml', '/etc/foo.yml'
|
59
|
-
end
|
60
|
-
```
|
61
|
-
|
62
|
-
## Accessing Options
|
63
|
-
|
64
|
-
Configuration options can be accessed as methods of the `Configru` module, or
|
65
|
-
`Configru` can be used as a Hash.
|
66
|
-
|
67
|
-
`foo.yml`
|
68
|
-
|
69
|
-
```yaml
|
70
|
-
nick: bob
|
71
|
-
server:
|
72
|
-
address: foo.net
|
73
|
-
port: 6782
|
74
|
-
```
|
75
|
-
|
76
|
-
`foo.rb`
|
77
|
-
|
78
|
-
```ruby
|
79
|
-
require 'configru'
|
80
|
-
require 'socket'
|
81
|
-
|
82
|
-
Configru.load do
|
83
|
-
just 'foo.yml'
|
84
|
-
end
|
85
|
-
|
86
|
-
s = TCPSocket.new(Configru.server.address, Configru['server']['port'])
|
87
|
-
s.puts "Hello, I am #{Configru.nick}"
|
88
|
-
```
|
89
|
-
|
90
|
-
Configuration options with hyphens can be accessed as methods by replacing the
|
91
|
-
hyphens with underscores.
|
92
|
-
|
93
|
-
## Defaults
|
94
|
-
|
95
|
-
Configru's load DSL allows for setting configuration defaults using a block.
|
96
|
-
If no configuration files are found or if the configuration file omits an
|
97
|
-
option, the values in `defaults` will be used.
|
98
|
-
|
99
|
-
|
100
|
-
```ruby
|
101
|
-
require 'configru'
|
102
|
-
|
103
|
-
Configru.load do
|
104
|
-
just 'foo.yml'
|
105
|
-
defaults do
|
106
|
-
nick 'Dr. Nader'
|
107
|
-
server do
|
108
|
-
address 'abcd.com'
|
109
|
-
port 1111
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
```
|
114
|
-
|
115
|
-
The above `defaults` block is equivalent to the following YAML:
|
116
|
-
|
117
|
-
```yaml
|
118
|
-
nick: Dr. Nader
|
119
|
-
server:
|
120
|
-
address: abcd.com
|
121
|
-
port: 1111
|
122
|
-
```
|
123
|
-
|
124
|
-
Defaults can also be set using a Hash instead of a block.
|
125
|
-
|
126
|
-
```ruby
|
127
|
-
Configru.load do
|
128
|
-
just 'foo.yml'
|
129
|
-
defaults 'nick' => 'Dr. Nader',
|
130
|
-
'server' => {'address' => 'abcd.com', 'port' => 1111}
|
131
|
-
end
|
132
|
-
```
|
133
|
-
|
134
|
-
Defaults can also be loaded from a YAML file by passing the filename to
|
135
|
-
`defaults`.
|
136
|
-
|
137
|
-
```ruby
|
138
|
-
Configru.load do
|
139
|
-
just 'foo.yml'
|
140
|
-
defaults 'foo.yml.dist'
|
141
|
-
end
|
142
|
-
```
|
143
|
-
|
144
|
-
## Verifying options
|
145
|
-
|
146
|
-
Configru provides a way to verify that configuration options meet certain
|
147
|
-
requirements. This is done using a `verify` block in `Configru.load`.
|
7
|
+
## Usage
|
148
8
|
|
149
9
|
```ruby
|
150
|
-
|
151
|
-
|
152
|
-
verify do
|
153
|
-
foo Fixnum
|
154
|
-
bar /^a+$/
|
155
|
-
baz ['one', 'two']
|
156
|
-
end
|
157
|
-
end
|
10
|
+
# Gemfile
|
11
|
+
gem 'configru', '~> 2.0.0'
|
158
12
|
```
|
159
13
|
|
160
|
-
Upon loading the configuration, Configru checks each option against this verify
|
161
|
-
block. In most cases, the `===` operator is used to compare the values, but
|
162
|
-
there are some special cases. If the verification value is a class, `is_a?` is
|
163
|
-
used. If the verification value is an array, `include?` is used. This
|
164
|
-
effectively means that with a class, the value must be an instance of that
|
165
|
-
class, and with an array, the value must be one of the values in the array.
|
166
|
-
|
167
|
-
FIXME: Talk about how Configru deals with invalid options
|
168
|
-
|
169
|
-
## Doing two things at once
|
170
|
-
|
171
|
-
Configru also has an `options` block in `Configru.load` which allows for
|
172
|
-
combining the `defaults` and `verify` blocks.
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
Configru.load do
|
176
|
-
just 'foo.yml'
|
177
|
-
options do
|
178
|
-
nick String, 'Dr. Nader'
|
179
|
-
server do
|
180
|
-
address String, 'abcd.com'
|
181
|
-
port (0..65535), 1111
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
```
|
186
|
-
|
187
|
-
In the `options` block, each option takes two arguments, the first being the
|
188
|
-
verification value, and the second being the default value.
|
189
|
-
|
190
14
|
# License
|
191
15
|
|
192
|
-
Copyright (c) 2011, Curtis McEnroe <programble@gmail.com>
|
16
|
+
Copyright (c) 2011-2012, Curtis McEnroe <programble@gmail.com>
|
193
17
|
|
194
18
|
Permission to use, copy, modify, and/or distribute this software for any
|
195
19
|
purpose with or without fee is hereby granted, provided that the above
|
data/Rakefile
CHANGED
@@ -1,15 +1,11 @@
|
|
1
|
+
require 'bundler/setup'
|
1
2
|
require 'bundler/gem_tasks'
|
2
3
|
|
3
|
-
require '
|
4
|
-
require '
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
Rake::TestTask.new do |t|
|
9
|
-
t.libs << "test"
|
10
|
-
t.pattern = "test/**/*_test.rb"
|
11
|
-
t.verbose = false
|
12
|
-
end
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
8
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
13
9
|
end
|
14
10
|
|
15
|
-
task :default => :
|
11
|
+
task :default => :spec
|
data/configru.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Curtis McEnroe"]
|
9
9
|
s.email = ["programble@gmail.com"]
|
10
10
|
s.homepage = "https://github.com/programble/configru"
|
11
|
-
s.summary = %q{
|
11
|
+
s.summary = %q{YAML configuration file loader}
|
12
12
|
s.description = s.summary
|
13
13
|
|
14
14
|
s.files = `git ls-files`.split("\n")
|
data/lib/configru/dsl.rb
CHANGED
@@ -1,106 +1,46 @@
|
|
1
|
+
require 'configru/option'
|
2
|
+
|
1
3
|
module Configru
|
2
4
|
module DSL
|
3
|
-
class
|
4
|
-
attr_reader :
|
5
|
+
class OptionGroup
|
6
|
+
attr_reader :options
|
5
7
|
|
6
|
-
def initialize(block)
|
7
|
-
@
|
8
|
-
@verify_hash = {}
|
9
|
-
@files_array = []
|
10
|
-
@load_method = :first
|
8
|
+
def initialize(&block)
|
9
|
+
@options = {}
|
11
10
|
instance_eval(&block)
|
12
11
|
end
|
13
12
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@files_array = args
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def method(m)
|
23
|
-
@load_method = m
|
24
|
-
end
|
25
|
-
|
26
|
-
def first_of(*args)
|
27
|
-
method(:first)
|
28
|
-
files(*args)
|
29
|
-
end
|
30
|
-
|
31
|
-
alias :just :first_of
|
32
|
-
|
33
|
-
def cascade(*args)
|
34
|
-
method(:cascade)
|
35
|
-
files(*args)
|
36
|
-
end
|
37
|
-
|
38
|
-
def defaults(arg=nil, &block)
|
39
|
-
if arg.is_a? String
|
40
|
-
@defaults_hash = YAML.load_file(arg)
|
41
|
-
elsif arg
|
42
|
-
@defaults_hash = arg
|
43
|
-
elsif block
|
44
|
-
@defaults_hash = HashDSL.new(block).hash
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def verify(hash=nil, &block)
|
49
|
-
if hash
|
50
|
-
@verify_hash = hash
|
51
|
-
elsif block
|
52
|
-
@verify_hash = HashDSL.new(block).hash
|
53
|
-
end
|
13
|
+
def option(name, type = Object, default = nil, validate = nil, &block)
|
14
|
+
option = Configru::Option.new(type, default, validate, nil)
|
15
|
+
Option.new(option, &block) if block
|
16
|
+
@options[name.to_s] = option
|
54
17
|
end
|
55
18
|
|
56
|
-
def
|
57
|
-
|
58
|
-
@defaults_hash = hashes.hash2
|
59
|
-
@verify_hash = hashes.hash1
|
19
|
+
def option_group(name, &block)
|
20
|
+
@options[name.to_s] = OptionGroup.new(&block).options
|
60
21
|
end
|
61
22
|
end
|
62
23
|
|
63
|
-
class
|
64
|
-
|
65
|
-
|
66
|
-
def initialize(block)
|
67
|
-
@hash = {}
|
24
|
+
class Option
|
25
|
+
def initialize(option, &block)
|
26
|
+
@option = option
|
68
27
|
instance_eval(&block)
|
69
28
|
end
|
70
29
|
|
71
|
-
def
|
72
|
-
|
73
|
-
if block
|
74
|
-
@hash[key] = HashDSL.new(block).hash
|
75
|
-
else
|
76
|
-
# Simulate method requiring 1 argument
|
77
|
-
raise ArgumentError, "wrong number of arguments(#{args.length} for 1)" unless args.length == 1
|
78
|
-
@hash[key] = args[0]
|
79
|
-
end
|
30
|
+
def type(t)
|
31
|
+
@option.type = t
|
80
32
|
end
|
81
|
-
end
|
82
33
|
|
83
|
-
|
84
|
-
|
34
|
+
def default(d)
|
35
|
+
@option.default = d
|
36
|
+
end
|
85
37
|
|
86
|
-
def
|
87
|
-
@
|
88
|
-
@hash2 = {}
|
89
|
-
instance_eval(&block)
|
38
|
+
def validate(v = nil, &block)
|
39
|
+
@option.validate = v || block
|
90
40
|
end
|
91
41
|
|
92
|
-
def
|
93
|
-
|
94
|
-
if block
|
95
|
-
child = DoubleHashDSL.new(block)
|
96
|
-
@hash1[key] = child.hash1
|
97
|
-
@hash2[key] = child.hash2
|
98
|
-
else
|
99
|
-
# Simulate method requiring 2 arguments
|
100
|
-
raise ArgumentError, "wrong number of arguments(#{args.length} for 2)" unless args.length == 2
|
101
|
-
@hash1[key] = args[0]
|
102
|
-
@hash2[key] = args[1]
|
103
|
-
end
|
42
|
+
def transform(&block)
|
43
|
+
@option.transform = block
|
104
44
|
end
|
105
45
|
end
|
106
46
|
end
|
data/lib/configru/version.rb
CHANGED
data/lib/configru.rb
CHANGED
@@ -1,88 +1,110 @@
|
|
1
|
-
$: << File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__))
|
2
|
-
|
3
|
-
require 'configru/confighash'
|
4
1
|
require 'configru/dsl'
|
2
|
+
require 'configru/option'
|
3
|
+
require 'configru/structhash'
|
5
4
|
|
6
5
|
require 'yaml'
|
7
6
|
|
8
7
|
module Configru
|
9
|
-
class
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
8
|
+
class OptionError < RuntimeError
|
9
|
+
def initialize(path, message)
|
10
|
+
super("#{path.join('.')}: #{message}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class OptionTypeError < OptionError
|
15
|
+
def initialize(path, expected, got)
|
16
|
+
super(path, "expected #{expected}, got #{got}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class OptionValidationError < OptionError
|
21
|
+
def initialize(path, validation = nil)
|
22
|
+
if validation
|
23
|
+
super(path, "failed validation `#{validation.inspect}`")
|
24
|
+
else
|
25
|
+
super(path, "failed validation")
|
26
|
+
end
|
23
27
|
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.load(*files, &block)
|
31
|
+
@files = files.flatten
|
32
|
+
@options = DSL::OptionGroup.new(&block).options
|
33
|
+
@root = StructHash.new
|
24
34
|
self.reload
|
25
35
|
end
|
26
36
|
|
27
37
|
def self.reload
|
28
|
-
|
29
|
-
@
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
if file = @files.find {|file| File.file?(file)} # Intended
|
34
|
-
@config.merge!(YAML.load_file(file) || {})
|
35
|
-
@loaded_files << file
|
36
|
-
end
|
37
|
-
when :cascade
|
38
|
-
@files.reverse_each do |file|
|
39
|
-
if File.file?(file)
|
40
|
-
@config.merge!(YAML.load_file(file) || {})
|
41
|
-
@loaded_files << file
|
42
|
-
end
|
38
|
+
loaded_files = []
|
39
|
+
@files.each do |file|
|
40
|
+
if File.file?(file) && !File.zero?(file)
|
41
|
+
self.load_file(file)
|
42
|
+
loaded_files << file
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
# Load all defaults if no files were loaded
|
47
|
+
# TODO: loaded_files as instance var?
|
48
|
+
# TODO: Better way to load defaults?
|
49
|
+
@option_path = []
|
50
|
+
self.load_group(@options, @root, {}) if loaded_files.empty?
|
47
51
|
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
def self.load_file(file)
|
54
|
+
@option_path = []
|
55
|
+
self.load_group(@options, @root, YAML.load_file(file) || {})
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.load_group(option_group, output, input)
|
59
|
+
option_group.each do |key, option|
|
60
|
+
@option_path << key
|
61
|
+
|
62
|
+
# option is a group
|
63
|
+
if option.is_a? Hash
|
64
|
+
if input.has_key?(key) && !input[key].is_a?(Hash)
|
65
|
+
raise OptionTypeError.new(@option_path, Hash, input[key].class)
|
66
|
+
end
|
67
|
+
group_output = output[key] || StructHash.new
|
68
|
+
self.load_group(option, group_output, input[key] || {})
|
69
|
+
output[key] = group_output
|
70
|
+
@option_path.pop
|
71
|
+
next
|
72
|
+
end
|
73
|
+
|
74
|
+
if input.include? key
|
75
|
+
value = input[key]
|
61
76
|
else
|
62
|
-
|
77
|
+
value = option.default
|
63
78
|
end
|
64
79
|
|
65
|
-
|
80
|
+
unless value.is_a? option.type
|
81
|
+
raise OptionTypeError.new(@option_path, option.type, value.class)
|
82
|
+
end
|
66
83
|
|
67
|
-
|
68
|
-
|
69
|
-
|
84
|
+
if option.validate.is_a? Proc
|
85
|
+
unless option.validate[value]
|
86
|
+
raise OptionValidationError.new(@option_path)
|
87
|
+
end
|
88
|
+
elsif option.validate
|
89
|
+
unless option.validate === value
|
90
|
+
raise OptionValidationError.new(@option_path, option.validate)
|
91
|
+
end
|
92
|
+
end
|
70
93
|
|
71
|
-
|
72
|
-
@loaded_files
|
73
|
-
end
|
94
|
+
output[key] = option.transform ? option.transform[value] : value
|
74
95
|
|
75
|
-
|
76
|
-
|
96
|
+
@option_path.pop
|
97
|
+
end
|
77
98
|
end
|
78
99
|
|
79
100
|
def self.[](key)
|
80
|
-
@
|
101
|
+
@root[key]
|
81
102
|
end
|
82
103
|
|
83
|
-
def self.method_missing(
|
84
|
-
#
|
85
|
-
|
86
|
-
|
104
|
+
def self.method_missing(method, *args)
|
105
|
+
# Let super raise the appropriate exception if it looks like the caller
|
106
|
+
# wants a real method
|
107
|
+
super(method, *args) unless args.empty?
|
108
|
+
@root.send(method)
|
87
109
|
end
|
88
110
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
describe Configru do
|
2
|
+
def example_file(x)
|
3
|
+
"spec/example_files/example_#{x}.yml"
|
4
|
+
end
|
5
|
+
|
6
|
+
it 'loads defaults if no files are given' do
|
7
|
+
Configru.load do
|
8
|
+
option :example, String, 'example'
|
9
|
+
end
|
10
|
+
|
11
|
+
Configru.example.should == 'example'
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'behaves like a hash' do
|
15
|
+
Configru.load do
|
16
|
+
option :example, String, 'example'
|
17
|
+
end
|
18
|
+
|
19
|
+
Configru['example'].should == 'example'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'loads defaults if no files exist' do
|
23
|
+
Configru.load(example_file :z) do
|
24
|
+
option :example, String, 'example'
|
25
|
+
end
|
26
|
+
|
27
|
+
Configru.example.should == 'example'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'loads defaults if file is empty' do
|
31
|
+
Configru.load(example_file :e) do
|
32
|
+
option :example, String, 'example'
|
33
|
+
end
|
34
|
+
|
35
|
+
Configru.example.should == 'example'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'loads defaults if file contains only whitespace' do
|
39
|
+
Configru.load(example_file :f) do
|
40
|
+
option :example, String, 'example'
|
41
|
+
end
|
42
|
+
|
43
|
+
Configru.example.should == 'example'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'loads a file' do
|
47
|
+
Configru.load(example_file :a) do
|
48
|
+
option :example
|
49
|
+
end
|
50
|
+
|
51
|
+
Configru.example.should == 'example_a'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'loads files in order' do
|
55
|
+
Configru.load(example_file(:a), example_file(:b)) do
|
56
|
+
option :example
|
57
|
+
end
|
58
|
+
|
59
|
+
Configru.example.should == 'example_b'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'loads a file with a group' do
|
63
|
+
Configru.load(example_file :c) do
|
64
|
+
option_group :example_group do
|
65
|
+
option :example
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Configru.example_group.example.should == 'example_c'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'checks that group values are Hashes' do
|
73
|
+
expect do
|
74
|
+
Configru.load(example_file :a) do
|
75
|
+
option_group :example do
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end.to raise_error(Configru::OptionTypeError)
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'checks option types' do
|
82
|
+
expect do
|
83
|
+
Configru.load(example_file :d) do
|
84
|
+
option :string, String, ''
|
85
|
+
end
|
86
|
+
end.to raise_error(Configru::OptionTypeError)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'validates options against values' do
|
90
|
+
expect do
|
91
|
+
Configru.load(example_file :d) do
|
92
|
+
option :example, String, 'test', /test/
|
93
|
+
end
|
94
|
+
end.to raise_error(Configru::OptionValidationError)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'validates options against blocks' do
|
98
|
+
expect do
|
99
|
+
Configru.load(example_file :d) do
|
100
|
+
option :example, String, '' do
|
101
|
+
validate { false }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end.to raise_error(Configru::OptionValidationError)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'applies transformations to options' do
|
108
|
+
Configru.load(example_file :d) do
|
109
|
+
option :example, String, '' do
|
110
|
+
transform {|x| x + 't' }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
Configru.example.should == 'example_dt'
|
115
|
+
end
|
116
|
+
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
describe Configru::DSL::Option do
|
2
|
+
before do
|
3
|
+
@option = Configru::Option.new(:type, :default, :validate, :transform)
|
4
|
+
end
|
5
|
+
|
6
|
+
it 'sets option type' do
|
7
|
+
described_class.new(@option) do
|
8
|
+
type String
|
9
|
+
end
|
10
|
+
|
11
|
+
@option.type.should == String
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'sets option default' do
|
15
|
+
described_class.new(@option) do
|
16
|
+
default 'Example'
|
17
|
+
end
|
18
|
+
|
19
|
+
@option.default.should == 'Example'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets option validate value' do
|
23
|
+
described_class.new(@option) do
|
24
|
+
validate /example/
|
25
|
+
end
|
26
|
+
|
27
|
+
@option.validate.should == /example/
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'sets option validate block' do
|
31
|
+
described_class.new(@option) do
|
32
|
+
validate do
|
33
|
+
:example
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@option.validate.should be_a(Proc)
|
38
|
+
@option.validate.call.should == :example
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'sets option transofmr block' do
|
42
|
+
described_class.new(@option) do
|
43
|
+
transform do
|
44
|
+
:example
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@option.transform.should be_a(Proc)
|
49
|
+
@option.transform.call.should == :example
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe Configru::DSL::OptionGroup do
|
54
|
+
it 'creates an option' do
|
55
|
+
group = described_class.new do
|
56
|
+
option 'example'
|
57
|
+
end
|
58
|
+
|
59
|
+
group.options.should have_key('example')
|
60
|
+
group.options['example'].should be_a(Configru::Option)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'converts option names to strings' do
|
64
|
+
group = described_class.new do
|
65
|
+
option :example
|
66
|
+
end
|
67
|
+
|
68
|
+
group.options.should have_key('example')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'creates a default option' do
|
72
|
+
group = described_class.new do
|
73
|
+
option 'example'
|
74
|
+
end
|
75
|
+
|
76
|
+
group.options['example'].type.should == Object
|
77
|
+
group.options['example'].default.should be_nil
|
78
|
+
group.options['example'].validate.should be_nil
|
79
|
+
group.options['example'].transform.should be_nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'runs the Option DSL' do
|
83
|
+
group = described_class.new do
|
84
|
+
option 'example' do
|
85
|
+
type String
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
group.options['example'].type.should == String
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'creates a group' do
|
93
|
+
group = described_class.new do
|
94
|
+
option_group 'example' do
|
95
|
+
option 'nested'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
group.options['example'].should be_a(Hash)
|
100
|
+
group.options['example'].should have_key('nested')
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'converts group names to strings' do
|
104
|
+
group = described_class.new do
|
105
|
+
option_group :example do
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
group.options.should have_key('example')
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
example: example_a
|
@@ -0,0 +1 @@
|
|
1
|
+
example: example_b
|
File without changes
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
describe Configru::StructHash do
|
2
|
+
before do
|
3
|
+
@hash = Configru::StructHash.new
|
4
|
+
end
|
5
|
+
|
6
|
+
it 'is a hash' do
|
7
|
+
@hash.should be_a_kind_of(Hash)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns nil for missing method key' do
|
11
|
+
@hash.example.should be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns value for method key' do
|
15
|
+
@hash['example'] = :example
|
16
|
+
@hash.example.should == :example
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configru
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,9 +9,9 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
|
-
description:
|
14
|
+
description: YAML configuration file loader
|
15
15
|
email:
|
16
16
|
- programble@gmail.com
|
17
17
|
executables: []
|
@@ -19,19 +19,29 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- .gitignore
|
22
|
+
- .rspec
|
23
|
+
- .travis.yml
|
22
24
|
- ChangeLog.md
|
23
25
|
- Gemfile
|
26
|
+
- Gemfile.lock
|
24
27
|
- README.md
|
25
28
|
- Rakefile
|
26
29
|
- configru.gemspec
|
27
30
|
- lib/configru.rb
|
28
|
-
- lib/configru/confighash.rb
|
29
31
|
- lib/configru/dsl.rb
|
32
|
+
- lib/configru/option.rb
|
33
|
+
- lib/configru/structhash.rb
|
30
34
|
- lib/configru/version.rb
|
31
|
-
-
|
32
|
-
-
|
33
|
-
-
|
34
|
-
-
|
35
|
+
- spec/configru_spec.rb
|
36
|
+
- spec/dsl_spec.rb
|
37
|
+
- spec/example_files/example_a.yml
|
38
|
+
- spec/example_files/example_b.yml
|
39
|
+
- spec/example_files/example_c.yml
|
40
|
+
- spec/example_files/example_d.yml
|
41
|
+
- spec/example_files/example_e.yml
|
42
|
+
- spec/example_files/example_f.yml
|
43
|
+
- spec/spec_helper.rb
|
44
|
+
- spec/structhash_spec.rb
|
35
45
|
homepage: https://github.com/programble/configru
|
36
46
|
licenses: []
|
37
47
|
post_install_message:
|
@@ -44,20 +54,32 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
54
|
- - ! '>='
|
45
55
|
- !ruby/object:Gem::Version
|
46
56
|
version: '0'
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
hash: -2633773784802344253
|
47
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
61
|
none: false
|
49
62
|
requirements:
|
50
63
|
- - ! '>='
|
51
64
|
- !ruby/object:Gem::Version
|
52
65
|
version: '0'
|
66
|
+
segments:
|
67
|
+
- 0
|
68
|
+
hash: -2633773784802344253
|
53
69
|
requirements: []
|
54
70
|
rubyforge_project:
|
55
71
|
rubygems_version: 1.8.11
|
56
72
|
signing_key:
|
57
73
|
specification_version: 3
|
58
|
-
summary:
|
74
|
+
summary: YAML configuration file loader
|
59
75
|
test_files:
|
60
|
-
-
|
61
|
-
-
|
62
|
-
-
|
63
|
-
-
|
76
|
+
- spec/configru_spec.rb
|
77
|
+
- spec/dsl_spec.rb
|
78
|
+
- spec/example_files/example_a.yml
|
79
|
+
- spec/example_files/example_b.yml
|
80
|
+
- spec/example_files/example_c.yml
|
81
|
+
- spec/example_files/example_d.yml
|
82
|
+
- spec/example_files/example_e.yml
|
83
|
+
- spec/example_files/example_f.yml
|
84
|
+
- spec/spec_helper.rb
|
85
|
+
- spec/structhash_spec.rb
|
data/lib/configru/confighash.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module Configru
|
2
|
-
class ConfigHash < Hash
|
3
|
-
def initialize(hash={})
|
4
|
-
super
|
5
|
-
merge!(hash)
|
6
|
-
end
|
7
|
-
|
8
|
-
def merge!(hash)
|
9
|
-
hash.each do |key, value|
|
10
|
-
if value.is_a?(Hash) && self[key].is_a?(ConfigHash)
|
11
|
-
self[key].merge!(value)
|
12
|
-
elsif value.is_a?(Hash)
|
13
|
-
self[key] = ConfigHash.new(value)
|
14
|
-
else
|
15
|
-
self[key] = value
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def [](key)
|
21
|
-
key = key.to_s if key.is_a?(Symbol)
|
22
|
-
# Allow for accessing keys with hyphens using underscores
|
23
|
-
key = key.gsub('_', '-') unless self.include?(key)
|
24
|
-
# For some reason, super(key) returns {} instead of nil when the key
|
25
|
-
# doesn't exist :\
|
26
|
-
super(key) if self.include?(key)
|
27
|
-
end
|
28
|
-
|
29
|
-
def method_missing(key, *args)
|
30
|
-
self[key]
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/test/confighash_test.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require 'teststrap'
|
2
|
-
|
3
|
-
context 'ConfigHash - ' do
|
4
|
-
setup { Configru::ConfigHash.new }
|
5
|
-
|
6
|
-
context 'empty' do
|
7
|
-
asserts('[:quux]') { topic[:quux] }.nil
|
8
|
-
asserts(:quux).nil
|
9
|
-
end
|
10
|
-
|
11
|
-
context 'non-empty' do
|
12
|
-
hookup do
|
13
|
-
topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
|
14
|
-
end
|
15
|
-
|
16
|
-
# Testing access methods
|
17
|
-
asserts("['foo']") { topic['foo'] }.equals(1)
|
18
|
-
asserts('[:foo]') { topic[:foo] }.equals(1)
|
19
|
-
asserts(:foo).equals(1)
|
20
|
-
end
|
21
|
-
|
22
|
-
context 'overwriting a value by merge' do
|
23
|
-
hookup do
|
24
|
-
topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
|
25
|
-
topic.merge!({'bar' => 4})
|
26
|
-
end
|
27
|
-
asserts(:bar).equals(4)
|
28
|
-
end
|
29
|
-
|
30
|
-
context 'merging a nested Hash' do
|
31
|
-
hookup do
|
32
|
-
topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
|
33
|
-
topic.merge!({'bar' => 4})
|
34
|
-
topic.merge!({'baz' => {'quux' => 5}})
|
35
|
-
end
|
36
|
-
|
37
|
-
asserts(:baz).kind_of(Configru::ConfigHash)
|
38
|
-
asserts('baz.quux') { topic.baz.quux }.equals(5)
|
39
|
-
end
|
40
|
-
|
41
|
-
context 'recursively merging a nested Hash' do
|
42
|
-
hookup do
|
43
|
-
topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
|
44
|
-
topic.merge!({'bar' => 4})
|
45
|
-
topic.merge!({'baz' => {'quux' => 5}})
|
46
|
-
topic.merge!({'baz' => {'apple' => 6}})
|
47
|
-
end
|
48
|
-
|
49
|
-
asserts(:baz).kind_of(Configru::ConfigHash)
|
50
|
-
asserts('baz.apple') { topic.baz.apple }.equals(6)
|
51
|
-
asserts('baz.quux') { topic.baz.quux }.equals(5)
|
52
|
-
end
|
53
|
-
|
54
|
-
context 'keys with hyphens' do
|
55
|
-
hookup do
|
56
|
-
topic.merge!({'foo-bar-baz' => 1})
|
57
|
-
end
|
58
|
-
|
59
|
-
asserts('foo_bar_baz') { topic.foo_bar_baz }.equals(1)
|
60
|
-
asserts('[:foo_bar_baz]') { topic[:foo_bar_baz] }.equals(1)
|
61
|
-
asserts("['foo_bar_baz']") { topic['foo_bar_baz'] }.equals(1)
|
62
|
-
end
|
63
|
-
end
|
data/test/hashdsl_test.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
require 'teststrap'
|
2
|
-
require 'configru/dsl'
|
3
|
-
|
4
|
-
context 'HashDSL - flat' do
|
5
|
-
setup do
|
6
|
-
block = proc do
|
7
|
-
foo 1
|
8
|
-
bar 2
|
9
|
-
end
|
10
|
-
Configru::DSL::HashDSL.new(block).hash
|
11
|
-
end
|
12
|
-
|
13
|
-
asserts_topic.equals({'foo' => 1, 'bar' => 2})
|
14
|
-
end
|
15
|
-
|
16
|
-
context 'HashDSL - nested' do
|
17
|
-
setup do
|
18
|
-
block = proc do
|
19
|
-
foo 1
|
20
|
-
bar do
|
21
|
-
baz 2
|
22
|
-
quux 3
|
23
|
-
end
|
24
|
-
end
|
25
|
-
Configru::DSL::HashDSL.new(block).hash
|
26
|
-
end
|
27
|
-
|
28
|
-
asserts_topic.equals({'foo' => 1, 'bar' => {'baz' => 2, 'quux' => 3}})
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'HashDSL - keys with underscores' do
|
32
|
-
setup do
|
33
|
-
block = proc do
|
34
|
-
foo_bar 1
|
35
|
-
baz_quux 2
|
36
|
-
end
|
37
|
-
Configru::DSL::HashDSL.new(block).hash
|
38
|
-
end
|
39
|
-
|
40
|
-
asserts_topic.equals({'foo-bar' => 1, 'baz-quux' => 2})
|
41
|
-
end
|
42
|
-
|
43
|
-
context 'DoubleHashDSL - flat' do
|
44
|
-
setup do
|
45
|
-
block = proc do
|
46
|
-
foo 1, 2
|
47
|
-
bar 3, 4
|
48
|
-
end
|
49
|
-
Configru::DSL::DoubleHashDSL.new(block)
|
50
|
-
end
|
51
|
-
|
52
|
-
asserts(:hash1).equals({'foo' => 1, 'bar' => 3})
|
53
|
-
asserts(:hash2).equals({'foo' => 2, 'bar' => 4})
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'DoubleHashDSL - nested' do
|
57
|
-
setup do
|
58
|
-
block = proc do
|
59
|
-
foo 1, 2
|
60
|
-
bar do
|
61
|
-
baz 3, 4
|
62
|
-
quux 5, 6
|
63
|
-
end
|
64
|
-
end
|
65
|
-
Configru::DSL::DoubleHashDSL.new(block)
|
66
|
-
end
|
67
|
-
|
68
|
-
asserts(:hash1).equals({'foo' => 1, 'bar' => {'baz' => 3, 'quux' => 5}})
|
69
|
-
asserts(:hash2).equals({'foo' => 2, 'bar' => {'baz' => 4, 'quux' => 6}})
|
70
|
-
end
|
71
|
-
|
72
|
-
context 'DoubleHashDSL - keys with underscores' do
|
73
|
-
setup do
|
74
|
-
block = proc do
|
75
|
-
foo_bar 1, 2
|
76
|
-
baz_quux 3, 4
|
77
|
-
end
|
78
|
-
Configru::DSL::DoubleHashDSL.new(block)
|
79
|
-
end
|
80
|
-
|
81
|
-
asserts(:hash1).equals({'foo-bar' => 1, 'baz-quux' => 3})
|
82
|
-
asserts(:hash2).equals({'foo-bar' => 2, 'baz-quux' => 4})
|
83
|
-
end
|
data/test/loaddsl_test.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'teststrap'
|
2
|
-
require 'configru/dsl'
|
3
|
-
|
4
|
-
context 'LoadDSL - Files' do
|
5
|
-
setup do
|
6
|
-
block = proc do
|
7
|
-
cascade 'a', 'b', 'c'
|
8
|
-
end
|
9
|
-
Configru::DSL::LoadDSL.new(block).files_array
|
10
|
-
end
|
11
|
-
|
12
|
-
asserts_topic.equals(['a', 'b', 'c'])
|
13
|
-
end
|
14
|
-
|
15
|
-
context 'LoadDSL - File-directories' do
|
16
|
-
setup do
|
17
|
-
block = proc do
|
18
|
-
cascade 'a', ['b', 'c']
|
19
|
-
end
|
20
|
-
Configru::DSL::LoadDSL.new(block).files_array
|
21
|
-
end
|
22
|
-
|
23
|
-
asserts_topic.equals([File.join('b', 'a'), File.join('c', 'a')])
|
24
|
-
end
|
data/test/teststrap.rb
DELETED