figgy 0.0.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/pd/figgy.png)](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]
|