configurative 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: efedb9a668b63966a08e43d75e84d3800e715ef0
4
+ data.tar.gz: f038eb86565e4fdbf2b08313da2f855e0b63bc6d
5
+ SHA512:
6
+ metadata.gz: 0ed9f644507c27678bb7d7e0c3e2040ccfa61227d5dc3ed1bb5ac7300ce4119efe9c38ea7b3f556641c556a6263febd507c8b13b709ef4102e35a23203b087a7
7
+ data.tar.gz: 8ebfc35fbc8fb3c2191b18c1609e20d8c2b997cdb42d85a0fcac9b099ebd38203e7d4a46aae38793bebb38328739e008909f1522e3b2ec0e9579ef026b7a087b
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in configurative.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Peter Wood
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,135 @@
1
+ # Configurative
2
+
3
+ Configurative is a library for handling basic configuration settings that was
4
+ heavily inspired by the Settingslogic library. I found Settingslogic to be an
5
+ excellent library but I just needed some extra capabilities...
6
+
7
+ * The library could use either YAML or JSON files as input.
8
+
9
+ * The library supported loading from a number of potential locations.
10
+
11
+ * The library was more accepting of failures (i.e. it would raise exceptions
12
+ in fewer cases).
13
+
14
+ * The library would allow configuration settings to be selected as a subset
15
+ of a larger file (i.e. the configuration settings could be tweaked to only
16
+ consist of the contents of a single section within a larger file).
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```ruby
23
+ gem 'configurative'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install configurative
33
+
34
+ ## Usage
35
+
36
+ The intended mechanism of usage is to derive a class from the
37
+ ```Configurative::Settings``` class. You can then customize this to have your
38
+ configuration settings come from one of a specific set of files. A basic
39
+ implementation therefore might look like this...
40
+
41
+ class Configuration < Configurative::Settings
42
+ files File.join(Dir.getwd, "config", "application.yml")
43
+ end
44
+
45
+ The files line here can accept more than a single file path and will work
46
+ through them in order until it finds a file that exists and that the code has
47
+ permission to read. This will then be loaded and its contents will become the
48
+ configuration settings.
49
+
50
+ The library supports the use of files in either the JSON or YAML formats (note
51
+ that the file extension should be either '.json' or '.yml'). The concept of
52
+ environments is also supported, with "development" being the default environment
53
+ used. You can support an alternative environment either by specifying it in
54
+ an environment variable (the library checks RAILS_ENV then RACK_ENV in that
55
+ order for the environment setting) or by specify it explicitly in the class
56
+ definition like this...
57
+
58
+ class Configuration < Configurative::Settings
59
+ files File.join(Dir.getwd, "config", "application.yml")
60
+ environment ENV["MY_ENV"]
61
+ end
62
+
63
+ If your configuration file is large and you really only want some of the
64
+ settings it contains then you can specify a subsection in your class definition
65
+ and the library will narrow the settings available to just those in the section
66
+ specified. For example, if you had a configuration file like this...
67
+
68
+ database:
69
+ user_name: db_user
70
+ password: db_password
71
+ logging:
72
+ active: true
73
+ file: ./log/application.log
74
+
75
+ Then you could extract only the entries in the logging section by defining your
76
+ class as follows...
77
+
78
+ class Configuration < Configurative::Settings
79
+ files File.join(Dir.getwd, "config", "application.yml")
80
+ section "logging"
81
+ end
82
+
83
+ Note that this setting can be used in conjunction with the environment setting
84
+ but the environment setting takes precedence. So if you specify an environment
85
+ setting of ```production``` and a section of ```logging``` then the production
86
+ section would be extracted first and then the logging section extracted from
87
+ what that returned. Note that for both the environment and section settings if
88
+ a matching subsection is not found then the library simply assumes that its
89
+ absence means that the relevant discrimator does not need to be applied.
90
+
91
+ Once you've defined your configuration class then you can access the settings
92
+ retrieved either directly through the class itself or by fetching an instance
93
+ of that class. So if you defined a setting class called Configuration and loaded
94
+ the following configuration settings...
95
+
96
+ one: 1
97
+ two: "Two"
98
+
99
+ You could access the settings in all of the following ways...
100
+
101
+ Configuration.one # = 1
102
+ Configuration[:one] # = 1
103
+ Configuration.fetch(:one) # = 1
104
+ Configuration.instance.two # = "Two"
105
+ Configuration.instance[:two] # = "Two"
106
+
107
+ If you request a setting that does not exist then the library returns ```nil```
108
+ by default. If you want to fetch something with a viable alternative in case
109
+ the original setting is not set then pass a second parameter to a call to the
110
+ ```#fetch()``` method. If you need to check whether a setting has been given a
111
+ value then use the ```#include?()``` method and pass in the setting key. Finally
112
+ if you want to check that you have at least some settings you can make a call
113
+ to the ```#empty?()``` method to determine whether or not your settings class
114
+ has anything in it.
115
+
116
+ Note that, as part of the loading process, the library converts all Hashes into
117
+ instances of the OpenStruct class and thus you can cascade down through a
118
+ settings hierarchy using the ```.``` operator rather than a dereference call.
119
+ So, for example, if you had this configuration...
120
+
121
+ one:
122
+ two:
123
+ three: 3
124
+
125
+ You could access the value of three with a call such as this...
126
+
127
+ Configuration.one.two.three
128
+
129
+ ## Contributing
130
+
131
+ 1. Fork it ( https://github.com/[my-github-username]/configurative/fork )
132
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
133
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
134
+ 4. Push to the branch (`git push origin my-new-feature`)
135
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'configurative/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "configurative"
8
+ spec.version = Configurative::VERSION
9
+ spec.authors = ["Peter Wood"]
10
+ spec.email = ["peter.wood@longboat.com"]
11
+ spec.summary = %q{A library for handling Ruby configuration settings.}
12
+ spec.description = %q{A library for handling Ruby configuration settings. Inspired by the Settingslogic library but providing some additional functionality.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.3"
24
+
25
+ spec.add_dependency "mime-types", "~> 2.6"
26
+ end
@@ -0,0 +1,14 @@
1
+ require "erb"
2
+ require "json"
3
+ require "mime/types"
4
+ require "ostruct"
5
+ require "yaml"
6
+ require "configurative/version"
7
+ require "configurative/exceptions"
8
+ require "configurative/settings_parser"
9
+ require "configurative/settings_loader"
10
+ require "configurative/settings"
11
+
12
+ module Configurative
13
+ # Your code goes here...
14
+ end
@@ -0,0 +1,14 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2015 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ module Configurative
7
+ class ConfigurationError < StandardError
8
+ def initialize(message, cause=nil)
9
+ super(message)
10
+ @cause = cause
11
+ end
12
+ attr_reader :cause
13
+ end
14
+ end
@@ -0,0 +1,166 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2015 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ module Configurative
7
+ class Settings
8
+ @@options = {}
9
+ @@instances = {}
10
+
11
+ def initialize(options={})
12
+ @settings = nil
13
+ self.class.options_for(self.class).merge!(options)
14
+ end
15
+
16
+ def self.instance
17
+ instance_for(self)
18
+ end
19
+
20
+ def [](key)
21
+ self.class[key]
22
+ end
23
+
24
+ def []=(key, value)
25
+ self.class[key] = value
26
+ end
27
+
28
+ def fetch(key, alternative=nil)
29
+ self.class.fetch(key, alternative)
30
+ end
31
+
32
+ def include?(key)
33
+ self.class.include?(key)
34
+ end
35
+
36
+ def empty?
37
+ self.class.empty?
38
+ end
39
+
40
+ def self.[](key)
41
+ instance_for(self)[key]
42
+ end
43
+
44
+ def self.[]=(key, value)
45
+ instance_for(self)[key] = value
46
+ end
47
+
48
+ def self.fetch(key, alternative=nil)
49
+ self[key] || alternative
50
+ end
51
+
52
+ def self.include?(key)
53
+ instance_for(self).include?(key)
54
+ end
55
+
56
+ def self.empty?
57
+ instance_for(self).empty?
58
+ end
59
+
60
+ def respond_to?(method_name, include_private=false)
61
+ self.class.instance_for?(self.class).include?(property_name(method_name)) || super
62
+ end
63
+
64
+ def method_missing(method_name, *arguments, &block)
65
+ self.class.method_missing(method_name, *arguments, &block) || super
66
+ end
67
+
68
+ def self.reset(full=false)
69
+ @@instances = {}
70
+ @@options.delete(self) if full
71
+ end
72
+
73
+ def self.respond_to?(method_name, include_private=false)
74
+ instance_for(self).include?(property_name(method_name)) || super
75
+ end
76
+
77
+ def self.method_missing(method_name, *arguments, &block)
78
+ data = instance_for(self)
79
+ if method_name[-1,1] == "="
80
+ data[property_name(method_name)] = arguments.first
81
+ else
82
+ if data.include?(method_name)
83
+ data[method_name]
84
+ else
85
+ super
86
+ end
87
+ end
88
+ end
89
+
90
+ def self.files(*files)
91
+ options_for(self)[:files] = files
92
+ end
93
+
94
+ def self.sources(*files)
95
+ files(*files)
96
+ end
97
+
98
+ def self.environment(setting)
99
+ options_for(self)[:environment] = setting
100
+ end
101
+
102
+ def self.namespace(setting)
103
+ environment(setting)
104
+ end
105
+
106
+ def self.section(setting)
107
+ options_for(self)[:section] = setting
108
+ end
109
+
110
+ def self.load(*files)
111
+ settings = nil
112
+ path = find_file(*files)
113
+ if path
114
+ begin
115
+ settings = SettingsLoader.new(options).load!(path)
116
+ @@instances[self] = settings
117
+ rescue => error
118
+ # Deliberately ignored.
119
+ end
120
+ end
121
+ settings
122
+ end
123
+
124
+ def self.load!(*files)
125
+ settings = load(*files)
126
+ raise ConfigurationError, "Unable to locate an accessible configuration file." if settings.nil?
127
+ settings
128
+ end
129
+
130
+ private
131
+
132
+ def options
133
+ self.class.options
134
+ end
135
+
136
+ def self.options
137
+ options_for(self)
138
+ end
139
+
140
+ def self.find_file(*paths)
141
+ paths.find {|entry| File.exist?(entry) && File.file?(entry) && File.readable?(entry)}
142
+ end
143
+
144
+ def self.instance_for(klass, defaults={})
145
+ if !@@instances.include?(klass)
146
+ files = options[:files] || []
147
+ settings = load(*files)
148
+ parser = SettingsParser.new(options_for(klass))
149
+ @@instances[klass] = settings.nil? ? parser.parse(defaults) : settings
150
+ end
151
+ @@instances[klass]
152
+ end
153
+
154
+ def self.options_for(klass)
155
+ if !@@options.include?(klass)
156
+ @@options[klass] = {environment: (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"),
157
+ section: nil}
158
+ end
159
+ @@options[klass]
160
+ end
161
+
162
+ def self.property_name(name)
163
+ (name.to_s[-1, 1] == "=" ? name.to_s[0...-1] : name.to_s).to_sym
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,53 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2015 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ module Configurative
7
+ class SettingsLoader
8
+ def initialize(options={})
9
+ @options = {}.merge(options)
10
+ end
11
+
12
+ def load!(path)
13
+ type = MIME::Types.type_for(path).first.content_type
14
+ case type
15
+ when "application/json"
16
+ load_json_file!(path)
17
+ when "text/x-yaml"
18
+ load_yaml_file!(path)
19
+ else
20
+ raise ConfigurationError, "Unsupported confguration file type '#{type}' encountered."
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def environment
27
+ (@options[:environment] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
28
+ end
29
+
30
+ def section
31
+ (@options[:section] || nil)
32
+ end
33
+
34
+ def load_yaml_file!(path)
35
+ extract_settings(YAML.load(ERB.new(File.read(path)).result))
36
+ rescue => error
37
+ raise ConfigurationError.new("Exception caught loading the '#{path}' configuration file.", error)
38
+ end
39
+
40
+ def load_json_file!(path)
41
+ extract_settings(JSON.parse(ERB.new(File.read(path)).result))
42
+ rescue => error
43
+ raise ConfigurationError.new("Exception caught loading the '#{path}' configuration file.", error)
44
+ end
45
+
46
+ def extract_settings(input)
47
+ settings = {}.merge(input)
48
+ settings = settings[environment] if settings.include?(environment)
49
+ settings = settings[section] if section && settings.include?(section)
50
+ SettingsParser.new(@options).parse(settings)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,28 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # Copyright (c), 2015 Peter Wood
4
+ # See the license.txt for details of the licensing of the code in this file.
5
+
6
+ module Configurative
7
+ class SettingsParser
8
+ def initialize(options={})
9
+ @options = {}.merge(options)
10
+ end
11
+
12
+ def parse(content)
13
+ parse_hash(content)
14
+ end
15
+
16
+ private
17
+
18
+ def parse_hash(hash)
19
+ output = hash.inject(OpenStruct.new) do |object, entry|
20
+ object[entry[0].to_sym] = (entry[1].kind_of?(Hash) ? parse_hash(entry[1]) : entry[1])
21
+ object
22
+ end
23
+ output.define_singleton_method(:empty?) {output.to_h.empty?}
24
+ output.define_singleton_method(:include?) {|key| output.to_h.include?(key)}
25
+ output
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module Configurative
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,150 @@
1
+ require "spec_helper"
2
+
3
+ describe Configurative::SettingsParser do
4
+ subject {
5
+ Configurative::SettingsLoader.new
6
+ }
7
+
8
+ describe "#load!()" do
9
+ describe "when loading a YAML file" do
10
+ let(:path) {
11
+ File.join(Dir.getwd, "spec", "data", "test.yml")
12
+ }
13
+
14
+ it "returns an OpenStruct containing the configuration data" do
15
+ output = subject.load!(path)
16
+ expect(output.class).to be(OpenStruct)
17
+ expect(output.one.two.three).to eq(3)
18
+ end
19
+ end
20
+
21
+ describe "when loading a JSON file" do
22
+ let(:path) {
23
+ File.join(Dir.getwd, "spec", "data", "test.json")
24
+ }
25
+
26
+ it "returns an OpenStruct containing the configuration data" do
27
+ output = subject.load!(path)
28
+ expect(output.class).to be(OpenStruct)
29
+ expect(output.one.two.three).to eq(3)
30
+ end
31
+ end
32
+
33
+ describe "when an alternative environment setting is available" do
34
+ before do
35
+ ENV['RACK_ENV'] = "production"
36
+ end
37
+
38
+ after do
39
+ ENV['RACK_ENV'] = nil
40
+ end
41
+
42
+ let(:path) {
43
+ File.join(Dir.getwd, "spec", "data", "environment_test.yml")
44
+ }
45
+
46
+ subject {
47
+ Configurative::SettingsLoader.new(section: "section2")
48
+ }
49
+
50
+ it "picks up the settings for the specified environment" do
51
+ output = subject.load!(path)
52
+ expect(output.one).to be_nil
53
+ expect(output.two).to be_nil
54
+ expect(output.three).to be_nil
55
+ expect(output.four).to eq(4)
56
+ expect(output.five).to eq(5)
57
+ expect(output.six).to eq(6)
58
+ end
59
+
60
+ it "still picks up the section setting" do
61
+ ENV['RACK_ENV'] = nil
62
+ output = subject.load!(path)
63
+ expect(output.one).to eq(1)
64
+ expect(output.two).to eq(2)
65
+ expect(output.three).to eq(3)
66
+ end
67
+
68
+ it "returns the entire file contents when an environment that doesn't exist is specified" do
69
+ ENV['RACK_ENV'] = "alternative"
70
+ output = subject.load!(path)
71
+ expect(output.development).not_to be_nil
72
+ expect(output.production).not_to be_nil
73
+ end
74
+ end
75
+
76
+ describe "when a section setting is specified" do
77
+ let(:path) {
78
+ File.join(Dir.getwd, "spec", "data", "section_test.yml")
79
+ }
80
+
81
+ subject {
82
+ Configurative::SettingsLoader.new(section: "section2")
83
+ }
84
+
85
+ it "returns only the specified sections contents if it exists" do
86
+ output = subject.load!(path)
87
+ expect(output.one).to be_nil
88
+ expect(output.two).to be_nil
89
+ expect(output.three).to eq(3)
90
+ expect(output.four).to eq(4)
91
+ expect(output.five).to be_nil
92
+ expect(output.six).to be_nil
93
+ end
94
+
95
+ it "returns the entire contents if a matching section does not exist" do
96
+ output = Configurative::SettingsLoader.new(section: "blah").load!(path)
97
+ expect(output.section1).not_to be_nil
98
+ expect(output.section2).not_to be_nil
99
+ expect(output.section3).not_to be_nil
100
+ end
101
+ end
102
+
103
+ it "raises an exception if given a non-existent file name" do
104
+ expect {
105
+ subject.load!("blah.yml")
106
+ }.to raise_exception(Configurative::ConfigurationError,
107
+ "Exception caught loading the 'blah.yml' configuration file.")
108
+ end
109
+
110
+ it "raises an exception if given an unsupported file type" do
111
+ expect {
112
+ subject.load!("blah.txt")
113
+ }.to raise_exception(Configurative::ConfigurationError,
114
+ "Unsupported confguration file type 'text/plain' encountered.")
115
+ end
116
+ end
117
+
118
+ describe "templated source file" do
119
+ before do
120
+ ENV["TEST_VALUE"] = "Testing Value"
121
+ end
122
+
123
+ subject {
124
+ Configurative::SettingsLoader.new(section: "section2")
125
+ }
126
+
127
+ describe "from a YAML source" do
128
+ let(:path) {
129
+ File.join(Dir.getwd, "spec", "data", "template_test.yml")
130
+ }
131
+
132
+ it "loads and parses the template correctly" do
133
+ output = subject.load!(path)
134
+ expect(output.setting).to eq("Testing Value")
135
+ end
136
+ end
137
+
138
+
139
+ describe "from a JSON source" do
140
+ let(:path) {
141
+ File.join(Dir.getwd, "spec", "data", "template_test.json")
142
+ }
143
+
144
+ it "loads and parses the template correctly" do
145
+ output = subject.load!(path)
146
+ expect(output.setting).to eq("Testing Value")
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+
3
+ describe Configurative::SettingsParser do
4
+ subject {
5
+ Configurative::SettingsParser.new
6
+ }
7
+
8
+ describe "#parse()" do
9
+ it "converts a Hash to an OpenStruct" do
10
+ output = subject.parse({"one" => 1, "two" => 2, "three" => 3})
11
+ expect(output.class).to eq(OpenStruct)
12
+ expect(output.one).to eq(1)
13
+ expect(output.two).to eq(2)
14
+ expect(output.three).to eq(3)
15
+ end
16
+
17
+ it "acts recursive on all Hashes contained within the specified one" do
18
+ output = subject.parse({one: {two: {three: 3}}})
19
+ expect(output.one.class).to eq(OpenStruct)
20
+ expect(output.one.two.class).to eq(OpenStruct)
21
+ expect(output.one.two.three).to eq(3)
22
+ end
23
+
24
+ it "adds an #empty?() method to the OpenStructs it generates" do
25
+ output = subject.parse({one: {two: {}}})
26
+ expect(output.one.empty?).to eq(false)
27
+ expect(output.one.two.empty?).to eq(true)
28
+ end
29
+
30
+ it "adds an #include?() method to the OpenStructs it generates" do
31
+ output = subject.parse({one: 1})
32
+ expect(output.include?(:one)).to eq(true)
33
+ expect(output.include?(:two)).to eq(false)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,228 @@
1
+ require "spec_helper"
2
+
3
+ describe Configurative::Settings do
4
+ before do
5
+ Configurative::Settings.reset
6
+ end
7
+
8
+ describe "when a source file is available" do
9
+ subject {
10
+ Configurative::Settings.new(files: [File.join(Dir.getwd, "spec", "data", "environment_test.yml")])
11
+ }
12
+
13
+ it "autoloads the files contents" do
14
+ expect(subject[:section1].class).to eq(OpenStruct)
15
+ expect(subject[:section2].class).to eq(OpenStruct)
16
+ expect(subject[:section3].class).to eq(OpenStruct)
17
+ end
18
+ end
19
+
20
+ describe "#[]()" do
21
+ subject {
22
+ Configurative::Settings.new
23
+ }
24
+
25
+ before do
26
+ subject.one = 1
27
+ subject.two = "Two"
28
+ subject.three = 3
29
+ end
30
+
31
+ describe "at the instance level" do
32
+ it "returns the requested value if it exists" do
33
+ expect(subject[:one]).to eq(1)
34
+ expect(subject[:two]).to eq("Two")
35
+ expect(subject[:three]).to eq(3)
36
+ end
37
+
38
+ it "returns nil if the requested value does not exist" do
39
+ expect(subject[:non_existent]).to be_nil
40
+ end
41
+ end
42
+
43
+ describe "at the class level" do
44
+ it "returns the requested value if it exists" do
45
+ expect(Configurative::Settings[:one]).to eq(1)
46
+ expect(Configurative::Settings[:two]).to eq("Two")
47
+ expect(Configurative::Settings[:three]).to eq(3)
48
+ end
49
+
50
+ it "returns nil if the requested value does not exist" do
51
+ expect(Configurative::Settings[:non_existent]).to be_nil
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#[]=()" do
57
+ subject {
58
+ Configurative::Settings.new
59
+ }
60
+
61
+ describe "at the instance level" do
62
+ it "assigns a value to a key" do
63
+ subject[:value] = "Value"
64
+ expect(subject.value).to eq("Value")
65
+ end
66
+ end
67
+
68
+
69
+ describe "at the class level" do
70
+ it "assigns a value to a key" do
71
+ Configurative::Settings[:value] = "Value"
72
+ expect(Configurative::Settings.value).to eq("Value")
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#fetch()" do
78
+ subject {
79
+ Configurative::Settings.new
80
+ }
81
+
82
+ before do
83
+ subject.one = 1
84
+ subject.two = "Two"
85
+ subject.three = 3
86
+ end
87
+
88
+ describe "at the instance level" do
89
+ it "returns the requested value if it exists" do
90
+ expect(subject.fetch(:one)).to eq(1)
91
+ expect(subject.fetch(:two)).to eq("Two")
92
+ expect(subject.fetch(:three)).to eq(3)
93
+ end
94
+
95
+ it "returns the alternative value if the requested value does not exist" do
96
+ expect(subject.fetch(:non_existent)).to be_nil
97
+ expect(subject.fetch(:non_existent, "blah")).to eq("blah")
98
+ end
99
+ end
100
+
101
+ describe "at the class level" do
102
+ it "returns the requested value if it exists" do
103
+ expect(Configurative::Settings.fetch(:one)).to eq(1)
104
+ expect(Configurative::Settings.fetch(:two)).to eq("Two")
105
+ expect(Configurative::Settings.fetch(:three)).to eq(3)
106
+ end
107
+
108
+ it "returns the alternative value if the requested value does not exist" do
109
+ expect(Configurative::Settings.fetch(:non_existent)).to be_nil
110
+ expect(Configurative::Settings.fetch(:non_existent, "blah")).to eq("blah")
111
+ end
112
+ end
113
+ end
114
+
115
+ describe "#include?()" do
116
+ subject {
117
+ Configurative::Settings.new
118
+ }
119
+
120
+ before do
121
+ subject.one = 1
122
+ end
123
+
124
+ describe "at the instance level" do
125
+ it "returns true if the specified key exists" do
126
+ expect(subject.include?(:one)).to eq(true)
127
+ end
128
+
129
+ it "returns false if the specified key does not exist" do
130
+ expect(subject.include?(:blah)).to eq(false)
131
+ end
132
+ end
133
+
134
+ describe "at the class level" do
135
+ it "returns true if the specified key exists" do
136
+ expect(Configurative::Settings.include?(:one)).to eq(true)
137
+ end
138
+
139
+ it "returns false if the specified key does not exist" do
140
+ expect(Configurative::Settings.include?(:blah)).to eq(false)
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "#empty?()" do
146
+ subject {
147
+ Configurative::Settings.new
148
+ }
149
+
150
+ before do
151
+ Configurative::Settings.reset(true)
152
+ end
153
+
154
+ describe "at the instance level" do
155
+ it "returns true if no values have been set" do
156
+ expect(subject.empty?).to eq(true)
157
+ end
158
+
159
+ it "returns false if at least one value has been set" do
160
+ subject.one = 1
161
+ expect(subject.empty?).to eq(false)
162
+ end
163
+ end
164
+
165
+ describe "at the class level" do
166
+ it "returns true if no values have been set" do
167
+ expect(Configurative::Settings.empty?).to eq(true)
168
+ end
169
+
170
+ it "returns false if at least one value has been set" do
171
+ Configurative::Settings.one = 1
172
+ expect(Configurative::Settings.empty?).to eq(false)
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "derived classes" do
178
+ describe "using the files setting" do
179
+ class TestClass1 < Configurative::Settings
180
+ files File.join(Dir.getwd, "spec", "data", "non_existent.yml"),
181
+ File.join(Dir.getwd, "spec", "data", "test.yml")
182
+ end
183
+
184
+ subject {
185
+ TestClass1
186
+ }
187
+
188
+ it "loads data from the first matching, readable file it finds" do
189
+ expect(subject.empty?).to eq(false)
190
+ expect(subject.include?(:one)).to eq(true)
191
+ end
192
+ end
193
+
194
+ describe "using the environment setting" do
195
+ class TestClass2 < Configurative::Settings
196
+ files File.join(Dir.getwd, "spec", "data", "environment_test.yml")
197
+ environment "production"
198
+ end
199
+
200
+ subject {
201
+ TestClass2
202
+ }
203
+
204
+ it "loads data from the correct section of the configuration file" do
205
+ expect(subject.empty?).to eq(false)
206
+ expect(subject.include?(:section2)).to eq(true)
207
+ end
208
+ end
209
+
210
+ describe "using the section setting" do
211
+ class TestClass3 < Configurative::Settings
212
+ files File.join(Dir.getwd, "spec", "data", "section_test.yml")
213
+ section "section3"
214
+ end
215
+
216
+ subject {
217
+ TestClass3
218
+ }
219
+
220
+ it "loads data from the correct section of the configuration file" do
221
+ expect(subject.empty?).to eq(false)
222
+ expect(subject.instance.to_h.size).to eq(2)
223
+ expect(subject.include?(:five)).to eq(true)
224
+ expect(subject.include?(:six)).to eq(true)
225
+ end
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,14 @@
1
+ development:
2
+ section1:
3
+ blah: 25
4
+ section2:
5
+ one: 1
6
+ two: 2
7
+ three: 3
8
+ section3:
9
+ ningy: "pow"
10
+ production:
11
+ section2:
12
+ four: 4
13
+ five: 5
14
+ six: 6
@@ -0,0 +1,9 @@
1
+ section1:
2
+ one: 1
3
+ two: 2
4
+ section2:
5
+ three: 3
6
+ four: 4
7
+ section3:
8
+ five: 5
9
+ six: 6
@@ -0,0 +1 @@
1
+ {"setting":"<%= ENV["TEST_VALUE"] %>"}
@@ -0,0 +1 @@
1
+ setting: <%= ENV["TEST_VALUE"] %>
@@ -0,0 +1 @@
1
+ {"one": {"two": {"three": 3}}}
@@ -0,0 +1,3 @@
1
+ one:
2
+ two:
3
+ three: 3
@@ -0,0 +1,99 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # The `.rspec` file also contains a few flags that are not defaults but that
16
+ # users commonly want.
17
+ #
18
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
+
20
+ require "configurative"
21
+
22
+ RSpec.configure do |config|
23
+ # rspec-expectations config goes here. You can use an alternate
24
+ # assertion/expectation library such as wrong or the stdlib/minitest
25
+ # assertions if you prefer.
26
+ config.expect_with :rspec do |expectations|
27
+ # This option will default to `true` in RSpec 4. It makes the `description`
28
+ # and `failure_message` of custom matchers include text for helper methods
29
+ # defined using `chain`, e.g.:
30
+ # be_bigger_than(2).and_smaller_than(4).description
31
+ # # => "be bigger than 2 and smaller than 4"
32
+ # ...rather than:
33
+ # # => "be bigger than 2"
34
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
35
+ end
36
+
37
+ # rspec-mocks config goes here. You can use an alternate test double
38
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
39
+ config.mock_with :rspec do |mocks|
40
+ # Prevents you from mocking or stubbing a method that does not exist on
41
+ # a real object. This is generally recommended, and will default to
42
+ # `true` in RSpec 4.
43
+ mocks.verify_partial_doubles = true
44
+ end
45
+
46
+ # The settings below are suggested to provide a good initial experience
47
+ # with RSpec, but feel free to customize to your heart's content.
48
+ =begin
49
+ # These two settings work together to allow you to limit a spec run
50
+ # to individual examples or groups you care about by tagging them with
51
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
52
+ # get run.
53
+ config.filter_run :focus
54
+ config.run_all_when_everything_filtered = true
55
+
56
+ # Allows RSpec to persist some state between runs in order to support
57
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
58
+ # you configure your source control system to ignore this file.
59
+ config.example_status_persistence_file_path = "spec/examples.txt"
60
+
61
+ # Limits the available syntax to the non-monkey patched syntax that is
62
+ # recommended. For more details, see:
63
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
64
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
65
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
66
+ config.disable_monkey_patching!
67
+
68
+ # This setting enables warnings. It's recommended, but in some cases may
69
+ # be too noisy due to issues in dependencies.
70
+ config.warnings = true
71
+
72
+ # Many RSpec users commonly either run the entire suite or an individual
73
+ # file, and it's useful to allow more verbose output when running an
74
+ # individual spec file.
75
+ if config.files_to_run.one?
76
+ # Use the documentation formatter for detailed output,
77
+ # unless a formatter has already been configured
78
+ # (e.g. via a command-line flag).
79
+ config.default_formatter = 'doc'
80
+ end
81
+
82
+ # Print the 10 slowest examples and example groups at the
83
+ # end of the spec run, to help surface which specs are running
84
+ # particularly slow.
85
+ config.profile_examples = 10
86
+
87
+ # Run specs in random order to surface order dependencies. If you find an
88
+ # order dependency and want to debug it, you can fix the order by providing
89
+ # the seed, which is printed after each run.
90
+ # --seed 1234
91
+ config.order = :random
92
+
93
+ # Seed global randomization in this process using the `--seed` CLI option.
94
+ # Setting this allows you to use `--seed` to deterministically reproduce
95
+ # test failures related to randomization by passing the same `--seed` value
96
+ # as the one that triggered the failure.
97
+ Kernel.srand config.seed
98
+ =end
99
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: configurative
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter Wood
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mime-types
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.6'
69
+ description: A library for handling Ruby configuration settings. Inspired by the Settingslogic
70
+ library but providing some additional functionality.
71
+ email:
72
+ - peter.wood@longboat.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - configurative.gemspec
84
+ - lib/configurative.rb
85
+ - lib/configurative/exceptions.rb
86
+ - lib/configurative/settings.rb
87
+ - lib/configurative/settings_loader.rb
88
+ - lib/configurative/settings_parser.rb
89
+ - lib/configurative/version.rb
90
+ - spec/configurative/settings_loader_spec.rb
91
+ - spec/configurative/settings_parser_spec.rb
92
+ - spec/configurative/settings_spec.rb
93
+ - spec/data/environment_test.yml
94
+ - spec/data/section_test.yml
95
+ - spec/data/template_test.json
96
+ - spec/data/template_test.yml
97
+ - spec/data/test.json
98
+ - spec/data/test.yml
99
+ - spec/spec_helper.rb
100
+ homepage: ''
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.4.5
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: A library for handling Ruby configuration settings.
124
+ test_files:
125
+ - spec/configurative/settings_loader_spec.rb
126
+ - spec/configurative/settings_parser_spec.rb
127
+ - spec/configurative/settings_spec.rb
128
+ - spec/data/environment_test.yml
129
+ - spec/data/section_test.yml
130
+ - spec/data/template_test.json
131
+ - spec/data/template_test.yml
132
+ - spec/data/test.json
133
+ - spec/data/test.yml
134
+ - spec/spec_helper.rb