figgy 0.0.1
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 +6 -0
- data/.rvmrc +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README +21 -0
- data/Rakefile +5 -0
- data/figgy.gemspec +25 -0
- data/lib/figgy/configuration.rb +85 -0
- data/lib/figgy/finder.rb +71 -0
- data/lib/figgy/hash.rb +49 -0
- data/lib/figgy/store.rb +19 -0
- data/lib/figgy/version.rb +3 -0
- data/lib/figgy.rb +33 -0
- data/spec/figgy_spec.rb +322 -0
- data/spec/spec_helper.rb +29 -0
- metadata +128 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ruby-1.9.3-rc1@figgy --create
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
In Gemfile:
|
2
|
+
|
3
|
+
gem 'figgy'
|
4
|
+
|
5
|
+
Configure (say, in a Rails initializer):
|
6
|
+
|
7
|
+
APP_CONFIG = Figgy.build do |config|
|
8
|
+
config.root = Rails.root.join('etc')
|
9
|
+
|
10
|
+
# config.foo is read from etc/foo.yml
|
11
|
+
config.define_overlay :default, nil
|
12
|
+
|
13
|
+
# config.foo is then updated with values from etc/production/foo.yml
|
14
|
+
config.define_overlay(:environment) { Rails.env }
|
15
|
+
end
|
16
|
+
|
17
|
+
Access it as a dottable, indifferent-access hash:
|
18
|
+
|
19
|
+
APP_CONFIG["foo"]["some_key"]
|
20
|
+
APP_CONFIG[:foo].some_key
|
21
|
+
APP_CONFIG.foo[:some_key]
|
data/Rakefile
ADDED
data/figgy.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "figgy/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "figgy"
|
7
|
+
s.version = Figgy::VERSION
|
8
|
+
s.authors = ["Kyle Hargraves"]
|
9
|
+
s.email = ["pd@krh.me"]
|
10
|
+
s.homepage = "http://github.com/pd/figgy"
|
11
|
+
s.summary = %q{Configuration file reading}
|
12
|
+
s.description = %q{Access YAML, JSON (and ...) configuration files with ease}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency "json"
|
20
|
+
s.add_development_dependency "rake"
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
s.add_development_dependency "simplecov"
|
23
|
+
s.add_development_dependency "aruba"
|
24
|
+
s.add_development_dependency "heredoc_unindent"
|
25
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class Figgy
|
2
|
+
class Configuration
|
3
|
+
attr_reader :root, :overlays
|
4
|
+
attr_accessor :always_reload, :preload, :freeze
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
self.root = Dir.pwd
|
8
|
+
@handlers = []
|
9
|
+
@overlays = []
|
10
|
+
@always_reload = false
|
11
|
+
@preload = false
|
12
|
+
@freeze = false
|
13
|
+
|
14
|
+
define_handler 'yml', 'yaml' do |contents|
|
15
|
+
YAML.load(contents)
|
16
|
+
end
|
17
|
+
|
18
|
+
define_handler 'yml.erb', 'yaml.erb' do |contents|
|
19
|
+
erb = ERB.new(contents).result
|
20
|
+
YAML.load(erb)
|
21
|
+
end
|
22
|
+
|
23
|
+
define_handler 'json' do |contents|
|
24
|
+
JSON.parse(contents)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def root=(path)
|
29
|
+
@root = File.expand_path(path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def always_reload?
|
33
|
+
!!@always_reload
|
34
|
+
end
|
35
|
+
|
36
|
+
def preload?
|
37
|
+
!!@preload
|
38
|
+
end
|
39
|
+
|
40
|
+
def freeze?
|
41
|
+
!!@freeze
|
42
|
+
end
|
43
|
+
|
44
|
+
def define_overlay(name, value = nil)
|
45
|
+
value = yield if block_given?
|
46
|
+
@overlays << [name, value]
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_combined_overlay(*names)
|
50
|
+
combined_name = names.join("_").to_sym
|
51
|
+
value = names.map { |name| overlay_value(name) }.join("_")
|
52
|
+
@overlays << [combined_name, value]
|
53
|
+
end
|
54
|
+
|
55
|
+
def overlay_dirs
|
56
|
+
return [@root] if @overlays.empty?
|
57
|
+
overlay_values.map { |v| v ? File.join(@root, v) : @root }.uniq
|
58
|
+
end
|
59
|
+
|
60
|
+
def define_handler(*extensions, &block)
|
61
|
+
@handlers += extensions.map { |ext| [ext, block] }
|
62
|
+
end
|
63
|
+
|
64
|
+
def extensions
|
65
|
+
@handlers.map { |ext, handler| ext }
|
66
|
+
end
|
67
|
+
|
68
|
+
def handler_for(filename)
|
69
|
+
match = @handlers.find { |ext, handler| filename =~ /\.#{ext}$/ }
|
70
|
+
match && match.last
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def overlay_value(name)
|
76
|
+
overlay = @overlays.find { |n, v| name == n }
|
77
|
+
raise "No such overlay: #{name.inspect}" unless overlay
|
78
|
+
overlay.last
|
79
|
+
end
|
80
|
+
|
81
|
+
def overlay_values
|
82
|
+
@overlays.map &:last
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/figgy/finder.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
class Figgy
|
2
|
+
class Finder
|
3
|
+
def initialize(config)
|
4
|
+
@config = config
|
5
|
+
end
|
6
|
+
|
7
|
+
def load(name)
|
8
|
+
result = files_for(name).reduce(nil) do |result, file|
|
9
|
+
object = @config.handler_for(file).call(File.read(file))
|
10
|
+
if result && result.respond_to?(:merge)
|
11
|
+
deep_merge(result, object)
|
12
|
+
else
|
13
|
+
object
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
raise(Figgy::FileNotFound, "Can't find config files for key: #{name.inspect}") unless result
|
18
|
+
deep_freeze(to_figgy_hash(result))
|
19
|
+
end
|
20
|
+
|
21
|
+
def files_for(name)
|
22
|
+
Dir[*file_globs(name)]
|
23
|
+
end
|
24
|
+
|
25
|
+
def all_key_names
|
26
|
+
Dir[*file_globs].map { |file| File.basename(file).sub(/\..+$/, '') }.uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def file_globs(name = '*')
|
32
|
+
globs = extension_globs(name)
|
33
|
+
@config.overlay_dirs.map { |dir|
|
34
|
+
globs.map { |glob| File.join(dir, glob) }
|
35
|
+
}.flatten
|
36
|
+
end
|
37
|
+
|
38
|
+
def extension_globs(name = '*')
|
39
|
+
@config.extensions.map { |ext| "#{name}.#{ext}" }
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_figgy_hash(obj)
|
43
|
+
case obj
|
44
|
+
when ::Hash
|
45
|
+
obj.each_pair { |k, v| obj[k] = to_figgy_hash(v) }
|
46
|
+
Figgy::Hash.new(obj)
|
47
|
+
when Array
|
48
|
+
obj.map { |v| to_figgy_hash(v) }
|
49
|
+
else
|
50
|
+
obj
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def deep_freeze(obj)
|
55
|
+
return obj unless @config.freeze?
|
56
|
+
case obj
|
57
|
+
when ::Hash
|
58
|
+
obj.each_pair { |k, v| obj[deep_freeze(k)] = deep_freeze(v) }
|
59
|
+
when Array
|
60
|
+
obj.map! { |v| deep_freeze(v) }
|
61
|
+
end
|
62
|
+
obj.freeze
|
63
|
+
end
|
64
|
+
|
65
|
+
def deep_merge(a, b)
|
66
|
+
a.merge(b) do |key, oldval, newval|
|
67
|
+
oldval.respond_to?(:merge) && newval.respond_to?(:merge) ? deep_merge(oldval, newval) : newval
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/figgy/hash.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Figgy
|
2
|
+
# Stolen from Thor::CoreExt::HashWithIndifferentAccess
|
3
|
+
# It's smaller and more grokkable than ActiveSupport's.
|
4
|
+
class Hash < ::Hash
|
5
|
+
def initialize(hash = {})
|
6
|
+
super()
|
7
|
+
hash.each do |key, value|
|
8
|
+
self[convert_key(key)] = value
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
super(convert_key(key))
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(key, value)
|
17
|
+
super(convert_key(key), value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete(key)
|
21
|
+
super(convert_key(key))
|
22
|
+
end
|
23
|
+
|
24
|
+
def values_at(*indices)
|
25
|
+
indices.collect { |key| self[convert_key(key)] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def merge(other)
|
29
|
+
dup.merge!(other)
|
30
|
+
end
|
31
|
+
|
32
|
+
def merge!(other)
|
33
|
+
other.each do |key, value|
|
34
|
+
self[convert_key(key)] = value
|
35
|
+
end
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def convert_key(key)
|
42
|
+
key.is_a?(Symbol) ? key.to_s : key
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(m, *args, &block)
|
46
|
+
self[m]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/figgy/store.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Figgy
|
2
|
+
class Store
|
3
|
+
def initialize(finder, config)
|
4
|
+
@finder = finder
|
5
|
+
@config = config
|
6
|
+
@cache = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(key)
|
10
|
+
key = key.to_s
|
11
|
+
@cache.delete(key) if @config.always_reload?
|
12
|
+
if @cache.key?(key)
|
13
|
+
@cache[key]
|
14
|
+
else
|
15
|
+
@cache[key] = @finder.load(key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/figgy.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "erb"
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
require "figgy/version"
|
6
|
+
require "figgy/configuration"
|
7
|
+
require "figgy/hash"
|
8
|
+
require "figgy/finder"
|
9
|
+
require "figgy/store"
|
10
|
+
|
11
|
+
class Figgy
|
12
|
+
FileNotFound = Class.new(StandardError)
|
13
|
+
|
14
|
+
def self.build(&block)
|
15
|
+
config = Configuration.new
|
16
|
+
block.call(config)
|
17
|
+
new(config)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(config)
|
21
|
+
@config = config
|
22
|
+
@finder = Finder.new(config)
|
23
|
+
@store = Store.new(@finder, @config)
|
24
|
+
|
25
|
+
if @config.preload?
|
26
|
+
@finder.all_key_names.each { |key| @store.get(key) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(m, *args, &block)
|
31
|
+
@store.get(m)
|
32
|
+
end
|
33
|
+
end
|
data/spec/figgy_spec.rb
ADDED
@@ -0,0 +1,322 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Figgy do
|
4
|
+
it "reads YAML config files" do
|
5
|
+
write_config 'values', <<-YML
|
6
|
+
foo: 1
|
7
|
+
bar: 2
|
8
|
+
YML
|
9
|
+
|
10
|
+
test_config.values.should == { "foo" => 1, "bar" => 2 }
|
11
|
+
end
|
12
|
+
|
13
|
+
it "raises an exception if the file can't be found" do
|
14
|
+
expect { test_config.values }.to raise_error(Figgy::FileNotFound)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "multiple extensions" do
|
18
|
+
it "supports .yaml" do
|
19
|
+
write_config 'values.yaml', 'foo: 1'
|
20
|
+
test_config.values.foo.should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it "supports .yml.erb and .yaml.erb" do
|
24
|
+
write_config 'values.yml.erb', '<%= "foo" %>: <%= 1 %>'
|
25
|
+
write_config 'values.yaml.erb', '<%= "foo" %>: <%= 2 %>'
|
26
|
+
test_config.values.foo.should == 2
|
27
|
+
end
|
28
|
+
|
29
|
+
it "supports .json" do
|
30
|
+
write_config "values.json", '{ "json": true }'
|
31
|
+
test_config.values.json.should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "loads in the order named" do
|
35
|
+
write_config 'values.yml', 'foo: 1'
|
36
|
+
write_config 'values.yaml', 'foo: 2'
|
37
|
+
|
38
|
+
config = test_config do |config|
|
39
|
+
config.define_handler('yml', 'yaml') { |body| YAML.load(body) }
|
40
|
+
end
|
41
|
+
config.values.foo.should == 2
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "hash contents" do
|
46
|
+
it "makes the hash result dottable and indifferent" do
|
47
|
+
write_config 'values', <<-YML
|
48
|
+
outer:
|
49
|
+
also: dottable
|
50
|
+
YML
|
51
|
+
|
52
|
+
config = test_config
|
53
|
+
config.values.outer.should == { "also" => "dottable" }
|
54
|
+
config.values["outer"].should == { "also" => "dottable" }
|
55
|
+
config.values[:outer].should == { "also" => "dottable" }
|
56
|
+
end
|
57
|
+
|
58
|
+
it "makes a hash inside the hash result dottable and indifferent" do
|
59
|
+
write_config 'values', <<-YML
|
60
|
+
outer:
|
61
|
+
also: dottable
|
62
|
+
YML
|
63
|
+
|
64
|
+
config = test_config
|
65
|
+
config.values.outer.also.should == "dottable"
|
66
|
+
config.values.outer["also"].should == "dottable"
|
67
|
+
config.values.outer[:also].should == "dottable"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "makes a hash inside an array result dottable and indifferent" do
|
71
|
+
write_config 'values', <<-YML
|
72
|
+
outer:
|
73
|
+
- in: an
|
74
|
+
array: it is
|
75
|
+
- still: a dottable hash
|
76
|
+
YML
|
77
|
+
|
78
|
+
config = test_config
|
79
|
+
config.values.outer.size.should == 2
|
80
|
+
first, second = *config.values.outer
|
81
|
+
|
82
|
+
first.should == { "in" => "an", "array" => "it is" }
|
83
|
+
first[:in].should == "an"
|
84
|
+
first.array.should == "it is"
|
85
|
+
|
86
|
+
second.still.should == "a dottable hash"
|
87
|
+
second[:still].should == "a dottable hash"
|
88
|
+
second["still"].should == "a dottable hash"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "overlays" do
|
93
|
+
it "defaults to no overlay, thus reading directly from the config root" do
|
94
|
+
write_config 'values', "foo: 1"
|
95
|
+
test_config.values.should == { "foo" => 1 }
|
96
|
+
end
|
97
|
+
|
98
|
+
it "interprets a nil overlay value as an indication to read from the config root" do
|
99
|
+
write_config 'values', "foo: 1"
|
100
|
+
config = test_config do |config|
|
101
|
+
config.define_overlay :default, nil
|
102
|
+
end
|
103
|
+
config.values.should == { "foo" => 1 }
|
104
|
+
end
|
105
|
+
|
106
|
+
it "allows the overlay's value to be the result of a block" do
|
107
|
+
write_config 'prod/values', "foo: 1"
|
108
|
+
config = test_config do |config|
|
109
|
+
config.define_overlay(:environment) { 'prod' }
|
110
|
+
end
|
111
|
+
config.values.should == { "foo" => 1 }
|
112
|
+
end
|
113
|
+
|
114
|
+
it "overwrites values if the config file does not define a hash" do
|
115
|
+
write_config 'some_string', "foo bar baz"
|
116
|
+
write_config 'prod/some_string', "foo bar baz quux"
|
117
|
+
|
118
|
+
config = test_config do |config|
|
119
|
+
config.define_overlay :default, nil
|
120
|
+
config.define_overlay :environment, 'prod'
|
121
|
+
end
|
122
|
+
|
123
|
+
config.some_string.should == "foo bar baz quux"
|
124
|
+
end
|
125
|
+
|
126
|
+
it "deep merges hash contents from overlays" do
|
127
|
+
write_config 'defaults/values', <<-YML
|
128
|
+
foo:
|
129
|
+
bar: 1
|
130
|
+
baz: 2
|
131
|
+
YML
|
132
|
+
|
133
|
+
write_config 'prod/values', <<-YML
|
134
|
+
foo:
|
135
|
+
baz: 3
|
136
|
+
quux: hi!
|
137
|
+
YML
|
138
|
+
|
139
|
+
config = test_config do |config|
|
140
|
+
config.define_overlay :default, 'defaults'
|
141
|
+
config.define_overlay :environment, 'prod'
|
142
|
+
end
|
143
|
+
|
144
|
+
config.values.should == { "foo" => { "bar" => 1, "baz" => 3 }, "quux" => "hi!" }
|
145
|
+
end
|
146
|
+
|
147
|
+
it "can use both a nil overlay and an overlay with a value" do
|
148
|
+
write_config 'values', "foo: 1\nbar: 2"
|
149
|
+
write_config 'prod/values', "foo: 2"
|
150
|
+
config = test_config do |config|
|
151
|
+
config.define_overlay :default, nil
|
152
|
+
config.define_overlay :environment, 'prod'
|
153
|
+
end
|
154
|
+
config.values.should == { "foo" => 2, "bar" => 2 }
|
155
|
+
end
|
156
|
+
|
157
|
+
it "reads from overlays in order of definition" do
|
158
|
+
write_config 'defaults/values', <<-YML
|
159
|
+
foo: 1
|
160
|
+
bar: 1
|
161
|
+
baz: 1
|
162
|
+
YML
|
163
|
+
|
164
|
+
write_config 'prod/values', <<-YML
|
165
|
+
bar: 2
|
166
|
+
baz: 2
|
167
|
+
YML
|
168
|
+
|
169
|
+
write_config 'local/values', <<-YML
|
170
|
+
baz: 3
|
171
|
+
YML
|
172
|
+
|
173
|
+
config = test_config do |config|
|
174
|
+
config.define_overlay :default, 'defaults'
|
175
|
+
config.define_overlay :environment, 'prod'
|
176
|
+
config.define_overlay :local, 'local'
|
177
|
+
end
|
178
|
+
|
179
|
+
config.values.should == { "foo" => 1, "bar" => 2, "baz" => 3 }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "combined overlays" do
|
184
|
+
it "allows new overlays to be defined from the values of others" do
|
185
|
+
write_config 'keys', "foo: 1"
|
186
|
+
write_config 'prod/keys', "foo: 2"
|
187
|
+
write_config 'prod_US/keys', "foo: 3"
|
188
|
+
|
189
|
+
config = test_config do |config|
|
190
|
+
config.define_overlay :default, nil
|
191
|
+
config.define_overlay :environment, 'prod'
|
192
|
+
config.define_overlay :country, 'US'
|
193
|
+
config.define_combined_overlay :environment, :country
|
194
|
+
end
|
195
|
+
|
196
|
+
config.keys.should == { "foo" => 3 }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context "reloading" do
|
201
|
+
it "can reload on each access when config.always_reload = true" do
|
202
|
+
write_config 'values', 'foo: 1'
|
203
|
+
config = test_config do |config|
|
204
|
+
config.always_reload = true
|
205
|
+
end
|
206
|
+
config.values.should == { "foo" => 1 }
|
207
|
+
|
208
|
+
write_config 'values', 'foo: bar'
|
209
|
+
config.values.should == { "foo" => "bar" }
|
210
|
+
end
|
211
|
+
|
212
|
+
it "does not reload when config.always_reload = false" do
|
213
|
+
write_config 'values', 'foo: 1'
|
214
|
+
config = test_config do |config|
|
215
|
+
config.always_reload = false
|
216
|
+
end
|
217
|
+
config.values.should == { "foo" => 1 }
|
218
|
+
|
219
|
+
write_config 'values', 'foo: bar'
|
220
|
+
config.values.should == { "foo" => 1 }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "preloading" do
|
225
|
+
it "can preload all available configs when config.preload = true" do
|
226
|
+
write_config 'values', 'foo: 1'
|
227
|
+
write_config 'prod/values', 'foo: 2'
|
228
|
+
write_config 'prod/prod_only', 'bar: baz'
|
229
|
+
|
230
|
+
config = test_config do |config|
|
231
|
+
config.define_overlay :default, nil
|
232
|
+
config.define_overlay :environment, 'prod'
|
233
|
+
config.preload = true
|
234
|
+
end
|
235
|
+
|
236
|
+
write_config 'prod/values', 'foo: 3'
|
237
|
+
write_config 'prod_only', 'bar: quux'
|
238
|
+
|
239
|
+
config.values['foo'].should == 2
|
240
|
+
config.prod_only['bar'].should == 'baz'
|
241
|
+
end
|
242
|
+
|
243
|
+
it "still works with multiple extension support" do
|
244
|
+
write_config 'values.yaml', 'foo: 1'
|
245
|
+
write_config 'values.json', '{ "foo": 2 }'
|
246
|
+
write_config 'prod/lonely.yml', 'only: yml'
|
247
|
+
write_config 'local/json_values.json', '{ "json": true }'
|
248
|
+
|
249
|
+
config = test_config do |config|
|
250
|
+
config.define_overlay :default, nil
|
251
|
+
config.define_overlay :environment, 'prod'
|
252
|
+
config.define_overlay :local, 'local'
|
253
|
+
end
|
254
|
+
|
255
|
+
finder = config.instance_variable_get(:@finder)
|
256
|
+
finder.all_key_names.should == ['values', 'lonely', 'json_values']
|
257
|
+
end
|
258
|
+
|
259
|
+
it "still supports reloading when preloading is enabled" do
|
260
|
+
write_config 'values', 'foo: 1'
|
261
|
+
|
262
|
+
config = test_config do |config|
|
263
|
+
config.preload = true
|
264
|
+
config.always_reload = true
|
265
|
+
end
|
266
|
+
|
267
|
+
config.values['foo'].should == 1
|
268
|
+
|
269
|
+
write_config 'values', 'foo: 2'
|
270
|
+
config.values['foo'].should == 2
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context "freezing" do
|
275
|
+
it "leaves results unfrozen by default" do
|
276
|
+
write_config 'values', 'foo: 1'
|
277
|
+
test_config.values.foo.should_not be_frozen
|
278
|
+
end
|
279
|
+
|
280
|
+
it "freezes the results when config.freeze = true" do
|
281
|
+
write_config 'values', 'foo: 1'
|
282
|
+
config = test_config do |config|
|
283
|
+
config.freeze = true
|
284
|
+
end
|
285
|
+
config.values.should be_frozen
|
286
|
+
end
|
287
|
+
|
288
|
+
it "freezes all the way down" do
|
289
|
+
write_config 'values', <<-YML
|
290
|
+
outer:
|
291
|
+
key: value
|
292
|
+
array:
|
293
|
+
- some string
|
294
|
+
- another string
|
295
|
+
- and: an inner hash
|
296
|
+
YML
|
297
|
+
|
298
|
+
config = test_config do |config|
|
299
|
+
config.freeze = true
|
300
|
+
end
|
301
|
+
|
302
|
+
expect { config.values.outer.array[2]['and'] = 'foo' }.to raise_error(/can't modify frozen/)
|
303
|
+
assert_deeply_frozen(config.values)
|
304
|
+
end
|
305
|
+
|
306
|
+
def assert_deeply_frozen(obj)
|
307
|
+
obj.should be_frozen
|
308
|
+
case obj
|
309
|
+
when Hash then obj.each { |k, v| assert_deeply_frozen(k); assert_deeply_frozen(v) }
|
310
|
+
when Array then obj.each { |v| assert_deeply_frozen(v) }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe Figgy do
|
317
|
+
describe 'CnuConfig drop-in compatibility' do
|
318
|
+
it "should maybe support path_formatter = some_proc.call(config_name, overlays)"
|
319
|
+
it "should support preload's all_key_names when using path_formatter"
|
320
|
+
it "should support preload's all_key_names when using path_formatter"
|
321
|
+
end
|
322
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
SimpleCov.start
|
4
|
+
|
5
|
+
require 'rspec'
|
6
|
+
require 'figgy'
|
7
|
+
require 'aruba/api'
|
8
|
+
require 'heredoc_unindent'
|
9
|
+
|
10
|
+
module Figgy::SpecHelpers
|
11
|
+
def test_config
|
12
|
+
Figgy.build do |config|
|
13
|
+
config.root = current_dir
|
14
|
+
yield config if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def write_config(filename, contents)
|
19
|
+
filename = "#{filename}.yml" unless filename =~ /\./
|
20
|
+
write_file(filename, contents.unindent)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
RSpec.configure do |c|
|
25
|
+
c.include Aruba::Api
|
26
|
+
c.include Figgy::SpecHelpers
|
27
|
+
|
28
|
+
c.after { FileUtils.rm_rf(current_dir) }
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: figgy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kyle Hargraves
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: &70234275187800 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70234275187800
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &70234275187060 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70234275187060
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &70234275186200 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70234275186200
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: simplecov
|
49
|
+
requirement: &70234275185340 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70234275185340
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: aruba
|
60
|
+
requirement: &70234275184520 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70234275184520
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: heredoc_unindent
|
71
|
+
requirement: &70234275177120 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70234275177120
|
80
|
+
description: Access YAML, JSON (and ...) configuration files with ease
|
81
|
+
email:
|
82
|
+
- pd@krh.me
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- .gitignore
|
88
|
+
- .rvmrc
|
89
|
+
- .travis.yml
|
90
|
+
- Gemfile
|
91
|
+
- README
|
92
|
+
- Rakefile
|
93
|
+
- figgy.gemspec
|
94
|
+
- lib/figgy.rb
|
95
|
+
- lib/figgy/configuration.rb
|
96
|
+
- lib/figgy/finder.rb
|
97
|
+
- lib/figgy/hash.rb
|
98
|
+
- lib/figgy/store.rb
|
99
|
+
- lib/figgy/version.rb
|
100
|
+
- spec/figgy_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
homepage: http://github.com/pd/figgy
|
103
|
+
licenses: []
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.8.10
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: Configuration file reading
|
126
|
+
test_files:
|
127
|
+
- spec/figgy_spec.rb
|
128
|
+
- spec/spec_helper.rb
|