configru 0.5.0 → 2.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/.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
|
+
[](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