clean_config 0.0.2

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.
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: