configoro 1.0.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/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +92 -0
- data/LICENSE.txt +20 -0
- data/README.textile +96 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/configoro.gemspec +76 -0
- data/generators/configoro_generator.rb +17 -0
- data/lib/configoro.rb +10 -0
- data/lib/configoro/base.rb +34 -0
- data/lib/configoro/hash.rb +131 -0
- data/lib/configoro/railtie.rb +7 -0
- data/spec/configoro/hash_spec.rb +101 -0
- data/spec/configoro_spec.rb +40 -0
- data/spec/data/config/environments/common/basic.yml +3 -0
- data/spec/data/config/environments/common/hash_test.yml +5 -0
- data/spec/data/config/environments/development/basic.yml +5 -0
- data/spec/data/config/environments/development/hash_test.yml +3 -0
- data/spec/data/config/environments/production/basic.yml +3 -0
- data/spec/spec_helper.rb +23 -0
- metadata +144 -0
data/.document
ADDED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
-fs --color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
GEM
|
|
2
|
+
remote: http://rubygems.org/
|
|
3
|
+
specs:
|
|
4
|
+
RedCloth (4.2.7)
|
|
5
|
+
abstract (1.0.0)
|
|
6
|
+
actionmailer (3.0.7)
|
|
7
|
+
actionpack (= 3.0.7)
|
|
8
|
+
mail (~> 2.2.15)
|
|
9
|
+
actionpack (3.0.7)
|
|
10
|
+
activemodel (= 3.0.7)
|
|
11
|
+
activesupport (= 3.0.7)
|
|
12
|
+
builder (~> 2.1.2)
|
|
13
|
+
erubis (~> 2.6.6)
|
|
14
|
+
i18n (~> 0.5.0)
|
|
15
|
+
rack (~> 1.2.1)
|
|
16
|
+
rack-mount (~> 0.6.14)
|
|
17
|
+
rack-test (~> 0.5.7)
|
|
18
|
+
tzinfo (~> 0.3.23)
|
|
19
|
+
activemodel (3.0.7)
|
|
20
|
+
activesupport (= 3.0.7)
|
|
21
|
+
builder (~> 2.1.2)
|
|
22
|
+
i18n (~> 0.5.0)
|
|
23
|
+
activerecord (3.0.7)
|
|
24
|
+
activemodel (= 3.0.7)
|
|
25
|
+
activesupport (= 3.0.7)
|
|
26
|
+
arel (~> 2.0.2)
|
|
27
|
+
tzinfo (~> 0.3.23)
|
|
28
|
+
activeresource (3.0.7)
|
|
29
|
+
activemodel (= 3.0.7)
|
|
30
|
+
activesupport (= 3.0.7)
|
|
31
|
+
activesupport (3.0.7)
|
|
32
|
+
arel (2.0.9)
|
|
33
|
+
builder (2.1.2)
|
|
34
|
+
diff-lcs (1.1.2)
|
|
35
|
+
erubis (2.6.6)
|
|
36
|
+
abstract (>= 1.0.0)
|
|
37
|
+
git (1.2.5)
|
|
38
|
+
i18n (0.5.0)
|
|
39
|
+
jeweler (1.6.0)
|
|
40
|
+
bundler (~> 1.0.0)
|
|
41
|
+
git (>= 1.2.5)
|
|
42
|
+
rake
|
|
43
|
+
mail (2.2.19)
|
|
44
|
+
activesupport (>= 2.3.6)
|
|
45
|
+
i18n (>= 0.4.0)
|
|
46
|
+
mime-types (~> 1.16)
|
|
47
|
+
treetop (~> 1.4.8)
|
|
48
|
+
mime-types (1.16)
|
|
49
|
+
polyglot (0.3.1)
|
|
50
|
+
rack (1.2.2)
|
|
51
|
+
rack-mount (0.6.14)
|
|
52
|
+
rack (>= 1.0.0)
|
|
53
|
+
rack-test (0.5.7)
|
|
54
|
+
rack (>= 1.0)
|
|
55
|
+
rails (3.0.7)
|
|
56
|
+
actionmailer (= 3.0.7)
|
|
57
|
+
actionpack (= 3.0.7)
|
|
58
|
+
activerecord (= 3.0.7)
|
|
59
|
+
activeresource (= 3.0.7)
|
|
60
|
+
activesupport (= 3.0.7)
|
|
61
|
+
bundler (~> 1.0)
|
|
62
|
+
railties (= 3.0.7)
|
|
63
|
+
railties (3.0.7)
|
|
64
|
+
actionpack (= 3.0.7)
|
|
65
|
+
activesupport (= 3.0.7)
|
|
66
|
+
rake (>= 0.8.7)
|
|
67
|
+
thor (~> 0.14.4)
|
|
68
|
+
rake (0.8.7)
|
|
69
|
+
rspec (2.5.0)
|
|
70
|
+
rspec-core (~> 2.5.0)
|
|
71
|
+
rspec-expectations (~> 2.5.0)
|
|
72
|
+
rspec-mocks (~> 2.5.0)
|
|
73
|
+
rspec-core (2.5.2)
|
|
74
|
+
rspec-expectations (2.5.0)
|
|
75
|
+
diff-lcs (~> 1.1.2)
|
|
76
|
+
rspec-mocks (2.5.0)
|
|
77
|
+
thor (0.14.6)
|
|
78
|
+
treetop (1.4.9)
|
|
79
|
+
polyglot (>= 0.3.1)
|
|
80
|
+
tzinfo (0.3.27)
|
|
81
|
+
yard (0.6.8)
|
|
82
|
+
|
|
83
|
+
PLATFORMS
|
|
84
|
+
ruby
|
|
85
|
+
|
|
86
|
+
DEPENDENCIES
|
|
87
|
+
RedCloth
|
|
88
|
+
bundler
|
|
89
|
+
jeweler
|
|
90
|
+
rails (>= 3.0)
|
|
91
|
+
rspec
|
|
92
|
+
yard
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2011 Tim Morgan
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
h1. Configoro -- Environment-specific configuration data for Rails apps
|
|
2
|
+
|
|
3
|
+
| *Author* | Tim Morgan |
|
|
4
|
+
| *Version* | 1.0 (May 5, 2011) |
|
|
5
|
+
| *License* | Released under the MIT license. |
|
|
6
|
+
|
|
7
|
+
h2. About
|
|
8
|
+
|
|
9
|
+
Pretty much every Rails app out there needs to store environment-specific
|
|
10
|
+
configuration data: API URLs, Memcache settings, AWS keys, etc. The "quick fix"
|
|
11
|
+
approach is usually to dump this information in, e.g., @development.rb@ as
|
|
12
|
+
constants, like @MAILCHIMP_API_URL@. This creates cluttered and unorganized
|
|
13
|
+
environment files.
|
|
14
|
+
|
|
15
|
+
Configoro creates a configuration object that can be accessed as both a hash and
|
|
16
|
+
struct. It stores common configuration data merged with environment-specific
|
|
17
|
+
data.
|
|
18
|
+
|
|
19
|
+
The data is read from YAML files stored alongside the environment files.
|
|
20
|
+
|
|
21
|
+
h2. Installation
|
|
22
|
+
|
|
23
|
+
To use this gem, simply add
|
|
24
|
+
|
|
25
|
+
<pre><code>
|
|
26
|
+
gem 'configoro'
|
|
27
|
+
</code></pre>
|
|
28
|
+
|
|
29
|
+
to your Gemfile, then run
|
|
30
|
+
|
|
31
|
+
<pre><code>
|
|
32
|
+
rails generate configoro
|
|
33
|
+
</code></pre>
|
|
34
|
+
|
|
35
|
+
to install some default configuration files. Edit these new files with your
|
|
36
|
+
configuration data.
|
|
37
|
+
|
|
38
|
+
h2. Usage
|
|
39
|
+
|
|
40
|
+
Assume your application namespace is @MyApp@ (which is what it
|
|
41
|
+
would be if you had created your Rails project using @rails new my_app@). You
|
|
42
|
+
can find your namespace in your @config/application.rb@ file.
|
|
43
|
+
|
|
44
|
+
In this case, you would access your configuration using the
|
|
45
|
+
@MyApp::Configuration@ object. You can access it as an indifferent hash
|
|
46
|
+
|
|
47
|
+
<pre><code>
|
|
48
|
+
MyApp::Configuration[:mailchimp_api_url]
|
|
49
|
+
MyApp::Configuration['mailchimp_api_url']
|
|
50
|
+
</code></pre>
|
|
51
|
+
|
|
52
|
+
or as a struct.
|
|
53
|
+
|
|
54
|
+
<pre><code>
|
|
55
|
+
MyApp::Configuration.mailchimp_api_url
|
|
56
|
+
</code></pre>
|
|
57
|
+
|
|
58
|
+
If you include any hashes in your configuration YAML files, they will also be
|
|
59
|
+
accessible as indifferent hashes or structs:
|
|
60
|
+
|
|
61
|
+
<pre><code>
|
|
62
|
+
MyApp::Configuration.memcache.timeout
|
|
63
|
+
MyApp::Configuration[:memcache]['timeout']
|
|
64
|
+
MyApp::Configuration['memcache'].timeout
|
|
65
|
+
</code></pre>
|
|
66
|
+
|
|
67
|
+
h2. Configuration Files
|
|
68
|
+
|
|
69
|
+
Configuration is stored within the @config/environments@ directory of your Rails
|
|
70
|
+
app. Files ending in ".yml" are loaded from the @common/@ subdirectory and a
|
|
71
|
+
subdirectory named after the current environment.
|
|
72
|
+
|
|
73
|
+
Each file goes into its own hash in the configuration. For example, if you
|
|
74
|
+
placed a file called @memcache.yml@ within @config/environments/development@,
|
|
75
|
+
you would be able to access your Memcache timeout using
|
|
76
|
+
@MyApp::Configuration.memcache.timeout@.
|
|
77
|
+
|
|
78
|
+
h3. Custom Configuration Locations
|
|
79
|
+
|
|
80
|
+
If you need to do your own configuration loading, you can do so using the
|
|
81
|
+
{Configoro::Hash#<<} method. For example, you could place the following in a
|
|
82
|
+
Ruby file under @config/initializers@:
|
|
83
|
+
|
|
84
|
+
<pre><code>
|
|
85
|
+
MyApp::Configuration << "path/to/additional/yaml_file.yml"
|
|
86
|
+
MyApp::Configuration << { 'additional' => 'configuration' }
|
|
87
|
+
|
|
88
|
+
MyApp::Configuration.additional #=> 'configuration'
|
|
89
|
+
</code></pre>
|
|
90
|
+
|
|
91
|
+
Note that if you pass a path to a YAML file, a key will be created to store the
|
|
92
|
+
contents of the file, named after the file name. If the key already exists, the
|
|
93
|
+
new values will be deep-merged into the existing values.
|
|
94
|
+
|
|
95
|
+
In the example above, the data in the @yaml_file.yml@ file can be accessed using
|
|
96
|
+
@MyApp::Configuration.yaml_file@.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
|
|
5
|
+
#################################### BUNDLER ###################################
|
|
6
|
+
|
|
7
|
+
require 'bundler'
|
|
8
|
+
begin
|
|
9
|
+
Bundler.setup(:default, :development)
|
|
10
|
+
rescue Bundler::BundlerError => e
|
|
11
|
+
$stderr.puts e.message
|
|
12
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
13
|
+
exit e.status_code
|
|
14
|
+
end
|
|
15
|
+
require 'rake'
|
|
16
|
+
|
|
17
|
+
#################################### JEWELER ###################################
|
|
18
|
+
|
|
19
|
+
require 'jeweler'
|
|
20
|
+
Jeweler::Tasks.new do |gem|
|
|
21
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
|
22
|
+
gem.name = "configoro"
|
|
23
|
+
gem.homepage = "http://github.com/RISCfuture/configoro"
|
|
24
|
+
gem.license = "MIT"
|
|
25
|
+
gem.summary = %Q{Configuration object and YAML-based storage for Rails apps}
|
|
26
|
+
gem.description = %Q{Creates a YourApp::Configuration object whose methods are generated from environment-specific YAML files.}
|
|
27
|
+
gem.email = "git@timothymorgan.info"
|
|
28
|
+
gem.authors = [ "Tim Morgan" ]
|
|
29
|
+
# dependencies defined in Gemfile
|
|
30
|
+
end
|
|
31
|
+
Jeweler::RubygemsDotOrgTasks.new
|
|
32
|
+
|
|
33
|
+
##################################### RSPEC ####################################
|
|
34
|
+
|
|
35
|
+
require 'rspec/core'
|
|
36
|
+
require 'rspec/core/rake_task'
|
|
37
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
38
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
task :default => :spec
|
|
42
|
+
|
|
43
|
+
##################################### YARD #####################################
|
|
44
|
+
|
|
45
|
+
require 'yard'
|
|
46
|
+
YARD::Rake::YardocTask.new do |doc|
|
|
47
|
+
doc.options << "-m" << "textile"
|
|
48
|
+
doc.options << "--protected"
|
|
49
|
+
doc.options << "--no-private"
|
|
50
|
+
doc.options << "-r" << "README.textile"
|
|
51
|
+
doc.options << "-o" << "doc"
|
|
52
|
+
doc.options << "--title" << "Configoro Documentation"
|
|
53
|
+
|
|
54
|
+
doc.files = [ 'lib/**/*', 'README.textile' ]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
desc "Generate API documentation"
|
|
58
|
+
task :doc => :yard
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.0
|
data/configoro.gemspec
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{configoro}
|
|
8
|
+
s.version = "1.0.0"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["Tim Morgan"]
|
|
12
|
+
s.date = %q{2011-05-05}
|
|
13
|
+
s.description = %q{Creates a YourApp::Configuration object whose methods are generated from environment-specific YAML files.}
|
|
14
|
+
s.email = %q{git@timothymorgan.info}
|
|
15
|
+
s.extra_rdoc_files = [
|
|
16
|
+
"LICENSE.txt",
|
|
17
|
+
"README.textile"
|
|
18
|
+
]
|
|
19
|
+
s.files = [
|
|
20
|
+
".document",
|
|
21
|
+
".rspec",
|
|
22
|
+
"Gemfile",
|
|
23
|
+
"Gemfile.lock",
|
|
24
|
+
"LICENSE.txt",
|
|
25
|
+
"README.textile",
|
|
26
|
+
"Rakefile",
|
|
27
|
+
"VERSION",
|
|
28
|
+
"configoro.gemspec",
|
|
29
|
+
"generators/configoro_generator.rb",
|
|
30
|
+
"lib/configoro.rb",
|
|
31
|
+
"lib/configoro/base.rb",
|
|
32
|
+
"lib/configoro/hash.rb",
|
|
33
|
+
"lib/configoro/railtie.rb",
|
|
34
|
+
"spec/configoro/hash_spec.rb",
|
|
35
|
+
"spec/configoro_spec.rb",
|
|
36
|
+
"spec/data/config/environments/common/basic.yml",
|
|
37
|
+
"spec/data/config/environments/common/hash_test.yml",
|
|
38
|
+
"spec/data/config/environments/development/basic.yml",
|
|
39
|
+
"spec/data/config/environments/development/hash_test.yml",
|
|
40
|
+
"spec/data/config/environments/production/basic.yml",
|
|
41
|
+
"spec/spec_helper.rb"
|
|
42
|
+
]
|
|
43
|
+
s.homepage = %q{http://github.com/RISCfuture/configoro}
|
|
44
|
+
s.licenses = ["MIT"]
|
|
45
|
+
s.require_paths = ["lib"]
|
|
46
|
+
s.rubygems_version = %q{1.7.2}
|
|
47
|
+
s.summary = %q{Configuration object and YAML-based storage for Rails apps}
|
|
48
|
+
|
|
49
|
+
if s.respond_to? :specification_version then
|
|
50
|
+
s.specification_version = 3
|
|
51
|
+
|
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
|
53
|
+
s.add_runtime_dependency(%q<rails>, [">= 3.0"])
|
|
54
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
|
55
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
|
56
|
+
s.add_development_dependency(%q<RedCloth>, [">= 0"])
|
|
57
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
|
58
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
|
59
|
+
else
|
|
60
|
+
s.add_dependency(%q<rails>, [">= 3.0"])
|
|
61
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
|
62
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
|
63
|
+
s.add_dependency(%q<RedCloth>, [">= 0"])
|
|
64
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
|
65
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
s.add_dependency(%q<rails>, [">= 3.0"])
|
|
69
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
|
70
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
|
71
|
+
s.add_dependency(%q<RedCloth>, [">= 0"])
|
|
72
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'rails'
|
|
2
|
+
require 'rails/generators'
|
|
3
|
+
|
|
4
|
+
class ConfigoroGenerator < Rails::Generators::Base
|
|
5
|
+
desc "Installs example configuration files for use with Configoro"
|
|
6
|
+
|
|
7
|
+
def create_yaml_files
|
|
8
|
+
create_file "config/environments/common/example.yml",
|
|
9
|
+
{ 'common_setting' => true }.to_yaml
|
|
10
|
+
create_file "config/environments/development/example.yml",
|
|
11
|
+
{ 'environment_name' => 'Development' }.to_yaml
|
|
12
|
+
create_file "config/environments/test/example.yml",
|
|
13
|
+
{ 'environment_name' => 'Test' }.to_yaml
|
|
14
|
+
create_file "config/environments/production/example.yml",
|
|
15
|
+
{ 'environment_name' => 'Production' }.to_yaml
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/configoro.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
raise "Configoro must be used in the context of a Rails 3 application" unless defined?(Rails)
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'bundler'
|
|
5
|
+
Bundler.setup
|
|
6
|
+
|
|
7
|
+
require 'configoro/base'
|
|
8
|
+
require 'configoro/hash'
|
|
9
|
+
require 'configoro/railtie'
|
|
10
|
+
require "#{File.dirname __FILE__}/../generators/configoro_generator"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# This module handles initialization of the Configoro object, and contains some
|
|
2
|
+
# utility methods.
|
|
3
|
+
|
|
4
|
+
module Configoro
|
|
5
|
+
|
|
6
|
+
# @return [Module] The Rails application namespace; e.g., @MyApp@ for a Rails
|
|
7
|
+
# app named @MyApp::Application@.
|
|
8
|
+
|
|
9
|
+
def self.namespace
|
|
10
|
+
Object.const_get Rails.application.class.to_s.split('::').first
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Creates the configuration dictionary and stores it under
|
|
14
|
+
# @MyApp::Configuration@ (assuming an application named @MyApp@).
|
|
15
|
+
|
|
16
|
+
def self.initialize
|
|
17
|
+
namespace.const_set :Configuration, build_hash(Rails.env)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def self.build_hash(env)
|
|
23
|
+
config = Hash.new
|
|
24
|
+
|
|
25
|
+
load_data config, 'common'
|
|
26
|
+
load_data config, env
|
|
27
|
+
|
|
28
|
+
config
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.load_data(config, env)
|
|
32
|
+
Dir.glob("#{Rails.root}/config/environments/#{env}/*.yml").sort.each { |file| config << file }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
|
2
|
+
require 'active_support/core_ext/hash/deep_merge'
|
|
3
|
+
|
|
4
|
+
class Configoro::Hash < HashWithIndifferentAccess
|
|
5
|
+
|
|
6
|
+
# @private
|
|
7
|
+
def initialize(hsh={})
|
|
8
|
+
if hsh.kind_of?(::Hash) then
|
|
9
|
+
super()
|
|
10
|
+
update hsh
|
|
11
|
+
else
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Deep-merges additional hash entries into this hash.
|
|
17
|
+
#
|
|
18
|
+
# @return [Configoro::Hash] This object.
|
|
19
|
+
# @overload <<(hash)
|
|
20
|
+
# Adds the entries from another hash.
|
|
21
|
+
# @param [::Hash] hash The additional keys to add.
|
|
22
|
+
# @overload <<(path)
|
|
23
|
+
# Adds the entries from a YAML file. The entries will be added under a
|
|
24
|
+
# sub-hash named after the YAML file's name.
|
|
25
|
+
# @param [String] path The path to a YAML file ending in ".yml".
|
|
26
|
+
# @raise [ArgumentError] If the filename does not end in ".yml".
|
|
27
|
+
|
|
28
|
+
def <<(hsh_or_path)
|
|
29
|
+
case hsh_or_path
|
|
30
|
+
when String
|
|
31
|
+
raise ArgumentError, "Only files ending in .yml can be added" unless File.extname(hsh_or_path) == '.yml'
|
|
32
|
+
return self unless File.exist?(hsh_or_path)
|
|
33
|
+
data = YAML.load_file(hsh_or_path)
|
|
34
|
+
deep_merge! File.basename(hsh_or_path, ".yml") => data
|
|
35
|
+
when ::Hash
|
|
36
|
+
deep_merge! hsh_or_path
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
alias_method :push, :<<
|
|
41
|
+
|
|
42
|
+
# @private
|
|
43
|
+
def dup
|
|
44
|
+
Hash.new(self)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Recursively merges this hash with another hash. All nested hashes are forced
|
|
48
|
+
# to be @Configoro::Hash@ instances.
|
|
49
|
+
#
|
|
50
|
+
# @param [::Hash] other_hash The hash to merge into this one.
|
|
51
|
+
# @return [Configoro::Hash] This object.
|
|
52
|
+
|
|
53
|
+
def deep_merge!(other_hash)
|
|
54
|
+
other_hash.each_pair do |k, v|
|
|
55
|
+
tv = self[k]
|
|
56
|
+
self[k] = if v.kind_of?(::Hash) then
|
|
57
|
+
if tv.kind_of?(::Hash) then
|
|
58
|
+
Configoro::Hash.new(tv).deep_merge!(v)
|
|
59
|
+
else
|
|
60
|
+
Configoro::Hash.new(v)
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
v
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @private
|
|
70
|
+
#
|
|
71
|
+
# To optimize access, we create a getter method every time we encounter a
|
|
72
|
+
# key that exists in the hash. If the key is later deleted, the method will
|
|
73
|
+
# be removed.
|
|
74
|
+
|
|
75
|
+
def method_missing(meth, *args)
|
|
76
|
+
if include?(meth.to_s) then
|
|
77
|
+
if args.empty? then
|
|
78
|
+
create_getter meth
|
|
79
|
+
else
|
|
80
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 0)"
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
super
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# @private
|
|
88
|
+
def inspect
|
|
89
|
+
"#{to_hash.inspect}:#{self.class.to_s}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
protected
|
|
93
|
+
|
|
94
|
+
def self.new_from_hash_copying_default(hash)
|
|
95
|
+
Configoro::Hash.new(hash).tap do |new_hash|
|
|
96
|
+
new_hash.default = hash.default
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def convert_value(value)
|
|
101
|
+
if value.is_a?(::Hash)
|
|
102
|
+
self.class.new_from_hash_copying_default(value)
|
|
103
|
+
elsif value.is_a?(Array)
|
|
104
|
+
value.dup.replace(value.map { |e| convert_value(e) })
|
|
105
|
+
else
|
|
106
|
+
value
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def create_getter(meth)
|
|
113
|
+
singleton_class.send(:define_method, meth) do
|
|
114
|
+
if include?(meth.to_s) then
|
|
115
|
+
self[meth.to_s]
|
|
116
|
+
else
|
|
117
|
+
remove_getter meth
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
self[meth.to_s]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def remove_getter(meth)
|
|
125
|
+
if methods.include?(meth.to_sym) then
|
|
126
|
+
instance_eval "undef #{meth.to_sym.inspect}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
raise NameError, "undefined local variable or method `#{meth}' for #{self.inspect}"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Configoro::Hash do
|
|
4
|
+
subject { Configoro::Hash.new(:string => 'value', :fixnum => 123, :hash => { :foo => 'bar' }, :array => [ 1, 2, 3 ]) }
|
|
5
|
+
|
|
6
|
+
context "[getters]" do
|
|
7
|
+
it "should allow access by symbol" do
|
|
8
|
+
subject[:string].should eql('value')
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "should allow access by string" do
|
|
12
|
+
subject['fixnum'].should eql(123)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should allow access by method" do
|
|
16
|
+
subject.array.should eql([ 1, 2, 3 ])
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "[accessor methods]" do
|
|
21
|
+
it "should define an accessor method upon first access" do
|
|
22
|
+
subject.methods.should_not include(:string)
|
|
23
|
+
subject.string
|
|
24
|
+
subject.methods.should include(:string)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should remove the accessor method if the key is removed from the hash" do
|
|
28
|
+
subject.string
|
|
29
|
+
subject.methods.should include(:string)
|
|
30
|
+
subject.delete 'string'
|
|
31
|
+
proc { subject.string }.should raise_error(NameError)
|
|
32
|
+
subject.methods.should_not include(:string)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should not override existing methods" do
|
|
36
|
+
subject['inspect'] = 'wrong!'
|
|
37
|
+
subject.inspect.should_not eql('wrong!')
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe "#include?" do
|
|
42
|
+
it "should accept symbols" do
|
|
43
|
+
subject.should include(:string)
|
|
44
|
+
subject.should_not include(:string2)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should accept strings" do
|
|
48
|
+
subject.should include('fixnum')
|
|
49
|
+
subject.should_not include('fixnum2')
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "#<<" do
|
|
54
|
+
subject { Configoro::Hash.new }
|
|
55
|
+
|
|
56
|
+
it "should deep-merge entries from a hash" do
|
|
57
|
+
subject << { :a => 'b', :b => { :c => 'd' } }
|
|
58
|
+
subject << { :a => 'b', :b => { :d => 'e' } }
|
|
59
|
+
|
|
60
|
+
subject.a.should eql('b')
|
|
61
|
+
subject.b.c.should eql('d')
|
|
62
|
+
subject.b.d.should eql('e')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should load a YAML file and deep-merge its entries" do
|
|
66
|
+
subject << "#{File.dirname __FILE__}/../data/config/environments/common/hash_test.yml"
|
|
67
|
+
subject << "#{File.dirname __FILE__}/../data/config/environments/development/hash_test.yml"
|
|
68
|
+
|
|
69
|
+
subject.hash_test.akey.should eql('value')
|
|
70
|
+
subject.hash_test.subhash.key1.should eql('val1')
|
|
71
|
+
subject.hash_test.subhash.key2.should eql('newval')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "should raise an error if the file is not a YAML file" do
|
|
75
|
+
lambda { subject << "example.txt" }.should raise_error(ArgumentError)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should not change the receiver if the file doesn't exist" do
|
|
79
|
+
subject << "example.yml"
|
|
80
|
+
subject.should be_empty
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "#deep_merge!" do
|
|
85
|
+
subject { Configoro::Hash.new }
|
|
86
|
+
|
|
87
|
+
it "should merge in keys and values" do
|
|
88
|
+
subject['a'] = 'old'
|
|
89
|
+
subject.deep_merge! :a => 'new'
|
|
90
|
+
subject.a.should eql('new')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "should deep-merge sub-hashes and convert them to Configoro::Hashes" do
|
|
94
|
+
subject['hsh'] = { 'key1' => 'val1', 'key2' => 'val2' }
|
|
95
|
+
subject.deep_merge! :hsh => { 'key2' => 'newval' }
|
|
96
|
+
|
|
97
|
+
subject.hsh.key1.should eql('val1')
|
|
98
|
+
subject.hsh.key2.should eql('newval')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module MyApp; end
|
|
4
|
+
|
|
5
|
+
describe Configoro do
|
|
6
|
+
subject { MyApp::Configuration }
|
|
7
|
+
|
|
8
|
+
describe "#initialize" do
|
|
9
|
+
it "should make the configuration available to MyApp::Configuration" do
|
|
10
|
+
subject.should be_kind_of(Configoro::Hash)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should load data from the config files" do
|
|
14
|
+
subject.basic.common_only.should eql('common')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should give priority to environment-specific files" do
|
|
18
|
+
subject.basic.env_name.should eql('development')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should not load data from other environments" do
|
|
22
|
+
subject.basic['should_not_exist'].should be_nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should convert hashes recursively" do
|
|
26
|
+
subject.hash_test.akey.should eql('value')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should deep-merge hashes" do
|
|
30
|
+
subject.hash_test.subhash.key1.should eql('val1')
|
|
31
|
+
subject.hash_test.subhash.key2.should eql('newval')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should not complain when there is no directory for the current environment" do
|
|
35
|
+
Rails.stub!(:env).and_return('unknown')
|
|
36
|
+
Configoro.initialize
|
|
37
|
+
MyApp::Configuration.should eql({"basic"=>{"common_only"=>"common", "env_name"=>"common"}, "hash_test"=>{"akey"=>"value", "subhash"=>{"key1"=>"val1", "key2"=>"val2"}}})
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
3
|
+
|
|
4
|
+
require 'bundler'
|
|
5
|
+
Bundler.require :development
|
|
6
|
+
|
|
7
|
+
require 'yaml'
|
|
8
|
+
Bundler.setup
|
|
9
|
+
|
|
10
|
+
require 'configoro/base'
|
|
11
|
+
require 'configoro/hash'
|
|
12
|
+
|
|
13
|
+
# Requires supporting files with custom matchers and macros, etc,
|
|
14
|
+
# in ./support/ and its subdirectories.
|
|
15
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
|
16
|
+
|
|
17
|
+
RSpec.configure do |config|
|
|
18
|
+
config.before :each do
|
|
19
|
+
application = mock('Rails.application', :class => 'MyApp::Application')
|
|
20
|
+
::Rails = mock('Rails', :application => application, :env => 'development', :root => File.join(File.dirname(__FILE__), 'data'))
|
|
21
|
+
Configoro.initialize
|
|
22
|
+
end
|
|
23
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: configoro
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease:
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Tim Morgan
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-05-05 00:00:00 Z
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: rails
|
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
18
|
+
none: false
|
|
19
|
+
requirements:
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: "3.0"
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: *id001
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rspec
|
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
29
|
+
none: false
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: "0"
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: *id002
|
|
37
|
+
- !ruby/object:Gem::Dependency
|
|
38
|
+
name: yard
|
|
39
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
40
|
+
none: false
|
|
41
|
+
requirements:
|
|
42
|
+
- - ">="
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: "0"
|
|
45
|
+
type: :development
|
|
46
|
+
prerelease: false
|
|
47
|
+
version_requirements: *id003
|
|
48
|
+
- !ruby/object:Gem::Dependency
|
|
49
|
+
name: RedCloth
|
|
50
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
51
|
+
none: false
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: "0"
|
|
56
|
+
type: :development
|
|
57
|
+
prerelease: false
|
|
58
|
+
version_requirements: *id004
|
|
59
|
+
- !ruby/object:Gem::Dependency
|
|
60
|
+
name: bundler
|
|
61
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
62
|
+
none: false
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: "0"
|
|
67
|
+
type: :development
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: *id005
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: jeweler
|
|
72
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: "0"
|
|
78
|
+
type: :development
|
|
79
|
+
prerelease: false
|
|
80
|
+
version_requirements: *id006
|
|
81
|
+
description: Creates a YourApp::Configuration object whose methods are generated from environment-specific YAML files.
|
|
82
|
+
email: git@timothymorgan.info
|
|
83
|
+
executables: []
|
|
84
|
+
|
|
85
|
+
extensions: []
|
|
86
|
+
|
|
87
|
+
extra_rdoc_files:
|
|
88
|
+
- LICENSE.txt
|
|
89
|
+
- README.textile
|
|
90
|
+
files:
|
|
91
|
+
- .document
|
|
92
|
+
- .rspec
|
|
93
|
+
- Gemfile
|
|
94
|
+
- Gemfile.lock
|
|
95
|
+
- LICENSE.txt
|
|
96
|
+
- README.textile
|
|
97
|
+
- Rakefile
|
|
98
|
+
- VERSION
|
|
99
|
+
- configoro.gemspec
|
|
100
|
+
- generators/configoro_generator.rb
|
|
101
|
+
- lib/configoro.rb
|
|
102
|
+
- lib/configoro/base.rb
|
|
103
|
+
- lib/configoro/hash.rb
|
|
104
|
+
- lib/configoro/railtie.rb
|
|
105
|
+
- spec/configoro/hash_spec.rb
|
|
106
|
+
- spec/configoro_spec.rb
|
|
107
|
+
- spec/data/config/environments/common/basic.yml
|
|
108
|
+
- spec/data/config/environments/common/hash_test.yml
|
|
109
|
+
- spec/data/config/environments/development/basic.yml
|
|
110
|
+
- spec/data/config/environments/development/hash_test.yml
|
|
111
|
+
- spec/data/config/environments/production/basic.yml
|
|
112
|
+
- spec/spec_helper.rb
|
|
113
|
+
homepage: http://github.com/RISCfuture/configoro
|
|
114
|
+
licenses:
|
|
115
|
+
- MIT
|
|
116
|
+
post_install_message:
|
|
117
|
+
rdoc_options: []
|
|
118
|
+
|
|
119
|
+
require_paths:
|
|
120
|
+
- lib
|
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
|
+
none: false
|
|
123
|
+
requirements:
|
|
124
|
+
- - ">="
|
|
125
|
+
- !ruby/object:Gem::Version
|
|
126
|
+
hash: -2726389570801720649
|
|
127
|
+
segments:
|
|
128
|
+
- 0
|
|
129
|
+
version: "0"
|
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
|
+
none: false
|
|
132
|
+
requirements:
|
|
133
|
+
- - ">="
|
|
134
|
+
- !ruby/object:Gem::Version
|
|
135
|
+
version: "0"
|
|
136
|
+
requirements: []
|
|
137
|
+
|
|
138
|
+
rubyforge_project:
|
|
139
|
+
rubygems_version: 1.7.2
|
|
140
|
+
signing_key:
|
|
141
|
+
specification_version: 3
|
|
142
|
+
summary: Configuration object and YAML-based storage for Rails apps
|
|
143
|
+
test_files: []
|
|
144
|
+
|