clean_config 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YTc4YTQ0Mjg4OWIzNGJkMmViYzQwYjI4YTkyMTY0YWE2OGQ3N2QzMw==
5
+ data.tar.gz: !binary |-
6
+ NTZlYTM1M2QxNTRmYmNkMDgwNjlhYThkYTM5MmU3MGQ4ZTQzOTM5ZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZTdlY2ZkZDAyY2M2Y2I3YTcwY2M2ODJiMzlmZTYyYTljMmUwMzQzOTJkY2Jk
10
+ MmNmMjczNjgyZDI5YTczNGVhYzRhOGU2NGQ4MWNhODBhZWNkNmVmZTg2NjQy
11
+ YjY5YmZlYmM2MGEwMjkyYTVhZTRlY2I0ZWU4YTFhYjkyOTc3OTM=
12
+ data.tar.gz: !binary |-
13
+ YWExOTljY2FmNDJlZmEwNGYyOGU4MThlMzkyMzQ0Y2YwZGU0Zjk2MGRhNDBi
14
+ Nzg0ZDM5NmQwYWEwMjVkNDVkNzVkNjU3NDFiNTQ1NTQ4MmRkZDA2M2Y1NzE4
15
+ NDc0MTE1YTljZmM5ZGZlMjdmYjM0ZWYxYTY3MWY5OWE4ZjgzMmM=
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ vendor
20
+ *.log
21
+ annotations_report.html
22
+ annotations.yml
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,22 @@
1
+ #do not check for utf stamp
2
+ Encoding:
3
+ Enabled: false
4
+
5
+ LineLength:
6
+ Enabled: true
7
+ Max: 128
8
+
9
+ # The rubocop Documentation cop is a duplicate of the Reek IrresponsibleModule
10
+ # check.
11
+ Documentation:
12
+ Enabled: false
13
+
14
+ CaseIndentation:
15
+ Enabled: false
16
+
17
+ # MethodLength is superseded by Reek's TooManyStatements smell-finder.
18
+ MethodLength:
19
+ Enabled: false
20
+
21
+ CyclomaticComplexity:
22
+ Max: 10
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.1.2
5
+ - 2.2.2
6
+ script: bundle exec rake spec
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --exclude lib/clean_config/version.rb
2
+ --exclude lib/tasks.rb
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # v0.0.1
2
+ - enables `fetch`,`keys` method on recursive_open_struct
3
+ - load!, merge! and reset! convenience methods
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clean_config.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2015, Opower
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ #clean_config
2
+
3
+ A simple configuration library for Ruby projects.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'clean_config'
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install 'clean_config'
18
+
19
+ ## Usage
20
+
21
+ By convention, configuration lives in a single file: `config/config.yml`.
22
+
23
+ You can put any data you want in this file and access that data in one of three ways:
24
+
25
+ ```yaml
26
+ # config/config.yml
27
+ :foo:
28
+ :bar: 'baz'
29
+ ```
30
+
31
+ ```ruby
32
+ require 'clean_config'
33
+
34
+ class MyClass
35
+ include CleanConfig::Configurable
36
+
37
+ def initialize()
38
+ config = CleanConfig::Configuration.instance
39
+ config[:foo][:bar] # 'baz'
40
+ config.foo.bar # 'baz'
41
+ config.parse_key('foo.bar') # 'baz'
42
+ end
43
+ end
44
+ ```
45
+
46
+ ### Conventions
47
+ Loading configuration data in Ruby is easy. In fact, it is so easy that if you look at several different Ruby projects,
48
+ you'll likely find several different implementations for loading configuration. We decided to standardize how we would
49
+ load our configuration across all our gems.
50
+
51
+ This library requires your configuration be stored in a single yml file, located at
52
+
53
+ `config/config.yml`
54
+
55
+ ### Loading Configuration
56
+ Including the `Configurable` module is what initializes the `Configuration` object with the data from `config/config.yml`.
57
+ Simply include this module and your configuration will be available via `CleanConfig::Configuration.instance`.
58
+
59
+ If you are using the CleanConfig outside of a module or class, there are a few methods available to you
60
+ to point CleanConfig to your configuration directory.
61
+
62
+ `add!` allows you to change the directory/file name for your configuration files.
63
+
64
+ `load!` looks for the default directory/file name (`config/config.yml`) but at the same level as the calling code.
65
+
66
+ `merge!` allows you to pass in a hash of additional configuration to add to your CleanConfig.
67
+
68
+ ### Accessing Configuration
69
+ After `include CleanConfig::Configurable` you can access your config with: `CleanConfig::Configuration.instance`.
70
+ This will have all of your project's configuration and any configuration defined in dependencies.
71
+
72
+ To get access to the underlying `Hash` methods prefix them with `to_hash` or `to_h`, e.g. `config.to_h.values`.
73
+
74
+ Note: We have added a `#keys` method also.
75
+ As a result, if you have a field called `:keys:` in your config file the only way to access it is `config.to_h[:keys]`
76
+
77
+ ### Layering Configuration
78
+ If you depend on gems that are using clean_config, you can override their key-value pairs in your own `config/config.yml`.
79
+ Also, it's a good idea to nest your configuration under some top-level, project-specific key to prevent accidental
80
+ configuration collisions.
81
+
82
+ ## Contributing
83
+
84
+ #### Contacts
85
+ + Adrian Cazacu
86
+ + CivJ
87
+ + Crystal Hsiung
88
+
89
+ #### Process
90
+ 1. Fork it
91
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
92
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
93
+ 4. Push to the branch (`git push origin my-new-feature`)
94
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require_relative 'lib/tasks'
data/build.sh ADDED
@@ -0,0 +1,31 @@
1
+ #!/bin/bash
2
+
3
+ # Tell bash that we want the whole script to fail if any part fails.
4
+ set -e
5
+
6
+ # Global Variables
7
+ SOURCE_DIR=`pwd`
8
+
9
+ # Begin
10
+ echo "*************************************"
11
+ echo "Running build script..."
12
+ echo "PWD :"$SOURCE_DIR
13
+
14
+ main() {
15
+ echo "*************************************"
16
+ startTime=$(date +%s)
17
+
18
+ bundle install --path vendor/bundle
19
+ bundle exec rake rubocop
20
+ bundle exec rake spec:full --trace
21
+ gem build orb_configuration.gemspec
22
+
23
+ endTime=$(date +%s)
24
+ timeDifference=$(( $endTime - $startTime ))
25
+
26
+ echo "Execution time :" $timeDifference
27
+ echo "Finished."
28
+ }
29
+
30
+ # RUN
31
+ main
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/clean_config/version', __FILE__)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'clean_config'
6
+ spec.version = CleanConfig::VERSION
7
+ spec.summary = 'A simple configuration library'
8
+ spec.description = 'Provides programatic access to config/config.yml'
9
+ spec.homepage = 'http://github.com/opower/clean_config'
10
+
11
+ spec.authors = ['John Crimmins', 'Crystal Hsiung', 'Adrian Cazacu']
12
+ spec.email = ['john.crimmins@opower.com']
13
+
14
+ # This gem will work with 1.9.3 or greater...
15
+ spec.required_ruby_version = '>= 1.9.3'
16
+
17
+ spec.license = 'Apache 2'
18
+
19
+ # file attributes...
20
+ spec.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
21
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_development_dependency('rake', '~> 10.1')
26
+ spec.add_development_dependency('rspec', '~> 3.1')
27
+ spec.add_development_dependency('rspec-legacy_formatters', '~> 1.0')
28
+ spec.add_development_dependency('rspec-extra-formatters', '~> 1.0')
29
+ spec.add_development_dependency('rubocop', '= 0.26.1')
30
+ spec.add_development_dependency('fuubar', '~> 2.0')
31
+ spec.add_development_dependency('yard')
32
+ spec.add_development_dependency('redcarpet')
33
+ spec.add_development_dependency('simplecov', '~> 0.7')
34
+
35
+ spec.add_runtime_dependency('recursive-open-struct', '~> 0.5')
36
+ # We're using deep_merge which is available in all versions of activesupport so far.
37
+ spec.add_runtime_dependency('activesupport', '< 5.0')
38
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'clean_config/version'
2
+ require_relative 'clean_config/configuration'
3
+ require_relative 'clean_config/configurable'
4
+ require_relative 'clean_config/ext/recursive_open_struct'
@@ -0,0 +1,33 @@
1
+ require 'logger'
2
+ require_relative 'configuration'
3
+
4
+ module CleanConfig
5
+ # Module to help initialize and gain access to CleanConfig::Configuration object
6
+ module Configurable
7
+ class << self
8
+
9
+ # Initialize the CleanConfig::Configuration object
10
+ # Reads in from default config/config.yml file based on project root directory
11
+ def included(_parent)
12
+ log = ConfLogger.new(STDOUT)
13
+
14
+ # calling_file is the file name of the Ruby code that is our parent in the call stack.
15
+ calling_file = caller.first.split(':').first
16
+ log.debug("calling_file: #{calling_file}")
17
+ config_path = Configuration.resolve_config_path(calling_file)
18
+
19
+ if File.exist?(config_path)
20
+ Configuration.instance.add!(config_path)
21
+ log.debug("Reading configuration from #{config_path}")
22
+ begin
23
+ Configuration.instance.add!(config_path)
24
+ rescue InvalidConfigException
25
+ log.warn("Read configuration from #{config_path}, but configuration not valid. Ignoring, check your config")
26
+ end
27
+ else
28
+ log.debug("Expected config directory #{config_path} not found. Not loading configuration")
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,161 @@
1
+ require 'active_support/all'
2
+ require 'forwardable'
3
+ require 'recursive-open-struct'
4
+ require 'singleton'
5
+ require 'logger'
6
+ require 'yaml'
7
+
8
+ module CleanConfig
9
+ # Exception when file not found
10
+ FileNotFoundException = Class.new(Exception)
11
+ # Exception for configuration parsing errors
12
+ InvalidConfigException = Class.new(Exception)
13
+
14
+ # Logger that reads DEBUG environment variable to set level
15
+ class ConfLogger < Logger
16
+ def initialize(*args)
17
+ super
18
+ self.level = ENV['DEBUG'] ? Logger::DEBUG : Logger::INFO
19
+ end
20
+ end
21
+
22
+ # Provides access to configuration data
23
+ # Supports both [:property] and '.property' style access to yml configuration
24
+ # Default configuration path is `config/config.yml`
25
+ class Configuration
26
+ include ::Singleton
27
+ extend ::Forwardable
28
+
29
+ # directory where configuration yml is expected to be, relative to project root directory
30
+ DEFAULT_CONFIGURATION_DIRECTORY = 'config'
31
+
32
+ # name of configuration yml
33
+ DEFAULT_CONFIGURATION_FILE_NAME = "#{DEFAULT_CONFIGURATION_DIRECTORY}.yml"
34
+
35
+ # directories commonly found at project root directory, used to find the project root
36
+ CODE_DIRECTORIES = %w(lib spec bin)
37
+
38
+ # logger
39
+ LOG = ConfLogger.new(STDOUT)
40
+
41
+ def_delegators :@data_hash, :[]
42
+
43
+ class << self
44
+ # Finds the full path to the project's configuration file
45
+ #
46
+ # @param [String] calling_file path of the file that invoked this code
47
+ # @return [String] full path to configuration
48
+ def resolve_config_path(calling_file)
49
+ config_location = File.join(Configuration::DEFAULT_CONFIGURATION_DIRECTORY,
50
+ Configuration::DEFAULT_CONFIGURATION_FILE_NAME)
51
+
52
+ config_path = find_execution_path(calling_file)
53
+ config_path.empty? ? config_path : File.join(config_path, config_location)
54
+ end
55
+
56
+ # Finds a Ruby project's lib directory by looking for a Gemfile sibling
57
+ #
58
+ # @param [String] path The path in which to look for the project's lib directory
59
+ # @return [String] the project root directory
60
+ def find_execution_path(path)
61
+ path = File.extname(path).empty? ? path : File.dirname(path)
62
+ directories = path.split(File::Separator)
63
+ project_directory = ''
64
+
65
+ until directories.nil? || directories.empty?
66
+ if CODE_DIRECTORIES.include?(directories.last) && project_directory.empty?
67
+ directories.pop
68
+ gemfile_location = File.join(directories.join(File::Separator), 'Gemfile')
69
+ project_directory = File.dirname(gemfile_location) if File.exist?(gemfile_location)
70
+ end
71
+ directories.pop
72
+ end
73
+ project_directory
74
+ end
75
+ end
76
+
77
+ # Loads configuration relative to Ruby's execution directory.
78
+ # Useful for accessing config in tests and Rake tasks
79
+ #
80
+ # @return [Configuration] self
81
+ def load!
82
+ add!(File.join(DEFAULT_CONFIGURATION_DIRECTORY, DEFAULT_CONFIGURATION_FILE_NAME))
83
+ end
84
+
85
+ # Allows the user to specify a config file other than 'config/config.yml'
86
+ #
87
+ # @param [String] config_path provided as a convenience, but should be avoided in favor of the default location
88
+ # @return [CleanConfig::Configuration] self
89
+ def add!(config_path = nil)
90
+ calling_code_file_path = caller.first.split(':').first
91
+ config_path ||= Configuration.resolve_config_path(calling_code_file_path)
92
+ fail FileNotFoundException, "#{config_path} not found" unless File.exist?(config_path)
93
+
94
+ LOG.debug("Reading configuration from #{config_path}")
95
+ config_hash = YAML.load_file(config_path)
96
+ fail InvalidConfigException, "YAML unable to parse empty #{config_path}" unless config_hash # empty YAML returns false
97
+
98
+ merge!(config_hash)
99
+ end
100
+
101
+ # Set and merge config at runtime without a file
102
+ #
103
+ # @param [Hash] config data to add to configuration
104
+ # @return [CleanConfig::Configuration] self
105
+ def merge!(config = {})
106
+ @data_hash = @data_hash.nil? ? config : @data_hash.deep_merge(config)
107
+ @data_model = RecursiveOpenStruct.new(@data_hash, recurse_over_arrays: true)
108
+ self
109
+ end
110
+
111
+ # Given a period-delimited string of keys, find the nested value stored in the configuration
112
+ #
113
+ # @param [String] config_key period-delimited string of nested keys
114
+ # @return [Object] value retrieved
115
+ def parse_key(config_key)
116
+ fail 'config_key required' if config_key.nil? || config_key.empty?
117
+ parse_key_recursive(@data_model, config_key.split('.').reverse, '')
118
+ end
119
+
120
+ # Pass along methods not recognized to the underlying data structure
121
+ def method_missing(method, *args, &block)
122
+ @data_model.send(method, *args, &block)
123
+ end
124
+
125
+ # Returns whether the configuration is empty or not
126
+ #
127
+ # @return [Boolean] true if configuration is empty
128
+ def empty?
129
+ @data_model.nil?
130
+ end
131
+
132
+ # Clear configuration
133
+ #
134
+ # @return [CleanConfig::Configuration] self
135
+ def reset!
136
+ @data_model = nil
137
+ @data_hash = nil
138
+ self
139
+ end
140
+
141
+ private
142
+
143
+ # For nested configurations, recursively look for the key
144
+ #
145
+ # @param [Object] data_model configuration object to look for key in
146
+ # @param [String] config_keys period-delimited string of keys to look for
147
+ # @param [String] key_message period-delimited string of keys we are nested in, used for debug output
148
+ # @return [Object] value
149
+ def parse_key_recursive(data_model, config_keys, key_message)
150
+ if config_keys.length > 0
151
+ config_key = config_keys.pop
152
+ key_message << config_key
153
+ configuration = data_model.send(config_key.to_sym)
154
+ fail "config_key #{key_message} has no defined value" unless configuration
155
+ parse_key_recursive(configuration, config_keys, key_message << '.')
156
+ else
157
+ data_model
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,16 @@
1
+ # Monkeypatching RecursiveOpenStruct to respond to common Hash methods
2
+ class RecursiveOpenStruct < OpenStruct
3
+ # Returns array with keys from the RecursiveOpenStruct
4
+ # @return [Array] keys
5
+ def keys
6
+ to_h.keys
7
+ end
8
+
9
+ # Returns value for given key
10
+ # @param [Object] key key to retrieve value
11
+ # @param [Object] default value to return if key is not found
12
+ # @return [Object] value associated with given key
13
+ def fetch(key, default = nil)
14
+ to_h.fetch(key, default)
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module CleanConfig
2
+ VERSION = '0.0.2'
3
+ end
data/lib/tasks.rb ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env rake
2
+ # Please note this file does not use the test_support gem because test_support depends on orb_configuration.
3
+ # As a work-around to avoid a circular-dependency, we're rolling our own task code here.
4
+ # This is a special case. The vast majority of projects should be using test_support to provide Rspec tasks.
5
+ require 'rspec'
6
+ require 'rspec/core/rake_task'
7
+ require 'rspec-extra-formatters'
8
+ require 'fuubar'
9
+ require 'rubocop/rake_task'
10
+ require 'logger'
11
+
12
+ TMP_DIR = 'tmp'
13
+ LOG = Logger.new(open('rake.log', File::WRONLY | File::APPEND | File::CREAT))
14
+
15
+ desc 'Run RuboCop on lib and spec directories, Gemfile, gemspec, Rakefile; Override with a space delimited list'
16
+ RuboCop::RakeTask.new(:rubocop, :pattern) do |task, args|
17
+ task.patterns = ['lib/**/*.rb', 'spec/**/*.rb', 'Gemfile', '*.gemspec', 'Rakefile']
18
+ task.patterns = args[:pattern].split(' ') if args[:pattern] # override default pattern
19
+ LOG.info("Running RuboCop against #{task.patterns}")
20
+ end
21
+
22
+ def timestamp
23
+ Time.now.strftime('%Y%m%d_%H%M%S')
24
+ end
25
+
26
+ def build_rspec_opts(test_type, task)
27
+ task.rspec_opts = ['-c']
28
+ task.rspec_opts << '--require' << 'fuubar'
29
+ task.rspec_opts << '--format' << Fuubar
30
+ task.rspec_opts << '--require' << 'rspec/legacy_formatters'
31
+ task.rspec_opts << '--require' << 'rspec-extra-formatters'
32
+ task.rspec_opts << '--format' << JUnitFormatter
33
+ task.rspec_opts << '--out' << "tmp/results/#{test_type}/#{timestamp}_results.xml"
34
+ task.rspec_opts << '--format' << 'html'
35
+ task.rspec_opts << '--out' << "tmp/results/#{test_type}/#{timestamp}_results.html"
36
+ task.pattern = "spec/#{test_type}/**/*_spec.rb"
37
+ end
38
+
39
+ namespace :clean do
40
+ desc 'Remove all tmp files'
41
+ task :tmp do
42
+ LOG.info("Deleting the #{TMP_DIR} directory...")
43
+ FileUtils.rm_rf(TMP_DIR)
44
+ LOG.info('Deleted.')
45
+ end
46
+ end
47
+
48
+ namespace :spec do
49
+ RSpec::Core::RakeTask.new(:unit) do |task|
50
+ FileUtils.mkdir_p(TMP_DIR)
51
+ build_rspec_opts('unit', task)
52
+ end
53
+
54
+ RSpec::Core::RakeTask.new(:e2e) do |task|
55
+ FileUtils.mkdir_p(TMP_DIR)
56
+ build_rspec_opts('e2e', task)
57
+ end
58
+
59
+ desc 'Run all tests'
60
+ task full: %w(clean:tmp spec:unit spec:e2e)
61
+ end
62
+
63
+ LOG.close
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor
19
+ *.log
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Settings><!--This file was automatically generated by Ruby plugin.
3
+ You are allowed to:
4
+ 1. Remove rake task
5
+ 2. Add existing rake tasks
6
+ To add existing rake tasks automatically delete this file and reload the project.
7
+ --><RakeGroup description="" fullCmd="" taksId="rake" /></Settings>
@@ -0,0 +1 @@
1
+ --require spec_helper
@@ -0,0 +1,22 @@
1
+ #do not check for utf stamp
2
+ Encoding:
3
+ Enabled: false
4
+
5
+ LineLength:
6
+ Enabled: true
7
+ Max: 128
8
+
9
+ # The rubocop Documentation cop is a duplicate of the Reek IrresponsibleModule
10
+ # check.
11
+ Documentation:
12
+ Enabled: false
13
+
14
+ CaseIndentation:
15
+ Enabled: false
16
+
17
+ # MethodLength is superseded by Reek's TooManyStatements smell-finder.
18
+ MethodLength:
19
+ Enabled: false
20
+
21
+ CyclomaticComplexity:
22
+ Max: 10
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ # Specify your gem's dependencies in core.gemspec
4
+ gemspec
5
+ gem 'clean_config', :path => '../../..'
@@ -0,0 +1 @@
1
+ require_relative 'lib/tasks'
@@ -0,0 +1,3 @@
1
+ :data:
2
+ :core: core value
3
+ :foo: foo value
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/core/version', __FILE__)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'core'
6
+ spec.version = Core::VERSION
7
+ spec.summary = 'TODO: Write a short summary. Required.'
8
+ spec.description = 'TODO: Write a longer description. Optional.'
9
+ spec.homepage = 'http://github.com/your-GH-name-here/core'
10
+
11
+ spec.authors = ['foo']
12
+ spec.email = ['your-email-here@email.com']
13
+
14
+ # This gem will work with 1.9.3 or greater...
15
+ spec.required_ruby_version = '>= 1.9.3'
16
+
17
+ spec.license = 'MIT'
18
+
19
+ # file attributes...
20
+ spec.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
21
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
23
+ spec.require_paths = ['lib']
24
+
25
+ spec.add_development_dependency('rake', '~> 10.1')
26
+ spec.add_development_dependency('rspec', ' 2.14.1')
27
+ spec.add_development_dependency('rspec-extra-formatters', ' 0.4')
28
+ spec.add_development_dependency('rubocop', ' 0.24.0')
29
+ spec.add_development_dependency('simplecov', ' 0.7.1')
30
+ spec.add_development_dependency('rspec', '~> 3.1')
31
+ end
@@ -0,0 +1,2 @@
1
+ require_relative 'core/version'
2
+ require_relative 'core/core_one'
@@ -0,0 +1,13 @@
1
+ require 'clean_config'
2
+
3
+ module Core
4
+ class CoreOne
5
+ include CleanConfig::Configurable
6
+
7
+ attr_reader :config
8
+
9
+ def initialize
10
+ @config = CleanConfig::Configuration.instance
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module Core
2
+ VERSION = '0.0.1'
3
+ ORB_ARCHETYPER = '0.0.4'
4
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+ require 'rubocop/rake_task'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Run RuboCop on lib and spec directories, Gemfile, gemspec, Rakefile; Override with a space delimited list'
6
+ RuboCop::RakeTask.new(:rubocop, :pattern) do |task, args|
7
+ task.patterns = ['lib/**/*.rb', 'spec/**/*.rb', 'Gemfile', '*.gemspec', 'Rakefile']
8
+ task.patterns = args[:pattern].split(' ') if args[:pattern] # override default pattern
9
+ # don't abort rake on failure
10
+ task.fail_on_error = false
11
+ end
12
+
13
+ namespace :spec do
14
+ RSpec::Core::RakeTask.new(:unit) do |task|
15
+ task.pattern = 'spec/**/*_spec.rb'
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ # TODO: make spec_helper_gem and spec_helper_test. Take coverage out of test.
2
+ require 'simplecov'
3
+
4
+ SimpleCov.start do
5
+ coverage_dir 'tmp/coverage/unit'
6
+ add_filter 'tmp/'
7
+ add_filter 'spec/'
8
+ end
9
+
10
+ RSpec.configure do |config|
11
+ # Only accept expect syntax do not allow old should syntax
12
+ config.expect_with :rspec do |c|
13
+ c.syntax = :expect
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ require_relative '../../lib/core/core_one'
2
+
3
+ module Core
4
+ describe CoreOne do
5
+ context 'layers config' do
6
+ it 'reads configs' do
7
+ core_one = CoreOne.new
8
+ expect(core_one.config.data.core).to eq('core value')
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ require 'English'
2
+
3
+ module CleanConfig
4
+ describe Configuration do
5
+ it 'should load correct config when required in a different gem' do
6
+ Dir.chdir(File.join('spec', 'e2e', 'core')) do
7
+ Bundler.with_clean_env do
8
+ `bundle install && bundle exec rake spec:unit`
9
+ end
10
+ end
11
+ expect($CHILD_STATUS.success?).to be true
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ :test:
2
+ :foo: bar
3
+ other_test: 123
4
+ :merge_test:
5
+ :foo: baz
File without changes
@@ -0,0 +1,14 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start do
4
+ coverage_dir 'tmp/coverage/unit'
5
+ add_filter 'tmp/'
6
+ add_filter 'spec/'
7
+ end
8
+
9
+ RSpec.configure do |config|
10
+ # Only accept expect syntax do not allow old should syntax
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+ end
@@ -0,0 +1,140 @@
1
+ require_relative '../../lib/clean_config/configuration'
2
+
3
+ module CleanConfig
4
+ describe Configuration do
5
+ context 'singleton creation' do
6
+ it 'only creates one instance' do
7
+ config = Configuration.instance
8
+ expect(config).to eql(Configuration.instance)
9
+ end
10
+
11
+ it '#new throws error' do
12
+ expect { Configuration.new }.to raise_error(NoMethodError)
13
+ end
14
+ end
15
+
16
+ context 'instance methods' do
17
+ let(:config_path) { File.join(File.dirname(__FILE__), '..', 'fixtures') }
18
+ let(:config) do
19
+ config = Configuration.instance
20
+ config.add!(File.join(config_path, 'config.yml'))
21
+ config
22
+ end
23
+
24
+ it '#load! loads the top-level config.yml' do
25
+ stub_const('CleanConfig::Configuration::DEFAULT_CONFIGURATION_DIRECTORY', config_path)
26
+ config = Configuration.instance
27
+ config.load!
28
+ expect(config.test.foo).to eq('bar')
29
+ end
30
+
31
+ it '#add! fails if YAML file is empty' do
32
+ config_path = File.join(File.dirname(__FILE__), '..', 'fixtures', 'empty.yml')
33
+ expect { config.add!(config_path) }.to raise_error("YAML unable to parse empty #{config_path}")
34
+ end
35
+
36
+ it '#merge! sets config as expected' do
37
+ expect(config.merge_test.foo).to eq('baz')
38
+ config.merge!(merge_test: { foo: 'asdf' })
39
+ expect(config.merge_test.foo).to eq('asdf')
40
+ end
41
+
42
+ context 'delegated methods' do
43
+ it '#[] accepts a symbol and provides access to config data' do
44
+ expect(config[:test][:foo]).to eq('bar')
45
+ end
46
+
47
+ it '#[] accepts a string and provides access to config data' do
48
+ expect(config['other_test']).to eq(123)
49
+ end
50
+ end
51
+
52
+ context 'config properties are methods' do
53
+ it '#method_missing forwards methods to internal RecursiveStruct' do
54
+ expect(config.test.foo).to eq('bar')
55
+ end
56
+
57
+ it '#method_missing returns nil when internal RecursiveStruct has no associated property' do
58
+ expect(config.fake).to be_nil
59
+ end
60
+ end
61
+
62
+ context 'configuration string parsing' do
63
+ it '#parse_config_key returns the correct property value for the given string' do
64
+ expect(config.parse_key('test.foo')).to eq('bar')
65
+ expect(config.parse_key('test').foo).to eq('bar')
66
+ end
67
+ it '#parse_config_key throws an error for nonexistant keys' do
68
+ # Providing 'fail' does not throw the error I expect. I am not sure why.
69
+ # expect(config.parse_config_key('fail')).to eq('bar')
70
+ expect { config.parse_key('nope') }.to raise_error(RuntimeError)
71
+ expect { config.parse_key('nope.again') }.to raise_error(RuntimeError)
72
+ end
73
+ end
74
+
75
+ context 'read config file' do
76
+ it 'add! throws FileNotFoundException if there is no file.' do
77
+ expect { config.add!('') }.to raise_error(FileNotFoundException)
78
+ end
79
+ end
80
+
81
+ it '#empty? should return false if config file was read.' do
82
+ expect(config.empty?).to be false
83
+ end
84
+
85
+ it '#empty? should return true if no config file was read.' do
86
+ config.reset!
87
+ expect(config.empty?).to be true
88
+ end
89
+
90
+ end
91
+
92
+ context 'class methods' do
93
+ let(:var_lib_jenkins_foo) { '/var/lib/jenkins/foo' }
94
+ let(:var_jenkins_foo) { '/var/jenkins/foo' }
95
+
96
+ it '#find_execution_dir finds a config location when code is in a bin directory' do
97
+ # We use `File.exist?` in configuration.rb to check if we have a Gemfile.
98
+ allow(File).to receive(:exist?).and_return(true)
99
+ expect(Configuration.find_execution_path('/var/lib/jenkins/foo/bin/file.rb')).to eq(var_lib_jenkins_foo)
100
+ end
101
+
102
+ it '#find_execution_dir does not find a config when none exists under a lib directory' do
103
+ allow(File).to receive(:exist?).and_return(false, false)
104
+ expect(Configuration.find_execution_path('/var/lib/jenkins/foo/lib/file.rb')).to be_empty
105
+ end
106
+
107
+ it '#find_execution_dir finds config location with nested libs' do
108
+ allow(File).to receive(:exist?).and_return(true)
109
+ expect(Configuration.find_execution_path('/var/lib/jenkins/foo/lib/file.rb')).to eq(var_lib_jenkins_foo)
110
+ end
111
+
112
+ it '#find_execution_dir finds config location with nested specs' do
113
+ allow(File).to receive(:exist?).and_return(false, true)
114
+ expect(Configuration.find_execution_path('/var/lib/jenkins/foo/spec/smoke/spec'))
115
+ .to eq(var_lib_jenkins_foo)
116
+ end
117
+
118
+ it '#find_execution_dir finds config location when calling code is deep under lib' do
119
+ allow(File).to receive(:exist?).and_return(true)
120
+ expect(Configuration.find_execution_path('/var/lib/jenkins/foo/lib/one/two/three/file.rb'))
121
+ .to eq(var_lib_jenkins_foo)
122
+ end
123
+
124
+ it '#find_execution_dir finds config location with spec nested under lib' do
125
+ allow(File).to receive(:exist?).and_return(true)
126
+ expect(Configuration.find_execution_path('/var/lib/jenkins/foo/spec/file.rb')).to eq(var_lib_jenkins_foo)
127
+ end
128
+
129
+ it '#find_execution_dir finds config location with one lib' do
130
+ allow(File).to receive(:exist?).and_return(true)
131
+ expect(Configuration.find_execution_path('/var/jenkins/foo/lib/file.rb')).to eq(var_jenkins_foo)
132
+ end
133
+
134
+ it '#find_execution_dir finds config location with one spec' do
135
+ allow(File).to receive(:exist?).and_return(true, true)
136
+ expect(Configuration.find_execution_path('/var/jenkins/foo/spec/file.rb')).to eq(var_jenkins_foo)
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../../lib/clean_config/configurable'
2
+ require_relative '../../lib/clean_config/configuration'
3
+
4
+ module CleanConfig
5
+ describe Configurable do
6
+ context 'hooks' do
7
+ it 'does not fail on include when config/config.yml not found' do
8
+ expect do
9
+ class ConfigTest
10
+ include Configurable
11
+ end
12
+ end.not_to raise_error
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ require_relative '../../lib/clean_config/ext/recursive_open_struct'
2
+
3
+ module CleanConfig
4
+ describe RecursiveOpenStruct do
5
+ context 'monkey patched methods' do
6
+ let(:subject) do
7
+ hash = { key1: 'value1', key2: 'value2' }
8
+ RecursiveOpenStruct.new(hash, recurse_over_arrays: true)
9
+ end
10
+
11
+ it 'responds to keys' do
12
+ expect(subject.keys).to include(:key1)
13
+ expect(subject.keys).to include(:key2)
14
+ end
15
+
16
+ it 'responds to fetch' do
17
+ expect(subject.fetch(:key1)).to eql('value1')
18
+ expect(subject.fetch(:monkey, 'default')).to eql('default')
19
+ end
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,261 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clean_config
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - John Crimmins
8
+ - Crystal Hsiung
9
+ - Adrian Cazacu
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2015-10-15 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rake
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '10.1'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ version: '10.1'
29
+ - !ruby/object:Gem::Dependency
30
+ name: rspec
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: '3.1'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: '3.1'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rspec-legacy_formatters
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ~>
48
+ - !ruby/object:Gem::Version
49
+ version: '1.0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: '1.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: rspec-extra-formatters
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: '1.0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ version: '1.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rubocop
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.26.1
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '='
83
+ - !ruby/object:Gem::Version
84
+ version: 0.26.1
85
+ - !ruby/object:Gem::Dependency
86
+ name: fuubar
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ version: '2.0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ~>
97
+ - !ruby/object:Gem::Version
98
+ version: '2.0'
99
+ - !ruby/object:Gem::Dependency
100
+ name: yard
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ! '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ - !ruby/object:Gem::Dependency
114
+ name: redcarpet
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ - !ruby/object:Gem::Dependency
128
+ name: simplecov
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: '0.7'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ~>
139
+ - !ruby/object:Gem::Version
140
+ version: '0.7'
141
+ - !ruby/object:Gem::Dependency
142
+ name: recursive-open-struct
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ~>
146
+ - !ruby/object:Gem::Version
147
+ version: '0.5'
148
+ type: :runtime
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ~>
153
+ - !ruby/object:Gem::Version
154
+ version: '0.5'
155
+ - !ruby/object:Gem::Dependency
156
+ name: activesupport
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - <
160
+ - !ruby/object:Gem::Version
161
+ version: '5.0'
162
+ type: :runtime
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - <
167
+ - !ruby/object:Gem::Version
168
+ version: '5.0'
169
+ description: Provides programatic access to config/config.yml
170
+ email:
171
+ - john.crimmins@opower.com
172
+ executables: []
173
+ extensions: []
174
+ extra_rdoc_files: []
175
+ files:
176
+ - .gitignore
177
+ - .rspec
178
+ - .rubocop.yml
179
+ - .travis.yml
180
+ - .yardopts
181
+ - CHANGELOG.md
182
+ - Gemfile
183
+ - LICENSE
184
+ - README.md
185
+ - Rakefile
186
+ - build.sh
187
+ - clean_config.gemspec
188
+ - lib/clean_config.rb
189
+ - lib/clean_config/configurable.rb
190
+ - lib/clean_config/configuration.rb
191
+ - lib/clean_config/ext/recursive_open_struct.rb
192
+ - lib/clean_config/version.rb
193
+ - lib/tasks.rb
194
+ - spec/e2e/core/.gitignore
195
+ - spec/e2e/core/.rakeTasks
196
+ - spec/e2e/core/.rspec
197
+ - spec/e2e/core/.rubocop.yml
198
+ - spec/e2e/core/Gemfile
199
+ - spec/e2e/core/Rakefile
200
+ - spec/e2e/core/config/config.yml
201
+ - spec/e2e/core/core.gemspec
202
+ - spec/e2e/core/lib/core.rb
203
+ - spec/e2e/core/lib/core/core_one.rb
204
+ - spec/e2e/core/lib/core/version.rb
205
+ - spec/e2e/core/lib/tasks.rb
206
+ - spec/e2e/core/spec/spec_helper.rb
207
+ - spec/e2e/core/spec/unit/core_spec.rb
208
+ - spec/e2e/core_config_spec.rb
209
+ - spec/fixtures/config.yml
210
+ - spec/fixtures/empty.yml
211
+ - spec/spec_helper.rb
212
+ - spec/unit/clean_configuration_spec.rb
213
+ - spec/unit/configurable_spec.rb
214
+ - spec/unit/recursive_open_struct_spec.rb
215
+ homepage: http://github.com/opower/clean_config
216
+ licenses:
217
+ - Apache 2
218
+ metadata: {}
219
+ post_install_message:
220
+ rdoc_options: []
221
+ require_paths:
222
+ - lib
223
+ required_ruby_version: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - ! '>='
226
+ - !ruby/object:Gem::Version
227
+ version: 1.9.3
228
+ required_rubygems_version: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - ! '>='
231
+ - !ruby/object:Gem::Version
232
+ version: '0'
233
+ requirements: []
234
+ rubyforge_project:
235
+ rubygems_version: 2.4.8
236
+ signing_key:
237
+ specification_version: 4
238
+ summary: A simple configuration library
239
+ test_files:
240
+ - spec/e2e/core/.gitignore
241
+ - spec/e2e/core/.rakeTasks
242
+ - spec/e2e/core/.rspec
243
+ - spec/e2e/core/.rubocop.yml
244
+ - spec/e2e/core/Gemfile
245
+ - spec/e2e/core/Rakefile
246
+ - spec/e2e/core/config/config.yml
247
+ - spec/e2e/core/core.gemspec
248
+ - spec/e2e/core/lib/core.rb
249
+ - spec/e2e/core/lib/core/core_one.rb
250
+ - spec/e2e/core/lib/core/version.rb
251
+ - spec/e2e/core/lib/tasks.rb
252
+ - spec/e2e/core/spec/spec_helper.rb
253
+ - spec/e2e/core/spec/unit/core_spec.rb
254
+ - spec/e2e/core_config_spec.rb
255
+ - spec/fixtures/config.yml
256
+ - spec/fixtures/empty.yml
257
+ - spec/spec_helper.rb
258
+ - spec/unit/clean_configuration_spec.rb
259
+ - spec/unit/configurable_spec.rb
260
+ - spec/unit/recursive_open_struct_spec.rb
261
+ has_rdoc: