yacht 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|