configurative 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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