figgy 0.0.1 → 0.9.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 +1 -0
- data/README.md +46 -0
- data/lib/figgy/configuration.rb +45 -2
- data/lib/figgy/finder.rb +15 -0
- data/lib/figgy/store.rb +15 -0
- data/lib/figgy/version.rb +1 -1
- data/lib/figgy.rb +24 -0
- data/spec/figgy_spec.rb +14 -0
- metadata +16 -15
- data/README +0 -21
data/.gitignore
CHANGED
data/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# figgy
|
|
2
|
+
|
|
3
|
+
Provides convenient access to configuration files in various formats, with
|
|
4
|
+
support for overriding the values based on environment, hostname, locale, or
|
|
5
|
+
any other arbitrary thing you happen to come up with.
|
|
6
|
+
|
|
7
|
+
## Travis-CI Build Status
|
|
8
|
+
[](http://travis-ci.org/pd/figgy)
|
|
9
|
+
|
|
10
|
+
## Documentation
|
|
11
|
+
[yardocs](http://rdoc.info/github/pd/figgy/master/frames)
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Just like everything else these days. In your Gemfile:
|
|
16
|
+
|
|
17
|
+
gem 'figgy'
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Set it up (say, in a Rails initializer):
|
|
22
|
+
|
|
23
|
+
AppConfig = Figgy.build do |config|
|
|
24
|
+
config.root = Rails.root.join('etc')
|
|
25
|
+
|
|
26
|
+
# config.foo is read from etc/foo.yml
|
|
27
|
+
config.define_overlay :default, nil
|
|
28
|
+
|
|
29
|
+
# config.foo is then updated with values from etc/production/foo.yml
|
|
30
|
+
config.define_overlay(:environment) { Rails.env }
|
|
31
|
+
|
|
32
|
+
# Maybe you need to load XML files?
|
|
33
|
+
config.define_handler 'xml' do |contents|
|
|
34
|
+
Hash.from_xml(contents)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
Access it as a dottable, indifferent-access hash:
|
|
39
|
+
|
|
40
|
+
AppConfig.foo.some_key
|
|
41
|
+
AppConfig["foo"]["some_key"]
|
|
42
|
+
AppConfig[:foo].some_key
|
|
43
|
+
|
|
44
|
+
## Thanks
|
|
45
|
+
|
|
46
|
+
This was written on [Enova Financial's](http://www.enovafinancial.com) dime/time.
|
data/lib/figgy/configuration.rb
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
class Figgy
|
|
2
2
|
class Configuration
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
# The directory in which to search for configuration files
|
|
4
|
+
attr_reader :root
|
|
5
5
|
|
|
6
|
+
# The list of defined overlays
|
|
7
|
+
attr_reader :overlays
|
|
8
|
+
|
|
9
|
+
# Whether to reload a configuration file each time it is accessed
|
|
10
|
+
attr_accessor :always_reload
|
|
11
|
+
|
|
12
|
+
# Whether to load all configuration files upon creation
|
|
13
|
+
# @note This does not prevent +:always_reload+ from working.
|
|
14
|
+
attr_accessor :preload
|
|
15
|
+
|
|
16
|
+
# Whether to freeze all loaded objects. Useful in production environments.
|
|
17
|
+
attr_accessor :freeze
|
|
18
|
+
|
|
19
|
+
# Constructs a new {Figgy::Configuration Figgy::Configuration} instance.
|
|
20
|
+
#
|
|
21
|
+
# By default, uses a +root+ of the current directory, and defines handlers
|
|
22
|
+
# for +.yml+, +.yaml+, +.yml.erb+, +.yaml.erb+, and +.json+.
|
|
6
23
|
def initialize
|
|
7
24
|
self.root = Dir.pwd
|
|
8
25
|
@handlers = []
|
|
@@ -29,42 +46,68 @@ class Figgy
|
|
|
29
46
|
@root = File.expand_path(path)
|
|
30
47
|
end
|
|
31
48
|
|
|
49
|
+
# @see #always_reload=
|
|
32
50
|
def always_reload?
|
|
33
51
|
!!@always_reload
|
|
34
52
|
end
|
|
35
53
|
|
|
54
|
+
# @see #preload=
|
|
36
55
|
def preload?
|
|
37
56
|
!!@preload
|
|
38
57
|
end
|
|
39
58
|
|
|
59
|
+
# @see #freeze=
|
|
40
60
|
def freeze?
|
|
41
61
|
!!@freeze
|
|
42
62
|
end
|
|
43
63
|
|
|
64
|
+
# Adds an overlay named +name+, found at +value+.
|
|
65
|
+
#
|
|
66
|
+
# If a block is given, yields to the block to determine +value+.
|
|
67
|
+
#
|
|
68
|
+
# @param name an internal name for the overlay
|
|
69
|
+
# @param value the value of the overlay
|
|
70
|
+
# @example An environment overlay
|
|
71
|
+
# config.define_overlay(:environment) { Rails.env }
|
|
44
72
|
def define_overlay(name, value = nil)
|
|
45
73
|
value = yield if block_given?
|
|
46
74
|
@overlays << [name, value]
|
|
47
75
|
end
|
|
48
76
|
|
|
77
|
+
# Adds an overlay using the combined values of other overlays.
|
|
78
|
+
#
|
|
79
|
+
# @example Searches for files in 'production_US'
|
|
80
|
+
# config.define_overlay :environment, 'production'
|
|
81
|
+
# config.define_overlay :country, 'US'
|
|
82
|
+
# config.define_combined_overlay :environment, :country
|
|
49
83
|
def define_combined_overlay(*names)
|
|
50
84
|
combined_name = names.join("_").to_sym
|
|
51
85
|
value = names.map { |name| overlay_value(name) }.join("_")
|
|
52
86
|
@overlays << [combined_name, value]
|
|
53
87
|
end
|
|
54
88
|
|
|
89
|
+
# @return [Array<String>] the list of directories to search for config files
|
|
55
90
|
def overlay_dirs
|
|
56
91
|
return [@root] if @overlays.empty?
|
|
57
92
|
overlay_values.map { |v| v ? File.join(@root, v) : @root }.uniq
|
|
58
93
|
end
|
|
59
94
|
|
|
95
|
+
# Adds a new handler for files with any extension in +extensions+.
|
|
96
|
+
#
|
|
97
|
+
# @example Adding an XML handler
|
|
98
|
+
# config.define_handler 'xml' do |body|
|
|
99
|
+
# Hash.from_xml(body)
|
|
100
|
+
# end
|
|
60
101
|
def define_handler(*extensions, &block)
|
|
61
102
|
@handlers += extensions.map { |ext| [ext, block] }
|
|
62
103
|
end
|
|
63
104
|
|
|
105
|
+
# @return [Array<String>] the list of recognized extensions
|
|
64
106
|
def extensions
|
|
65
107
|
@handlers.map { |ext, handler| ext }
|
|
66
108
|
end
|
|
67
109
|
|
|
110
|
+
# @return [Proc] the handler for a given filename
|
|
68
111
|
def handler_for(filename)
|
|
69
112
|
match = @handlers.find { |ext, handler| filename =~ /\.#{ext}$/ }
|
|
70
113
|
match && match.last
|
data/lib/figgy/finder.rb
CHANGED
|
@@ -4,6 +4,18 @@ class Figgy
|
|
|
4
4
|
@config = config
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
+
# Searches for files defining the configuration key +name+, merging each
|
|
8
|
+
# instance found with the previous. In this way, the overlay configuration
|
|
9
|
+
# at +production/foo.yml+ can override values in +foo.yml+.
|
|
10
|
+
#
|
|
11
|
+
# If the contents of the file were a Hash, Figgy will translate it into
|
|
12
|
+
# a {Figgy::Hash Figgy::Hash} and perform deep-merging for all overlays. This
|
|
13
|
+
# allows you to override only a single key deep within the configuration, and to
|
|
14
|
+
# access it using dot-notation, symbol keys or string keys.
|
|
15
|
+
#
|
|
16
|
+
# @param [String] name the configuration file to load
|
|
17
|
+
# @return Whatever was in the config file loaded
|
|
18
|
+
# @raise [Figgy::FileNotFound] if no config file could be found for +name+
|
|
7
19
|
def load(name)
|
|
8
20
|
result = files_for(name).reduce(nil) do |result, file|
|
|
9
21
|
object = @config.handler_for(file).call(File.read(file))
|
|
@@ -18,10 +30,13 @@ class Figgy
|
|
|
18
30
|
deep_freeze(to_figgy_hash(result))
|
|
19
31
|
end
|
|
20
32
|
|
|
33
|
+
# @param [String] name the configuration key to search for
|
|
34
|
+
# @return [Array<String>] the paths to all files to load for configuration key +name+
|
|
21
35
|
def files_for(name)
|
|
22
36
|
Dir[*file_globs(name)]
|
|
23
37
|
end
|
|
24
38
|
|
|
39
|
+
# @return [Array<String>] the names of all unique configuration keys
|
|
25
40
|
def all_key_names
|
|
26
41
|
Dir[*file_globs].map { |file| File.basename(file).sub(/\..+$/, '') }.uniq
|
|
27
42
|
end
|
data/lib/figgy/store.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
class Figgy
|
|
2
|
+
# The backing object for a {Figgy} instance.
|
|
2
3
|
class Store
|
|
3
4
|
def initialize(finder, config)
|
|
4
5
|
@finder = finder
|
|
@@ -6,6 +7,10 @@ class Figgy
|
|
|
6
7
|
@cache = {}
|
|
7
8
|
end
|
|
8
9
|
|
|
10
|
+
# Retrieve the value for a key, expiring the cache and/or loading it
|
|
11
|
+
# if necessary.
|
|
12
|
+
#
|
|
13
|
+
# @raise [Figgy::FileNotFound] if no config file could be found for +name+
|
|
9
14
|
def get(key)
|
|
10
15
|
key = key.to_s
|
|
11
16
|
@cache.delete(key) if @config.always_reload?
|
|
@@ -15,5 +20,15 @@ class Figgy
|
|
|
15
20
|
@cache[key] = @finder.load(key)
|
|
16
21
|
end
|
|
17
22
|
end
|
|
23
|
+
|
|
24
|
+
# @return [Array<String>] the list of currently loaded keys
|
|
25
|
+
def keys
|
|
26
|
+
@cache.keys
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Integer] the current size of the cache
|
|
30
|
+
def size
|
|
31
|
+
@cache.size
|
|
32
|
+
end
|
|
18
33
|
end
|
|
19
34
|
end
|
data/lib/figgy/version.rb
CHANGED
data/lib/figgy.rb
CHANGED
|
@@ -8,9 +8,24 @@ require "figgy/hash"
|
|
|
8
8
|
require "figgy/finder"
|
|
9
9
|
require "figgy/store"
|
|
10
10
|
|
|
11
|
+
# An instance of Figgy is the object used to provide access to your
|
|
12
|
+
# configuration files. This does very little but recognize missing
|
|
13
|
+
# methods and go look them up as a configuration key.
|
|
14
|
+
#
|
|
15
|
+
# To create a new instance, you probably want to use +Figgy.build+:
|
|
16
|
+
#
|
|
17
|
+
# MyConfig = Figgy.build do |config|
|
|
18
|
+
# config.root = '/path/to/my/configs'
|
|
19
|
+
# end
|
|
20
|
+
# MyConfig.foo.bar #=> read from /path/to/my/configs/foo.yml
|
|
21
|
+
#
|
|
22
|
+
# This should maybe be a BasicObject or similar, to provide as many
|
|
23
|
+
# available configuration keys as possible. Maybe.
|
|
11
24
|
class Figgy
|
|
12
25
|
FileNotFound = Class.new(StandardError)
|
|
13
26
|
|
|
27
|
+
# @yield [Figgy::Configuration] an object to set things up with
|
|
28
|
+
# @return [Figgy] a Figgy instance using the configuration
|
|
14
29
|
def self.build(&block)
|
|
15
30
|
config = Configuration.new
|
|
16
31
|
block.call(config)
|
|
@@ -30,4 +45,13 @@ class Figgy
|
|
|
30
45
|
def method_missing(m, *args, &block)
|
|
31
46
|
@store.get(m)
|
|
32
47
|
end
|
|
48
|
+
|
|
49
|
+
def inspect
|
|
50
|
+
if @store.size > 0
|
|
51
|
+
key_names = @store.keys.sort
|
|
52
|
+
"#<Figgy (#{@store.size} keys): #{key_names.join(' ')}>"
|
|
53
|
+
else
|
|
54
|
+
"#<Figgy (empty)>"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
33
57
|
end
|
data/spec/figgy_spec.rb
CHANGED
|
@@ -14,6 +14,20 @@ describe Figgy do
|
|
|
14
14
|
expect { test_config.values }.to raise_error(Figgy::FileNotFound)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
it "has a useful #inspect method" do
|
|
18
|
+
write_config 'values', 'foo: 1'
|
|
19
|
+
write_config 'wtf', 'bar: 2'
|
|
20
|
+
|
|
21
|
+
config = test_config
|
|
22
|
+
config.inspect.should == "#<Figgy (empty)>"
|
|
23
|
+
|
|
24
|
+
config.values
|
|
25
|
+
config.inspect.should == "#<Figgy (1 keys): values>"
|
|
26
|
+
|
|
27
|
+
config.wtf
|
|
28
|
+
config.inspect.should == "#<Figgy (2 keys): values wtf>"
|
|
29
|
+
end
|
|
30
|
+
|
|
17
31
|
context "multiple extensions" do
|
|
18
32
|
it "supports .yaml" do
|
|
19
33
|
write_config 'values.yaml', 'foo: 1'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: figgy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,11 +9,11 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2011-10-
|
|
12
|
+
date: 2011-10-22 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: json
|
|
16
|
-
requirement: &
|
|
16
|
+
requirement: &70227241849560 !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ! '>='
|
|
@@ -21,10 +21,10 @@ dependencies:
|
|
|
21
21
|
version: '0'
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *70227241849560
|
|
25
25
|
- !ruby/object:Gem::Dependency
|
|
26
26
|
name: rake
|
|
27
|
-
requirement: &
|
|
27
|
+
requirement: &70227241848860 !ruby/object:Gem::Requirement
|
|
28
28
|
none: false
|
|
29
29
|
requirements:
|
|
30
30
|
- - ! '>='
|
|
@@ -32,10 +32,10 @@ dependencies:
|
|
|
32
32
|
version: '0'
|
|
33
33
|
type: :development
|
|
34
34
|
prerelease: false
|
|
35
|
-
version_requirements: *
|
|
35
|
+
version_requirements: *70227241848860
|
|
36
36
|
- !ruby/object:Gem::Dependency
|
|
37
37
|
name: rspec
|
|
38
|
-
requirement: &
|
|
38
|
+
requirement: &70227241847880 !ruby/object:Gem::Requirement
|
|
39
39
|
none: false
|
|
40
40
|
requirements:
|
|
41
41
|
- - ! '>='
|
|
@@ -43,10 +43,10 @@ dependencies:
|
|
|
43
43
|
version: '0'
|
|
44
44
|
type: :development
|
|
45
45
|
prerelease: false
|
|
46
|
-
version_requirements: *
|
|
46
|
+
version_requirements: *70227241847880
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
48
|
name: simplecov
|
|
49
|
-
requirement: &
|
|
49
|
+
requirement: &70227241845880 !ruby/object:Gem::Requirement
|
|
50
50
|
none: false
|
|
51
51
|
requirements:
|
|
52
52
|
- - ! '>='
|
|
@@ -54,10 +54,10 @@ dependencies:
|
|
|
54
54
|
version: '0'
|
|
55
55
|
type: :development
|
|
56
56
|
prerelease: false
|
|
57
|
-
version_requirements: *
|
|
57
|
+
version_requirements: *70227241845880
|
|
58
58
|
- !ruby/object:Gem::Dependency
|
|
59
59
|
name: aruba
|
|
60
|
-
requirement: &
|
|
60
|
+
requirement: &70227241842760 !ruby/object:Gem::Requirement
|
|
61
61
|
none: false
|
|
62
62
|
requirements:
|
|
63
63
|
- - ! '>='
|
|
@@ -65,10 +65,10 @@ dependencies:
|
|
|
65
65
|
version: '0'
|
|
66
66
|
type: :development
|
|
67
67
|
prerelease: false
|
|
68
|
-
version_requirements: *
|
|
68
|
+
version_requirements: *70227241842760
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: heredoc_unindent
|
|
71
|
-
requirement: &
|
|
71
|
+
requirement: &70227241840320 !ruby/object:Gem::Requirement
|
|
72
72
|
none: false
|
|
73
73
|
requirements:
|
|
74
74
|
- - ! '>='
|
|
@@ -76,7 +76,7 @@ dependencies:
|
|
|
76
76
|
version: '0'
|
|
77
77
|
type: :development
|
|
78
78
|
prerelease: false
|
|
79
|
-
version_requirements: *
|
|
79
|
+
version_requirements: *70227241840320
|
|
80
80
|
description: Access YAML, JSON (and ...) configuration files with ease
|
|
81
81
|
email:
|
|
82
82
|
- pd@krh.me
|
|
@@ -88,7 +88,7 @@ files:
|
|
|
88
88
|
- .rvmrc
|
|
89
89
|
- .travis.yml
|
|
90
90
|
- Gemfile
|
|
91
|
-
- README
|
|
91
|
+
- README.md
|
|
92
92
|
- Rakefile
|
|
93
93
|
- figgy.gemspec
|
|
94
94
|
- lib/figgy.rb
|
|
@@ -126,3 +126,4 @@ summary: Configuration file reading
|
|
|
126
126
|
test_files:
|
|
127
127
|
- spec/figgy_spec.rb
|
|
128
128
|
- spec/spec_helper.rb
|
|
129
|
+
has_rdoc:
|
data/README
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
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]
|