yacht 0.1.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/Gemfile +5 -0
- data/README.rdoc +73 -0
- data/Rakefile +9 -0
- data/lib/monkeypatches/hash.rb +24 -0
- data/lib/yacht.rb +12 -0
- data/lib/yacht_loader/base.rb +112 -0
- data/lib/yacht_loader/classy_struct.rb +15 -0
- data/lib/yacht_loader/rails.rb +16 -0
- data/lib/yacht_loader/version.rb +3 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +115 -0
- data/spec/yacht_loader/base_spec.rb +179 -0
- data/spec/yacht_loader/classy_struct_spec.rb +39 -0
- data/spec/yacht_loader/rails_spec.rb +37 -0
- metadata +102 -0
data/README.rdoc
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
= Yacht
|
2
|
+
|
3
|
+
Yacht is an application configuration gem that lets you define settings for multiple environments in YAML files. It is similar to AppConfig[https://github.com/cjbottaro/app_config] with additional features like:
|
4
|
+
* use of ClassyStruct for improved performance over OpenStruct
|
5
|
+
* protection of sensitive settings by specifying a whitelist in a YAML file
|
6
|
+
* easy override of nested keys (not pretty with YAML references)
|
7
|
+
|
8
|
+
== Installation
|
9
|
+
|
10
|
+
* <b>Rails</b>:
|
11
|
+
Add this to your Gemfile and run the +bundle+ command.
|
12
|
+
|
13
|
+
gem "yacht"
|
14
|
+
|
15
|
+
* Outside of rails, just require the gem as usual:
|
16
|
+
|
17
|
+
require 'rubygems'
|
18
|
+
require 'yacht'
|
19
|
+
|
20
|
+
== Getting Started
|
21
|
+
|
22
|
+
=== Step 1: YAML files
|
23
|
+
|
24
|
+
First create two (or more) YAML files in the same directory to define your settings:
|
25
|
+
|
26
|
+
# config/yacht/base.yml (required)
|
27
|
+
production:
|
28
|
+
cdn_host: 1.2.3.4
|
29
|
+
super_secret_info:
|
30
|
+
aws_key: foofoo
|
31
|
+
twitter_key: barbar
|
32
|
+
test:
|
33
|
+
cdn_host: localhost
|
34
|
+
super_secret_info:
|
35
|
+
# you can safely overwrite a single value in a nested key
|
36
|
+
# YAML references (& and *) don't let you do this
|
37
|
+
# see https://gist.github.com/979804 for an explanation
|
38
|
+
aws_key: bazbaz
|
39
|
+
|
40
|
+
# config/yacht/whitelist.yml (required)
|
41
|
+
# any keys specified here can be used as a whitelist filter:
|
42
|
+
# YachtLoader.to_hash(:apply_whitelist? => true)
|
43
|
+
# (by default the whitelist is ignored)
|
44
|
+
- super_secret_info
|
45
|
+
|
46
|
+
# config/yacht/local.yml (optional)
|
47
|
+
# any values set in local.yml will override values set in base.yml
|
48
|
+
# useful for development and testing
|
49
|
+
production:
|
50
|
+
cdn_host: localhost
|
51
|
+
|
52
|
+
=== Step 2: Define a constant
|
53
|
+
|
54
|
+
* <b>Rails</b>:
|
55
|
+
# config/initializers/01_yacht.rb
|
56
|
+
# Define a constant that will store all settings
|
57
|
+
# looks for the following YAML files:
|
58
|
+
# * config/yacht/base.yml
|
59
|
+
# * config/yacht/local.yml
|
60
|
+
# * config/yacht/whitelist.yml
|
61
|
+
Yacht = YachtLoader.to_classy_struct
|
62
|
+
# now you can access any key set in your YAML files with:
|
63
|
+
# Yacht.my_key
|
64
|
+
|
65
|
+
* Outside of rails, you need to tell +YachtLoader+ where your YAML files are stored, and what environment you want to use.
|
66
|
+
YachtLoader.dir = '/path/to/YAML/dir'
|
67
|
+
YachtLoader.environment = 'my_environment'
|
68
|
+
Yacht = YachtLoader.to_classy_struct
|
69
|
+
|
70
|
+
|
71
|
+
== How it works
|
72
|
+
|
73
|
+
Currently, the +Yacht+ gem defines a class called +YachtLoader+ that will read your YAML files and output them as a regular +Hash+ or a +ClassyStruct+ . Then you use <tt>YachtLoader#to_classy_struct</tt> (or <tt>YachtLoader#to_hash</tt>) to define a constant. You can name the constant whatever you like, but you should use +Yacht+. This is because a planned feature is to have +Yacht+ defined when the gem is required and have everything namespaced under a single +Yacht+ class.
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
if !Hash.instance_methods.include?(:deep_merge)
|
2
|
+
class Hash
|
3
|
+
# copied from ActiveSupport::CoreExtensions::Hash::DeepMerge 2.3.8
|
4
|
+
def deep_merge(other_hash)
|
5
|
+
self.merge(other_hash) do |key, oldval, newval|
|
6
|
+
oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
|
7
|
+
newval = newval.to_hash if newval.respond_to?(:to_hash)
|
8
|
+
oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
if !Hash.instance_methods.include?(:slice)
|
15
|
+
class Hash
|
16
|
+
# copied from http://as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/Slice.html
|
17
|
+
def slice(*keys)
|
18
|
+
allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
19
|
+
hash = {}
|
20
|
+
allowed.each { |k| hash[k] = self[k] if has_key?(k) }
|
21
|
+
hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/yacht.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# This file is named yacht.rb
|
2
|
+
# so that Bundler will automatically load YachtLoader
|
3
|
+
#
|
4
|
+
# In the future, once we figure out how to integrate ClassyStruct,
|
5
|
+
# YachtLoader will be renamed to Yacht
|
6
|
+
|
7
|
+
require "yacht_loader/base"
|
8
|
+
require "yacht_loader/classy_struct"
|
9
|
+
require "yacht_loader/version"
|
10
|
+
|
11
|
+
require "yacht_loader/rails" if Object.const_defined?(:Rails)
|
12
|
+
require 'monkeypatches/hash'
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# TODO: Rename YachtLoader to Yacht and somehow incorporate ClassyStruct
|
2
|
+
|
3
|
+
class YachtLoader
|
4
|
+
class LoadError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def environment
|
9
|
+
@environment ||= 'default'
|
10
|
+
end
|
11
|
+
attr_writer :environment
|
12
|
+
|
13
|
+
def dir
|
14
|
+
@dir ||= File.join( File.dirname(__FILE__), 'yacht')
|
15
|
+
end
|
16
|
+
attr_writer :dir
|
17
|
+
|
18
|
+
def full_file_path_for_config(config_type)
|
19
|
+
File.join( self.dir, "#{config_type}.yml" )
|
20
|
+
end
|
21
|
+
|
22
|
+
def config_file_for(config_type)
|
23
|
+
raise LoadError.new "#{config_type} is not a valid config type" unless valid_config_types.include?(config_type.to_s)
|
24
|
+
|
25
|
+
full_file_path_for_config(config_type)
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_config_types
|
29
|
+
%w( base local whitelist )
|
30
|
+
end
|
31
|
+
|
32
|
+
def all
|
33
|
+
chain_configs(base_config, self.environment).deep_merge(local_config)
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_hash(opts={})
|
37
|
+
opts[:apply_whitelist?] = false unless opts.has_key?(:apply_whitelist?)
|
38
|
+
self.environment = opts[:env] if opts.has_key?(:env)
|
39
|
+
|
40
|
+
if opts[:apply_whitelist?]
|
41
|
+
all.slice(*whitelist)
|
42
|
+
else
|
43
|
+
all
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def whitelist
|
48
|
+
load_config_file(:whitelist, :expect_to_load => Array) || begin
|
49
|
+
raise LoadError.new("Couldn't load whitelist")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def base_config
|
54
|
+
load_config_file(:base) || begin
|
55
|
+
raise LoadError.new("Couldn't load base config")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def local_config
|
60
|
+
load_config_file(:local, :require_presence? => false) || {}
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
def load_config_file(file_type, opts={})
|
65
|
+
# by default, expect a Hash to be loaded
|
66
|
+
expected_class = opts[:expect_to_load] || Hash
|
67
|
+
|
68
|
+
# by default, raise error if file missing or empty
|
69
|
+
presence_required = if opts.has_key?(:require_presence?)
|
70
|
+
opts[:require_presence?]
|
71
|
+
else
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
file_name = self.config_file_for(file_type)
|
76
|
+
|
77
|
+
loaded = if File.exists?(file_name)
|
78
|
+
YAML.load( File.read(file_name) )
|
79
|
+
else
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# an empty YAML file will be converted to boolean false
|
84
|
+
raise LoadError.new "#{file_name} cannot be empty" if presence_required && loaded === false
|
85
|
+
|
86
|
+
# YAML contained the wrong type
|
87
|
+
raise LoadError.new "#{file_name} must contain #{expected_class} (got #{loaded.class})" if loaded && !loaded.is_a?(expected_class)
|
88
|
+
|
89
|
+
loaded
|
90
|
+
rescue => e
|
91
|
+
# don't do anything to our own custom errors
|
92
|
+
if e.is_a? YachtLoader::LoadError
|
93
|
+
raise e
|
94
|
+
else
|
95
|
+
# convert other errors to YachtLoader::LoadError
|
96
|
+
raise LoadError.new "ERROR: loading config file: '#{file_type}': #{e}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def chain_configs(config, env)
|
101
|
+
raise LoadError.new "environment '#{env}' does not exist" unless config.has_key?(env)
|
102
|
+
|
103
|
+
parent = if config[env]['_parent']
|
104
|
+
chain_configs(config, config[env]['_parent'])
|
105
|
+
else
|
106
|
+
config['default'] || {}
|
107
|
+
end
|
108
|
+
|
109
|
+
parent.deep_merge(config[env])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'classy_struct'
|
2
|
+
|
3
|
+
class YachtLoader
|
4
|
+
class << self
|
5
|
+
def classy_struct_instance
|
6
|
+
@classy_struct_instance ||= ClassyStruct.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_classy_struct(opts={})
|
10
|
+
classy_struct_instance.new( self.to_hash(opts) )
|
11
|
+
rescue StandardError => e
|
12
|
+
raise LoadError.new("Error creating ClassyStruct: #{e.message}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class YachtLoader
|
2
|
+
class << self
|
3
|
+
# use the current rails environment by default
|
4
|
+
def environment
|
5
|
+
@environment ||= Rails.env
|
6
|
+
end
|
7
|
+
|
8
|
+
def dir
|
9
|
+
@dir ||= Rails.root.join('config', 'yacht')
|
10
|
+
end
|
11
|
+
|
12
|
+
def full_file_path_for_config(config_type)
|
13
|
+
dir.join("#{config_type}.yml")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
$:.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler'
|
6
|
+
Bundler.setup
|
7
|
+
|
8
|
+
# ==============
|
9
|
+
# = SimpleCov! =
|
10
|
+
# ==============
|
11
|
+
require 'simplecov'
|
12
|
+
SimpleCov.start
|
13
|
+
|
14
|
+
require 'yacht'
|
15
|
+
|
16
|
+
Spec::Runner.configure do |config|
|
17
|
+
config.after :each do
|
18
|
+
YachtLoader.environment = nil
|
19
|
+
YachtLoader.dir = nil
|
20
|
+
YachtLoader.instance_variable_set(:@config_file_names, nil)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
BASE_CONFIG_FILE = <<EOF
|
25
|
+
default:
|
26
|
+
name: default
|
27
|
+
defaultkey: defaultvalue
|
28
|
+
dog: schnauzer
|
29
|
+
hashkey:
|
30
|
+
foo: bar
|
31
|
+
baz: wurble
|
32
|
+
xyzzy: thud
|
33
|
+
an_environment:
|
34
|
+
name: an_environment
|
35
|
+
hashkey:
|
36
|
+
baz: yay
|
37
|
+
dog: terrier
|
38
|
+
a_child_environment:
|
39
|
+
_parent: an_environment
|
40
|
+
name: a_child_environment
|
41
|
+
hashkey:
|
42
|
+
foo: kung
|
43
|
+
test:
|
44
|
+
baloney: delicious
|
45
|
+
EOF
|
46
|
+
|
47
|
+
WHITELIST_CONFIG_FILE = <<EOF
|
48
|
+
- defaultkey
|
49
|
+
- hashkey
|
50
|
+
EOF
|
51
|
+
|
52
|
+
EMPTY_WHITELIST_CONFIG_FILE = <<EOF
|
53
|
+
EOF
|
54
|
+
|
55
|
+
INVALID_WHITELIST_CONFIG_FILE = <<EOF
|
56
|
+
somenonsenseorother
|
57
|
+
EOF
|
58
|
+
|
59
|
+
LOCAL_CONFIG_FILE = <<EOF
|
60
|
+
localkey: localvalue
|
61
|
+
EOF
|
62
|
+
|
63
|
+
EMPTY_LOCAL_CONFIG_FILE = <<EOF
|
64
|
+
EOF
|
65
|
+
|
66
|
+
INVALID_LOCAL_CONFIG_FILE = <<EOF
|
67
|
+
someinvalidstufforother
|
68
|
+
EOF
|
69
|
+
|
70
|
+
|
71
|
+
# ===================================
|
72
|
+
# = Helpers to mock file operations =
|
73
|
+
# ===================================
|
74
|
+
def banish_config_file_from_prefix(prefix)
|
75
|
+
file_name = YachtLoader.config_file_for(prefix)
|
76
|
+
banish_file(file_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
# shortcut to mock config file
|
80
|
+
def conjure_config_file_from_prefix(prefix, file_contents=nil)
|
81
|
+
file_name = YachtLoader.config_file_for(prefix)
|
82
|
+
file_contents ||= "#{prefix.upcase}_CONFIG_FILE".constantize
|
83
|
+
|
84
|
+
conjure_file(file_name, file_contents)
|
85
|
+
end
|
86
|
+
|
87
|
+
def conjure_bad_config_file_from_prefix(prefix)
|
88
|
+
file_name = "#{prefix}_config_file"
|
89
|
+
file_contents = file_name.upcase.constantize
|
90
|
+
|
91
|
+
conjure_file(file_name, file_contents)
|
92
|
+
end
|
93
|
+
|
94
|
+
# mock file existence & contents
|
95
|
+
def conjure_file(file_name, file_contents)
|
96
|
+
File.stub!(:exists?).with(file_name).and_return true
|
97
|
+
File.stub!(:read).with(file_name).and_return file_contents
|
98
|
+
end
|
99
|
+
|
100
|
+
# mock file non-existence
|
101
|
+
def banish_file(file_name)
|
102
|
+
File.stub!(:exists?).with(file_name).and_return(false)
|
103
|
+
File.stub!(:read).with(file_name).and_raise Errno::ENOENT.new("No such file or directory - #{file_name}")
|
104
|
+
end
|
105
|
+
|
106
|
+
# ================================================
|
107
|
+
# = Railsy helper to turn strings into constants =
|
108
|
+
# ================================================
|
109
|
+
if !String.instance_methods.include?(:constantize)
|
110
|
+
class String
|
111
|
+
def constantize # NOT as awesome as ActiveSupport::Inflector#constantize
|
112
|
+
Object.const_get(self)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe YachtLoader do
|
4
|
+
before(:each) do
|
5
|
+
# mock existence and content of files to avoid file system in specs
|
6
|
+
%w(base whitelist local).each do |prefix|
|
7
|
+
conjure_config_file_from_prefix(prefix)
|
8
|
+
end
|
9
|
+
|
10
|
+
# stub bad config files to check error handling
|
11
|
+
%w(empty_whitelist invalid_whitelist empty_local invalid_local).each do |baddie|
|
12
|
+
conjure_bad_config_file_from_prefix(baddie)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "raises an error if base config file is missing" do
|
17
|
+
banish_config_file_from_prefix('base')
|
18
|
+
|
19
|
+
expect {
|
20
|
+
YachtLoader.to_hash
|
21
|
+
}.to raise_error( YachtLoader::LoadError, /Couldn't load base config/)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "whitelist" do
|
25
|
+
it "loads keys into an Array" do
|
26
|
+
YachtLoader.whitelist.should == ["defaultkey", "hashkey"]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns all keys by default, ignoring whitelist" do
|
30
|
+
YachtLoader.environment = 'default'
|
31
|
+
YachtLoader.to_hash.keys.should include("defaultkey", "hashkey")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "only returns keys included in whitelist when :apply_whitelist? option is true" do
|
35
|
+
YachtLoader.environment = 'default'
|
36
|
+
YachtLoader.to_hash(:apply_whitelist? => true).keys.should == ["defaultkey", "hashkey"]
|
37
|
+
end
|
38
|
+
|
39
|
+
context "config file" do
|
40
|
+
it "raises an error if missing and whitelist is applied" do
|
41
|
+
banish_config_file_from_prefix('whitelist')
|
42
|
+
|
43
|
+
expect {
|
44
|
+
YachtLoader.to_hash(:apply_whitelist? => true)
|
45
|
+
}.to raise_error( YachtLoader::LoadError, /Couldn't load whitelist/)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "raises an error if empty and whitelist is applied" do
|
49
|
+
conjure_config_file_from_prefix( :whitelist, File.read('empty_whitelist_config_file') )
|
50
|
+
|
51
|
+
expect {
|
52
|
+
YachtLoader.to_hash(:apply_whitelist? => true)
|
53
|
+
}.to raise_error( YachtLoader::LoadError, /whitelist.+ cannot be empty/)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "raises an error if invalid and whitelist is applied" do
|
57
|
+
conjure_config_file_from_prefix( :whitelist, File.read('invalid_whitelist_config_file') )
|
58
|
+
|
59
|
+
expect {
|
60
|
+
YachtLoader.to_hash(:apply_whitelist? => true)
|
61
|
+
}.to raise_error( YachtLoader::LoadError, /whitelist.+ must contain Array/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "checks environment and sets sensible defaults" do
|
67
|
+
it "sets the environment to 'default'" do
|
68
|
+
YachtLoader.environment.should == "default"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "allows setting the environment by passing an option to `to_hash`" do
|
72
|
+
YachtLoader.to_hash(:env => 'a_child_environment')
|
73
|
+
YachtLoader.environment.should == 'a_child_environment'
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
it "raises an error if an environment is requested that doesn't exist" do
|
78
|
+
expect {
|
79
|
+
YachtLoader.environment = 'nonexistent'
|
80
|
+
YachtLoader.to_hash
|
81
|
+
}.to raise_error( YachtLoader::LoadError, /does not exist/)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "merges configuration for named environment onto defaults" do
|
85
|
+
YachtLoader.environment = 'an_environment'
|
86
|
+
YachtLoader.to_hash['defaultkey'].should == 'defaultvalue'
|
87
|
+
YachtLoader.to_hash['name'].should == 'an_environment'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "handles child environment" do
|
92
|
+
before do
|
93
|
+
YachtLoader.environment = 'a_child_environment'
|
94
|
+
end
|
95
|
+
|
96
|
+
it "merges child onto the parent it names" do
|
97
|
+
YachtLoader.to_hash['dog'].should == 'terrier'
|
98
|
+
end
|
99
|
+
|
100
|
+
it "merges the hashes recursively" do
|
101
|
+
child = YachtLoader.to_hash['hashkey']
|
102
|
+
|
103
|
+
child['foo'].should == 'kung'
|
104
|
+
child['baz'].should == 'yay'
|
105
|
+
child['xyzzy'].should == 'thud'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "handling of local config file" do
|
110
|
+
before do
|
111
|
+
YachtLoader.environment = 'an_environment'
|
112
|
+
end
|
113
|
+
|
114
|
+
it "merges values onto named environment and defaults" do
|
115
|
+
YachtLoader.to_hash['defaultkey'].should == 'defaultvalue'
|
116
|
+
YachtLoader.to_hash['name'].should == 'an_environment'
|
117
|
+
YachtLoader.to_hash['localkey'].should == 'localvalue'
|
118
|
+
end
|
119
|
+
|
120
|
+
it "uses base config if missing" do
|
121
|
+
banish_config_file_from_prefix('local')
|
122
|
+
|
123
|
+
File.should_not_receive(:read).with('local_config_file')
|
124
|
+
|
125
|
+
YachtLoader.send(:local_config).should == {}
|
126
|
+
YachtLoader.to_hash['defaultkey'].should == 'defaultvalue'
|
127
|
+
YachtLoader.to_hash['name'].should == 'an_environment'
|
128
|
+
end
|
129
|
+
|
130
|
+
it "uses base config if empty" do
|
131
|
+
conjure_config_file_from_prefix(:local, File.read('empty_local_config_file'))
|
132
|
+
|
133
|
+
File.should_not_receive(:read).with('local_config_file')
|
134
|
+
|
135
|
+
YachtLoader.send(:local_config).should == {}
|
136
|
+
YachtLoader.to_hash['defaultkey'].should == 'defaultvalue'
|
137
|
+
YachtLoader.to_hash['name'].should == 'an_environment'
|
138
|
+
end
|
139
|
+
|
140
|
+
it "raises an error if invalid" do
|
141
|
+
conjure_config_file_from_prefix( :local, File.read('invalid_local_config_file') )
|
142
|
+
expect {
|
143
|
+
YachtLoader.to_hash
|
144
|
+
}.to raise_error( YachtLoader::LoadError, %r{local.yml must contain Hash})
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe :config_file_for do
|
149
|
+
it "returns the full file path for the following config files: base, local & whitelist" do
|
150
|
+
%w(base local whitelist).each do |config_file|
|
151
|
+
YachtLoader.should_receive(:full_file_path_for_config).with(config_file)
|
152
|
+
|
153
|
+
YachtLoader.config_file_for(config_file)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it "raises an error if the config file is not found" do
|
158
|
+
expect {
|
159
|
+
YachtLoader.config_file_for(:foo)
|
160
|
+
}.to raise_error( YachtLoader::LoadError, "foo is not a valid config type")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe :load_config_file do
|
165
|
+
it "loads the specified file" do
|
166
|
+
conjure_config_file_from_prefix("base", "some contents")
|
167
|
+
|
168
|
+
YachtLoader.send(:load_config_file, "base", :expect_to_load => String).should == "some contents"
|
169
|
+
end
|
170
|
+
|
171
|
+
it "raises an error if opening the file leads to an exception" do
|
172
|
+
YachtLoader.stub(:config_file_for).and_raise(StandardError.new("my_unique_error_message"))
|
173
|
+
|
174
|
+
expect {
|
175
|
+
YachtLoader.send(:load_config_file, "some file")
|
176
|
+
}.to raise_error( YachtLoader::LoadError, %r{ERROR: loading.+my_unique_error_message} )
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "to_classy_struct" do
|
4
|
+
before do
|
5
|
+
conjure_config_file_from_prefix("base")
|
6
|
+
conjure_config_file_from_prefix("local")
|
7
|
+
conjure_config_file_from_prefix("whitelist")
|
8
|
+
|
9
|
+
YachtLoader.environment = 'an_environment'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "creates a ClassyStruct based on to_hash" do
|
13
|
+
Yacht = YachtLoader.to_classy_struct
|
14
|
+
Yacht.dog.should == "terrier"
|
15
|
+
end
|
16
|
+
|
17
|
+
# ClassyStruct improves performance by adding accessors to the instance object
|
18
|
+
# If the instance is not reused, there is no advantage to ClassyStruct over OpenStruct
|
19
|
+
it "reuses the instance of ClassyStruct on subsequent calls" do
|
20
|
+
first_obj = YachtLoader.classy_struct_instance
|
21
|
+
second_obj = YachtLoader.classy_struct_instance
|
22
|
+
|
23
|
+
first_obj.object_id.should == second_obj.object_id.should
|
24
|
+
end
|
25
|
+
|
26
|
+
it "passes options to to_hash" do
|
27
|
+
YachtLoader.should_receive(:to_hash).with({:my => :awesome_option})
|
28
|
+
|
29
|
+
Yacht = YachtLoader.to_classy_struct({:my => :awesome_option})
|
30
|
+
end
|
31
|
+
|
32
|
+
it "raises a custom error if ClassyStruct cannot be created" do
|
33
|
+
YachtLoader.stub!(:to_hash).and_raise("some funky error")
|
34
|
+
|
35
|
+
expect {
|
36
|
+
Yacht = YachtLoader.to_classy_struct
|
37
|
+
}.to raise_error(YachtLoader::LoadError, /some funky error/)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Rails support" do
|
4
|
+
before do
|
5
|
+
require "yacht_loader/rails"
|
6
|
+
@yacht_dir = "/path/to/rails/config/yacht"
|
7
|
+
end
|
8
|
+
|
9
|
+
describe :environment do
|
10
|
+
before do
|
11
|
+
Rails = stub("Rails")
|
12
|
+
end
|
13
|
+
it "uses the current rails environment by default" do
|
14
|
+
Rails.should_receive(:env)
|
15
|
+
|
16
|
+
YachtLoader.environment
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe :dir do
|
21
|
+
it "uses config/yacht by default" do
|
22
|
+
Rails.stub_chain(:root, :join).and_return(@yacht_dir)
|
23
|
+
|
24
|
+
YachtLoader.dir.should == @yacht_dir
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe :full_file_path_for_config do
|
29
|
+
it "calls dir.join" do
|
30
|
+
fake_dir = stub("dir stub")
|
31
|
+
YachtLoader.stub(:dir).and_return(fake_dir)
|
32
|
+
fake_dir.should_receive(:join).with("some.yml")
|
33
|
+
|
34
|
+
YachtLoader.full_file_path_for_config("some")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yacht
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mani Tadayon
|
9
|
+
- Rico Rodriquez Collins
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
|
14
|
+
date: 2011-05-18 00:00:00 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: classy_struct
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.3.2
|
25
|
+
type: :runtime
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - "="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 1.3.1
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: simplecov
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - "="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.4.2
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id003
|
49
|
+
description:
|
50
|
+
email:
|
51
|
+
- mtadayon@atti.com
|
52
|
+
- rcollins@atti.com
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files: []
|
58
|
+
|
59
|
+
files:
|
60
|
+
- lib/monkeypatches/hash.rb
|
61
|
+
- lib/yacht.rb
|
62
|
+
- lib/yacht_loader/base.rb
|
63
|
+
- lib/yacht_loader/classy_struct.rb
|
64
|
+
- lib/yacht_loader/rails.rb
|
65
|
+
- lib/yacht_loader/version.rb
|
66
|
+
- spec/spec.opts
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
- spec/yacht_loader/base_spec.rb
|
69
|
+
- spec/yacht_loader/classy_struct_spec.rb
|
70
|
+
- spec/yacht_loader/rails_spec.rb
|
71
|
+
- Gemfile
|
72
|
+
- Rakefile
|
73
|
+
- README.rdoc
|
74
|
+
homepage: https://github.com/attinteractive/yacht
|
75
|
+
licenses: []
|
76
|
+
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.3.7
|
94
|
+
requirements: []
|
95
|
+
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 1.7.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 3
|
100
|
+
summary: Use YAML files to manage application configuration.
|
101
|
+
test_files: []
|
102
|
+
|