envolve 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0957b934da66eced716f716a370f189da7633f49
4
+ data.tar.gz: c6198fd51c04495d8f232114d7d062afc52bbe85
5
+ SHA512:
6
+ metadata.gz: ea88528575352a016613859399447d0bfc881989db78cf9ab91ca7dc531098da7c882e9dab740b6338310c5c8c7eec10b703c58f1170cd3c746efc07d9d89cc1
7
+ data.tar.gz: dfb14703a551ee5c85b82d7e98da09da8e732083750cefc17aa711df77989c9933723dc46802b62cde484d95b8dd822bde8a0278e1578b9e605cd886d3cc8570
@@ -0,0 +1,45 @@
1
+ # Hi there!
2
+
3
+ I see you are interested in contributing. That is wonderful. I love
4
+ contributions.
5
+
6
+ I guarantee that there are bugs in this software. And I guarantee that there is
7
+ a feature you want that is not in here yet. As such, any and all bugs reports
8
+ are gratefully accepted, bugfixes even more so. Helping out with bugs is the
9
+ easiest way to contribute.
10
+
11
+
12
+ ## The Quick Version
13
+
14
+ * Have a [GitHub Account][].
15
+ * Search the [GitHub Issues][] and see if your issue already present. If so
16
+ add your comments, :thumbsup:, etc.
17
+ * Issue not there? Not a problem, open up a [new issue][].
18
+ * **Bug reports** please be as detailed as possible. Include:
19
+ * full ruby engine and version: `ruby -e 'puts RUBY_DESCRIPTION'`
20
+ * operating system and version
21
+ * version of envolve `ruby -rubygems -e "require 'envolve'; puts Envolve::VERSION"`
22
+ * as much detail about the bug as possible so I can replicate it. Feel free
23
+ to link in a [gist][]
24
+ * **New Feature**
25
+ * What the new feature should do.
26
+ * What benefit the new feature brings to the project.
27
+ * Fork the [repo][].
28
+ * Create a new branch for your issue: `git checkout -b issue/my-issue`
29
+ * Lovingly craft your contribution:
30
+ * `rake develop` to get started, or if you prefer bundler `rake develop:using_bundler && bundle`.
31
+ * `rake test` to run tests
32
+ * Make sure that `rake test` passes. It's important, I said it twice.
33
+ * Add yourself to the contributors section below.
34
+ * Submit your [pull request][].
35
+
36
+ # Contributors
37
+
38
+ * [Jeremy Hinegardner](https://github.com/copiousfreetime)
39
+
40
+ [GitHub Account]: https://github.com/signup/free "GitHub Signup"
41
+ [GitHub Issues]: https://github.com/copiousfreetime/envolve/issues "Envolve Issues"
42
+ [new issue]: https://github.com/copiousfreetime/envolve/issues/new "New Envolve Issue"
43
+ [gist]: https://gist.github.com/ "New Gist"
44
+ [repo]: https://github.com/copiousfreetime/envolve "Envolve Repo"
45
+ [pull request]: https://help.github.com/articles/using-pull-requests "Using Pull Requests"
@@ -0,0 +1,5 @@
1
+ # Envolve Changelog
2
+ ## Version 1.0.0
3
+
4
+ * Initial Release - Yeah!
5
+
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ ISC LICENSE - http://opensource.org/licenses/isc-license.txt
2
+
3
+ Copyright (c) 2013 Jeremy Hinegardner
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+
@@ -0,0 +1,19 @@
1
+ CONTRIBUTING.md
2
+ HISTORY.md
3
+ LICENSE
4
+ Manifest.txt
5
+ README.md
6
+ Rakefile
7
+ lib/envolve.rb
8
+ lib/envolve/config.rb
9
+ lib/envolve/version.rb
10
+ tasks/default.rake
11
+ tasks/this.rb
12
+ test/test_config.rb
13
+ test/test_filtered_config.rb
14
+ test/test_helper.rb
15
+ test/test_key_separator_config.rb
16
+ test/test_meta_config.rb
17
+ test/test_prefix_config.rb
18
+ test/test_property_config.rb
19
+ test/test_version.rb
@@ -0,0 +1,57 @@
1
+ ## envolve
2
+
3
+ * [Homepage](https://github.com/copiousfreetime/envolve/)
4
+ * [Github Project](https://github.com/copiousfreetime/envolve)
5
+
6
+ ## DESCRIPTION
7
+
8
+ Envolve provides a consistent and validating way to access your application
9
+ configuration that is set via environment variables.
10
+
11
+ This is double beneficial if you are configuring your entire application with
12
+ environment variables. See. http://12factor.net/config
13
+
14
+ ## USAGE
15
+
16
+ module MyApp
17
+ class MyConfig < ::Envolve::Config
18
+ prefix 'my_app'
19
+ end
20
+
21
+
22
+ def self.config
23
+ @config ||= MyConfig.new
24
+ end
25
+ end
26
+
27
+
28
+ # deep in some code somewhere
29
+
30
+ hostname = MyApp.config.hostname # which was originall from ENV['MY_APP_HOSTNAME']
31
+
32
+
33
+ ## FEATURES
34
+
35
+ envolve
36
+
37
+ ## Examples
38
+
39
+ ## ISC LICENSE
40
+
41
+ http://opensource.org/licenses/isc-license.txt
42
+
43
+ Copyright (c) 2013 Jeremy Hinegardner
44
+
45
+ Permission to use, copy, modify, and/or distribute this software for any
46
+ purpose with or without fee is hereby granted, provided that the above
47
+ copyright notice
48
+ and this permission notice appear in all copies.
49
+
50
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
51
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
52
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
53
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
54
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
55
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
56
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
57
+
@@ -0,0 +1,16 @@
1
+ # vim: syntax=ruby
2
+ load 'tasks/this.rb'
3
+
4
+ This.name = "envolve"
5
+ This.author = "Jeremy Hinegardner"
6
+ This.email = "jeremy@copiousfreetime.org"
7
+ This.homepage = "http://github.com/copiousfreetime/#{ This.name }"
8
+
9
+ This.ruby_gemspec do |spec|
10
+ spec.add_development_dependency( 'rake' , '~> 10.3')
11
+ spec.add_development_dependency( 'minitest' , '~> 5.4' )
12
+ spec.add_development_dependency( 'rdoc' , '~> 4.1' )
13
+ spec.add_development_dependency( 'simplecov', '~> 0.9' )
14
+ end
15
+
16
+ load 'tasks/default.rake'
@@ -0,0 +1,9 @@
1
+ module Envolve
2
+ # Public: load the environment into a Config and return it.
3
+ #
4
+ def self.load( env = ENV )
5
+ Envolve::Config.new( env )
6
+ end
7
+ end
8
+ require 'envolve/version'
9
+ require 'envolve/config'
@@ -0,0 +1,245 @@
1
+ module Envolve
2
+ # Public: A Configuration class to hold your application configuration
3
+ #
4
+ # Feed it ENV or some other Hash-like object and it will allow you to access
5
+ # the elements in that hash via methods.
6
+ #
7
+ # You can also tell it to only pull those items from the initial has that have
8
+ # a particular prefix, and that prefix will be stripped off of the elements
9
+ # for access.
10
+ class Config
11
+ # Public: Return the default environment.
12
+ #
13
+ # Override this to return a different environment source
14
+ def self.environment_source( *args, &block )
15
+ if args.size > 0 then
16
+ @_default_env = args.first
17
+ elsif block_given? then
18
+ @_default_env = block.call
19
+ else
20
+ @_default_env ||= ENV.to_hash
21
+ end
22
+ end
23
+
24
+ # Public: Return the prefix to be used by the class
25
+ #
26
+ # Override this to return a different prefix, or meta program it in an
27
+ # inherited class.
28
+ def self.prefix( *args )
29
+ if args.size > 0 then
30
+ @_prefix = args.first
31
+ else
32
+ @_prefix = nil if !defined?( @_prefix )
33
+ end
34
+ @_prefix
35
+ end
36
+
37
+ # Public: Return the key_separator to be used by the class
38
+ #
39
+ # Override this to return a different key_separator, or meta program it in an
40
+ # inherited class.
41
+ def self.key_separator( *args, &block )
42
+ if args.size > 0 then
43
+ @_key_separator = args.first
44
+ else
45
+ @_key_separator = '_'.freeze if !defined?( @_key_separator )
46
+ end
47
+ @_key_separator
48
+ end
49
+
50
+ # Public: Set a property, with possible transformations
51
+ #
52
+ # In the conversion of a the environment to the configuration properties
53
+ # sometimes the keys and/or values need to be converted to a new name.
54
+ #
55
+ # All property transformations take place AFTER the initial keys have been downcased
56
+ # and prefix stripped.
57
+ #
58
+ # property - the name of the property we want to appear in the configuration
59
+ # key - the source key from the environment where this property comes from
60
+ # value - the new value for this property
61
+ # default - setting a default for this property should it not exist
62
+ #
63
+ # value may also be a lambda, in which ase the lambda is given the original
64
+ # value and the return value from the labmda is used for the new value.
65
+ #
66
+ def self.property( property, key: nil, value: nil, default: nil )
67
+ properties[property] = { :key => key, :value => value, :default => default }
68
+ end
69
+
70
+ # Internal: Return the hash holding the properties
71
+ #
72
+ # Returns Hash
73
+ def self.properties
74
+ @_properties ||= Hash.new
75
+ end
76
+
77
+ # Internal: The internal hash holding all the keys and values
78
+ attr_reader :_env
79
+
80
+ # Internal: The prefix to strip off all the keys
81
+ attr_reader :_prefix
82
+
83
+ # Internal: The character to use as the key separator
84
+ attr_reader :_key_separator
85
+
86
+ # Public: Create a new Config
87
+ def initialize( env: self.class.environment_source, prefix: self.class.prefix,
88
+ key_separator: self.class.key_separator )
89
+ @_key_separator = key_separator
90
+ @_prefix = prefix.nil? ? nil : prefix.to_s.downcase.strip
91
+ @_env = process_env( env )
92
+
93
+ end
94
+
95
+
96
+ # Internal: Process and Transform the keys and values from the environment
97
+ # into the final hash
98
+ #
99
+ def process_env( env )
100
+ env = downcase_keys( env )
101
+ if _prefix then
102
+ env = filter_by_prefix( env, _prefix )
103
+ end
104
+ env = transform_properties( env, self.class.properties )
105
+ end
106
+
107
+ # Internal: Transform the environment variables to propreties
108
+ #
109
+ # Returns the transformed hash
110
+ def transform_properties( env, properties )
111
+ transformed = env.to_h.dup
112
+
113
+ properties.each do |dest_key, trans|
114
+ src_key = trans[:key]
115
+ src_key ||= dest_key
116
+ if value = transformed.delete(src_key) then
117
+ value = apply_transformation(value, trans[:value]) if trans[:value]
118
+ elsif value.nil? then
119
+ value = trans[:default] if trans[:default]
120
+ end
121
+ transformed[dest_key] = value
122
+ end
123
+
124
+ return transformed
125
+ end
126
+
127
+ # Internal: Apply the given transformation to the input.
128
+ #
129
+ # Returns the result of the transformation
130
+ def apply_transformation( input, transformer )
131
+ return input if transformer.nil?
132
+ return transformer.call( input ) if transformer.respond_to?( :call )
133
+ return transformer
134
+ end
135
+
136
+ # Public: The number of elements in the config
137
+ #
138
+ # Returns the number of elements
139
+ def size
140
+ _env.size
141
+ end
142
+
143
+ # Public: Just the keys, only the keys, as Strings
144
+ #
145
+ # Returns an Array of the keys as Strings
146
+ def keys
147
+ _env.keys
148
+ end
149
+
150
+ # Public: return the value for the give key
151
+ #
152
+ # key - the String key to use for fetching
153
+ #
154
+ # Returns value or nil if no value is found
155
+ def []( key )
156
+ _env[key]
157
+ end
158
+
159
+ # Public: Return a subset of the config with just those items that have a
160
+ # prefix on them
161
+ #
162
+ # The resulting Config only has those keys, and all with the prefixes
163
+ # stripped
164
+ def config_with_prefix( prefix )
165
+ self.class.new( env: _env, prefix: prefix )
166
+ end
167
+
168
+ # Public: Return as hash of the keys and values
169
+ #
170
+ # Returns a Hash
171
+ def to_h
172
+ _env.to_h.dup
173
+ end
174
+ alias to_hash to_h
175
+
176
+ # Public: Return a hash of the keys and values with the keys as symbols.
177
+ #
178
+ # Returns a Hash
179
+ def to_symbolized_h
180
+ h = {}
181
+ _env.each do |key, value|
182
+ h[key.to_sym] = value
183
+ end
184
+ return h
185
+ end
186
+ alias to_symbolized_hash to_symbolized_h
187
+
188
+ # Internal: This is how we convert method calls into key lookups in the
189
+ # internal hash.
190
+ def method_missing( method, *args, &block )
191
+ s_method = method.to_s
192
+ if _env.has_key?( s_method ) then
193
+ _env[ s_method ]
194
+ else
195
+ super
196
+ end
197
+ end
198
+
199
+ # Internal: Respond to missing should always be implemented if you implement
200
+ # method_missing
201
+ def respond_to_missing?( symbol, include_all = false )
202
+ _env.has_key?( symbol.to_s ) || super
203
+ end
204
+
205
+ # Internal: The prefix regular expression used for stripping leading prefix
206
+ #
207
+ # This matches Beginning of String followed by the prefix followed by 0 or
208
+ # more key_separator characters.
209
+ #
210
+ # This will use named captures. The prefix will be under key 'prefix' and
211
+ # the following will be under 'rest'
212
+ #
213
+ # Returns the regex
214
+ def prefix_regex( prefix, key_separator )
215
+ /\A(?<prefix>#{prefix}[#{key_separator}]*)(?<rest>.*)\Z/i
216
+ end
217
+
218
+
219
+ private
220
+
221
+ # Return a copy of the hash filtered by the prefix.
222
+ #
223
+ # All the keys that match the prefix will have hte prefix stripped off. All
224
+ # others will be ignored
225
+ def filter_by_prefix( in_hash, pre = _prefix, ks = _key_separator )
226
+ out_hash = {}
227
+ matcher = prefix_regex( pre, ks )
228
+ in_hash.each do |key, value|
229
+ if md = matcher.match( key ) then
230
+ out_hash[ md[:rest] ] = value
231
+ end
232
+ end
233
+ return out_hash
234
+ end
235
+
236
+ # Return a copy of the hash with all the keys downcased
237
+ def downcase_keys( in_hash )
238
+ out_hash = {}
239
+ in_hash.each do |key, value|
240
+ out_hash[key.downcase] = value
241
+ end
242
+ return out_hash
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,3 @@
1
+ module Envolve
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,259 @@
1
+ # vim: syntax=ruby
2
+ require 'rake/clean'
3
+ require 'digest'
4
+ #------------------------------------------------------------------------------
5
+ # If you want to Develop on this project just run 'rake develop' and you'll
6
+ # have all you need to get going. If you want to use bundler for development,
7
+ # then run 'rake develop:using_bundler'
8
+ #------------------------------------------------------------------------------
9
+ namespace :develop do
10
+
11
+ # Install all the development and runtime dependencies of this gem using the
12
+ # gemspec.
13
+ task :default do
14
+ require 'rubygems/dependency_installer'
15
+ installer = ::Gem::DependencyInstaller.new
16
+
17
+ puts "Installing gem depedencies needed for development"
18
+ This.platform_gemspec.dependencies.each do |dep|
19
+ if dep.matching_specs.empty? then
20
+ puts "Installing : #{dep}"
21
+ installer.install dep
22
+ else
23
+ puts "Skipping : #{dep} -> already installed #{dep.matching_specs.first.full_name}"
24
+ end
25
+ end
26
+ puts "\n\nNow run 'rake test'"
27
+ end
28
+
29
+ # Create a Gemfile that just references the gemspec
30
+ file 'Gemfile' => :gemspec do
31
+ File.open( "Gemfile", "w+" ) do |f|
32
+ f.puts "# DO NOT EDIT - This file is automatically generated"
33
+ f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate"
34
+ f.puts 'source "https://rubygems.org/"'
35
+ f.puts 'gemspec'
36
+ end
37
+ end
38
+
39
+ desc "Create a bundler Gemfile"
40
+ task :using_bundler => 'Gemfile' do
41
+ puts "Now you can 'bundle'"
42
+ end
43
+
44
+ # Gemfiles are build artifacts
45
+ CLOBBER << FileList['Gemfile*']
46
+ end
47
+ desc "Boostrap development"
48
+ task :develop => "develop:default"
49
+
50
+ #------------------------------------------------------------------------------
51
+ # Minitest - standard TestTask
52
+ #------------------------------------------------------------------------------
53
+ begin
54
+ require 'rake/testtask'
55
+ Rake::TestTask.new( :test ) do |t|
56
+ t.ruby_opts = %w[ -w -rubygems ]
57
+ t.libs = %w[ lib spec test ]
58
+ t.pattern = "{test,spec}/**/{test_*,*_spec}.rb"
59
+ end
60
+
61
+ task :test_requirements
62
+ task :test => :test_requirements
63
+ task :default => :test
64
+ rescue LoadError
65
+ This.task_warning( 'test' )
66
+ end
67
+
68
+ #------------------------------------------------------------------------------
69
+ # RDoc - standard rdoc rake task, although we must make sure to use a more
70
+ # recent version of rdoc since it is the one that has 'tomdoc' markup
71
+ #------------------------------------------------------------------------------
72
+ begin
73
+ gem 'rdoc' # otherwise we get the wrong task from stdlib
74
+ require 'rdoc/task'
75
+ RDoc::Task.new do |t|
76
+ t.markup = 'tomdoc'
77
+ t.rdoc_dir = 'doc'
78
+ t.main = 'README.md'
79
+ t.title = "#{This.name} #{This.version}"
80
+ t.rdoc_files.include( FileList['*.{rdoc,md,txt}'], FileList['ext/**/*.c'],
81
+ FileList['lib/**/*.rb'] )
82
+ end
83
+ rescue StandardError, LoadError
84
+ This.task_warning( 'rdoc' )
85
+ end
86
+
87
+ #------------------------------------------------------------------------------
88
+ # Coverage - optional code coverage, rcov for 1.8 and simplecov for 1.9, so
89
+ # for the moment only rcov is listed.
90
+ #------------------------------------------------------------------------------
91
+ begin
92
+ require 'simplecov'
93
+ desc 'Run tests with code coverage'
94
+ task :coverage do
95
+ ENV['COVERAGE'] = 'true'
96
+ Rake::Task[:test].execute
97
+ end
98
+ CLOBBER << FileList["coverage/**/*"]
99
+ rescue LoadError
100
+ This.task_warning( 'simplecov' )
101
+ end
102
+
103
+ #------------------------------------------------------------------------------
104
+ # Manifest - We want an explicit list of thos files that are to be packaged in
105
+ # the gem. Most of this is from Hoe.
106
+ #------------------------------------------------------------------------------
107
+ namespace 'manifest' do
108
+ desc "Check the manifest"
109
+ task :check => :clean do
110
+ files = FileList["**/*", ".*"].exclude( This.exclude_from_manifest ).to_a.sort
111
+ files = files.select{ |f| File.file?( f ) }
112
+
113
+ tmp = "Manifest.tmp"
114
+ File.open( tmp, 'w' ) do |f|
115
+ f.puts files.join("\n")
116
+ end
117
+
118
+ begin
119
+ sh "diff -du Manifest.txt #{tmp}"
120
+ ensure
121
+ rm tmp
122
+ end
123
+ puts "Manifest looks good"
124
+ end
125
+
126
+ desc "Generate the manifest"
127
+ task :generate => :clean do
128
+ files = %x[ git ls-files ].split("\n").sort
129
+ files.reject! { |f| f =~ This.exclude_from_manifest }
130
+ File.open( "Manifest.txt", "w" ) do |f|
131
+ f.puts files.join("\n")
132
+ end
133
+ end
134
+ end
135
+
136
+ #------------------------------------------------------------------------------
137
+ # Fixme - look for fixmes and report them
138
+ #------------------------------------------------------------------------------
139
+ namespace :fixme do
140
+ task :default => 'manifest:check' do
141
+ This.manifest.each do |file|
142
+ next if file == __FILE__
143
+ next unless file =~ %r/(txt|rb|md|rdoc|css|html|xml|css)\Z/
144
+ puts "FIXME: Rename #{file}" if file =~ /fixme/i
145
+ IO.readlines( file ).each_with_index do |line, idx|
146
+ prefix = "FIXME: #{file}:#{idx+1}".ljust(42)
147
+ puts "#{prefix} => #{line.strip}" if line =~ /fixme/i
148
+ end
149
+ end
150
+ end
151
+
152
+ def fixme_project_root
153
+ This.project_path( '../fixme' )
154
+ end
155
+
156
+ def fixme_project_path( subtree )
157
+ fixme_project_root.join( subtree )
158
+ end
159
+
160
+ def local_fixme_files
161
+ This.manifest.select { |p| p =~ %r|^tasks/| }
162
+ end
163
+
164
+ def outdated_fixme_files
165
+ local_fixme_files.reject do |local|
166
+ upstream = fixme_project_path( local )
167
+ Digest::SHA256.file( local ) == Digest::SHA256.file( upstream )
168
+ end
169
+ end
170
+
171
+ def fixme_up_to_date?
172
+ outdated_fixme_files.empty?
173
+ end
174
+
175
+ desc "See if the fixme tools are outdated"
176
+ task :outdated => :release_check do
177
+ if fixme_up_to_date? then
178
+ puts "Fixme files are up to date."
179
+ else
180
+ outdated_fixme_files.each do |f|
181
+ puts "#{f} is outdated"
182
+ end
183
+ end
184
+ end
185
+
186
+ desc "Update outdated fixme files"
187
+ task :update => :release_check do
188
+ if fixme_up_to_date? then
189
+ puts "Fixme files are already up to date."
190
+ else
191
+ puts "Updating fixme files:"
192
+ outdated_fixme_files.each do |local|
193
+ upstream = fixme_project_path( local )
194
+ puts " * #{local}"
195
+ FileUtils.cp( upstream, local )
196
+ end
197
+ puts "Use your git commands as appropriate."
198
+ end
199
+ end
200
+ end
201
+ desc "Look for fixmes and report them"
202
+ task :fixme => "fixme:default"
203
+
204
+ #------------------------------------------------------------------------------
205
+ # Gem Specification
206
+ #------------------------------------------------------------------------------
207
+ # Really this is only here to support those who use bundler
208
+ desc "Build the #{This.name}.gemspec file"
209
+ task :gemspec do
210
+ File.open( This.gemspec_file, "wb+" ) do |f|
211
+ f.puts "# DO NOT EDIT - This file is automatically generated"
212
+ f.puts "# Make changes to Manifest.txt and/or Rakefile and regenerate"
213
+ f.write This.platform_gemspec.to_ruby
214
+ end
215
+ end
216
+
217
+ # the gemspec is also a dev artifact and should not be kept around.
218
+ CLOBBER << This.gemspec_file.to_s
219
+
220
+ # .rbc files from ruby 2.0
221
+ CLOBBER << FileList["**/*.rbc"]
222
+
223
+ # The standard gem packaging task, everyone has it.
224
+ require 'rubygems/package_task'
225
+ ::Gem::PackageTask.new( This.platform_gemspec ) do
226
+ # nothing
227
+ end
228
+
229
+ #------------------------------------------------------------------------------
230
+ # Release - the steps we go through to do a final release, this is pulled from
231
+ # a compbination of mojombo's rakegem, hoe and hoe-git
232
+ #
233
+ # 1) make sure we are on the master branch
234
+ # 2) make sure there are no uncommitted items
235
+ # 3) check the manifest and make sure all looks good
236
+ # 4) build the gem
237
+ # 5) do an empty commit to have the commit message of the version
238
+ # 6) tag that commit as the version
239
+ # 7) push master
240
+ # 8) push the tag
241
+ # 7) pus the gem
242
+ #------------------------------------------------------------------------------
243
+ task :release_check do
244
+ unless `git branch` =~ /^\* master$/
245
+ abort "You must be on the master branch to release!"
246
+ end
247
+ unless `git status` =~ /^nothing to commit/m
248
+ abort "Nope, sorry, you have unfinished business"
249
+ end
250
+ end
251
+
252
+ desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_name} to rubygems.org"
253
+ task :release => [ :release_check, 'manifest:check', :gem ] do
254
+ sh "git commit --allow-empty -a -m 'Release #{This.version}'"
255
+ sh "git tag -a -m 'v#{This.version}' v#{This.version}"
256
+ sh "git push origin master"
257
+ sh "git push origin v#{This.version}"
258
+ sh "gem push pkg/#{This.platform_gemspec.full_name}.gem"
259
+ end
@@ -0,0 +1,206 @@
1
+ require 'pathname'
2
+
3
+ # Public: A Class containing all the metadata and utilities needed to manage a
4
+ # ruby project.
5
+ class ThisProject
6
+ # The name of this project
7
+ attr_accessor :name
8
+
9
+ # The author's name
10
+ attr_accessor :author
11
+
12
+ # The email address of the author(s)
13
+ attr_accessor :email
14
+
15
+ # The homepage of this project
16
+ attr_accessor :homepage
17
+
18
+ # The regex of files to exclude from the manifest
19
+ attr_accessor :exclude_from_manifest
20
+
21
+ # The hash of Gem::Specifications keyed' by platform
22
+ attr_accessor :gemspecs
23
+
24
+ # Public: Initialize ThisProject
25
+ #
26
+ # Yields self
27
+ def initialize(&block)
28
+ @exclude_from_manifest = Regexp.union(/\.(git|DS_Store)/,
29
+ /^(doc|coverage|pkg|tmp|Gemfile(\.lock)?)/,
30
+ /^[^\/]+\.gemspec/,
31
+ /\.(swp|jar|bundle|so|rvmrc|travis.yml)$/,
32
+ /~$/)
33
+ @gemspecs = Hash.new
34
+ yield self if block_given?
35
+ end
36
+
37
+ # Public: return the version of ThisProject
38
+ #
39
+ # Search the ruby files in the project looking for the one that has the
40
+ # version string in it. This does not eval any code in the project, it parses
41
+ # the source code looking for the string.
42
+ #
43
+ # Returns a String version
44
+ def version
45
+ [ "lib/#{ name }.rb", "lib/#{ name }/version.rb" ].each do |v|
46
+ path = project_path( v )
47
+ line = path.read[/^\s*VERSION\s*=\s*.*/]
48
+ if line then
49
+ return line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
50
+ end
51
+ end
52
+ end
53
+
54
+ # Internal: Return a section of an RDoc file with the given section name
55
+ #
56
+ # path - the relative path in the project of the file to parse
57
+ # section_name - the section out of the file from which to parse data
58
+ #
59
+ # Retuns the text of the section as an array of paragrphs.
60
+ def section_of( file, section_name )
61
+ re = /^[=#]+ (.*)$/
62
+ sectional = project_path( file )
63
+ parts = sectional.read.split( re )[1..-1]
64
+ parts.map! { |p| p.strip }
65
+
66
+ sections = Hash.new
67
+ Hash[*parts].each do |k,v|
68
+ sections[k] = v.split("\n\n")
69
+ end
70
+ return sections[section_name]
71
+ end
72
+
73
+ # Internal: print out a warning about the give task
74
+ def task_warning( task )
75
+ warn "WARNING: '#{task}' tasks are not defined. Please run 'rake develop'"
76
+ end
77
+
78
+ # Internal: Return the full path to the file that is relative to the project
79
+ # root.
80
+ #
81
+ # path - the relative path of the file from the project root
82
+ #
83
+ # Returns the Pathname of the file
84
+ def project_path( *relative_path )
85
+ project_root.join( *relative_path )
86
+ end
87
+
88
+ # Internal: The absolute path of this file
89
+ #
90
+ # Returns the Pathname of this file.
91
+ def this_file_path
92
+ Pathname.new( __FILE__ ).expand_path
93
+ end
94
+
95
+ # Internal: The root directory of this project
96
+ #
97
+ # This is defined as being the directory that is in the path of this project
98
+ # that has the first Rakefile
99
+ #
100
+ # Returns the Pathname of the directory
101
+ def project_root
102
+ this_file_path.ascend do |p|
103
+ rakefile = p.join( 'Rakefile' )
104
+ return p if rakefile.exist?
105
+ end
106
+ end
107
+
108
+ # Internal: Returns the contents of the Manifest.txt file as an array
109
+ #
110
+ # Returns an Array of strings
111
+ def manifest
112
+ manifest_file = project_path( "Manifest.txt" )
113
+ abort "You need a Manifest.txt" unless manifest_file.readable?
114
+ manifest_file.readlines.map { |l| l.strip }
115
+ end
116
+
117
+ # Internal: Return the files that define the extensions
118
+ #
119
+ # Returns an Array
120
+ def extension_conf_files
121
+ manifest.grep( /extconf.rb\Z/ )
122
+ end
123
+
124
+ # Internal: Returns the gemspace associated with the current ruby platform
125
+ def platform_gemspec
126
+ gemspecs[platform]
127
+ end
128
+
129
+ def core_gemspec
130
+ Gem::Specification.new do |spec|
131
+ spec.name = name
132
+ spec.version = version
133
+ spec.author = author
134
+ spec.email = email
135
+ spec.homepage = homepage
136
+
137
+ spec.summary = summary
138
+ spec.description = description
139
+ spec.license = license
140
+
141
+ spec.files = manifest
142
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
143
+ spec.test_files = spec.files.grep(/^spec/)
144
+
145
+ spec.extra_rdoc_files += spec.files.grep(/(txt|rdoc|md)$/)
146
+ spec.rdoc_options = [ "--main" , 'README.md',
147
+ "--markup", "tomdoc" ]
148
+
149
+ spec.required_ruby_version = '~> 2.0'
150
+ end
151
+ end
152
+
153
+ # Internal: Return the gemspec for the ruby platform
154
+ def ruby_gemspec( core = core_gemspec, &block )
155
+ yielding_gemspec( 'ruby', core, &block )
156
+ end
157
+
158
+ # Internal: Return the gemspec for the jruby platform
159
+ def java_gemspec( core = core_gemspec, &block )
160
+ yielding_gemspec( 'java', core, &block )
161
+ end
162
+
163
+ # Internal: give an initial spec and a key, create a new gemspec based off of
164
+ # it.
165
+ #
166
+ # This will force the new gemspecs 'platform' to be that of the key, since the
167
+ # only reason you would have multiple gemspecs at this point is to deal with
168
+ # different platforms.
169
+ def yielding_gemspec( key, core )
170
+ spec = gemspecs[key] ||= core.dup
171
+ spec.platform = key
172
+ yield spec if block_given?
173
+ return spec
174
+ end
175
+
176
+ # Internal: Return the platform of ThisProject at the current moment in time.
177
+ def platform
178
+ (RUBY_PLATFORM == "java") ? 'java' : Gem::Platform::RUBY
179
+ end
180
+
181
+ # Internal: Return the DESCRIPTION section of the README.rdoc file
182
+ def description_section
183
+ section_of( 'README.md', 'DESCRIPTION')
184
+ end
185
+
186
+ # Internal: Return the summary text from the README
187
+ def summary
188
+ description_section.first
189
+ end
190
+
191
+ # Internal: Return the full description text from the README
192
+ def description
193
+ description_section.join(" ").tr("\n", ' ').gsub(/[{}]/,'').gsub(/\[[^\]]+\]/,'') # strip rdoc
194
+ end
195
+
196
+ def license
197
+ "ISC"
198
+ end
199
+
200
+ # Internal: The path to the gemspec file
201
+ def gemspec_file
202
+ project_path( "#{ name }.gemspec" )
203
+ end
204
+ end
205
+
206
+ This = ThisProject.new
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+ require 'envolve/config'
3
+
4
+ class TestConfig < ::Minitest::Test
5
+ def env
6
+ { 'EV_TEST_1' => 'test-1',
7
+ 'EV_TEST_2' => 'test-2' }
8
+ end
9
+
10
+ def setup
11
+ @config = Envolve::Config.new( env: env )
12
+ end
13
+
14
+ def test_access_key_as_method
15
+ assert_equal( 'test-1', @config.ev_test_1 )
16
+ end
17
+
18
+ def test_respond_to
19
+ assert( @config.respond_to?( :ev_test_2 ) )
20
+ end
21
+
22
+ def test_size
23
+ assert( 2, @config.size )
24
+ end
25
+
26
+ def test_keys
27
+ assert_equal( [ 'ev_test_1', 'ev_test_2'], @config.keys.sort )
28
+ end
29
+
30
+
31
+ def test_access
32
+ assert_equal( 'test-2', @config['ev_test_2'] )
33
+ end
34
+
35
+ def test_config_with_prefix
36
+ assert_equal( { 'test_1' => 'test-1', 'test_2' => 'test-2' }, @config.config_with_prefix( 'ev' ).to_h )
37
+ end
38
+
39
+ def test_symbolized_h
40
+ assert_equal( { test_1: 'test-1', test_2: 'test-2' }, @config.config_with_prefix( 'ev' ).to_symbolized_hash )
41
+ end
42
+
43
+ end
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+ require 'envolve/config'
3
+
4
+ class TestFilteredConfig < ::Minitest::Test
5
+ def env
6
+ { 'EV_X_TEST_1' => 'test-1',
7
+ 'EV_X_TEST_2' => 'test-2',
8
+ 'EV_TEST_3' => 'test-3',
9
+ 'EV_TEST_4' => 'test-4' }
10
+ end
11
+
12
+ def setup
13
+ @config = Envolve::Config.new( env: env, prefix: 'ev_x' )
14
+ end
15
+
16
+ def test_access_key_as_method
17
+ assert_equal( 'test-1', @config.test_1 )
18
+ end
19
+
20
+ def test_respond_to
21
+ assert( @config.respond_to?( :test_2 ) )
22
+ end
23
+
24
+ def test_size
25
+ assert( 2, @config.size )
26
+ end
27
+
28
+ def test_keys
29
+ assert_equal( [ 'test_1', 'test_2'], @config.keys.sort )
30
+ end
31
+
32
+
33
+ def test_access
34
+ assert_equal( 'test-2', @config['test_2'] )
35
+ end
36
+
37
+ end
@@ -0,0 +1,6 @@
1
+ require 'simplecov'
2
+ SimpleCov.start if ENV['COVERAGE']
3
+
4
+ gem 'minitest'
5
+ require 'minitest/autorun'
6
+ require 'minitest/pride'
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+ require 'envolve/config'
3
+ require 'test_config'
4
+
5
+ class KeySeparatorConfig < Envolve::Config
6
+ environment_source {
7
+ { 'EV-TEST-1' => 'test-1', 'EV-TEST-2' => 'test-2' }
8
+ }
9
+ key_separator '-'
10
+ end
11
+
12
+ class TestKeySeparatorConfig < ::TestConfig
13
+ def setup
14
+ @config = LambdaConfig.new
15
+ end
16
+ end
17
+
@@ -0,0 +1,16 @@
1
+ require 'test_helper'
2
+ require 'envolve/config'
3
+ require 'test_config'
4
+
5
+ class LambdaConfig < Envolve::Config
6
+ environment_source {
7
+ { 'EV_TEST_1' => 'test-1', 'EV_TEST_2' => 'test-2' }
8
+ }
9
+ end
10
+
11
+ class TestMetaConfig < ::TestConfig
12
+ def setup
13
+ @config = LambdaConfig.new
14
+ end
15
+ end
16
+
@@ -0,0 +1,20 @@
1
+ require 'test_helper'
2
+ require 'envolve/config'
3
+ require 'test_filtered_config'
4
+
5
+ class PrefixConfig < Envolve::Config
6
+ def self.env
7
+ { 'EV_X_TEST_1' => 'test-1',
8
+ 'EV_X_TEST_2' => 'test-2',
9
+ 'EV_TEST_3' => 'test-3',
10
+ 'EV_TEST_4' => 'test-4' }
11
+ end
12
+
13
+ environment_source( env )
14
+ prefix 'ev_x'
15
+ end
16
+ class TestPrefixConfig < TestFilteredConfig
17
+ def setup
18
+ @config = PrefixConfig.new
19
+ end
20
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+ require 'envolve/config'
3
+
4
+ class PropertyConfig < ::Envolve::Config
5
+ environment_source {
6
+ {
7
+ 'EV_TEST_1' => 'test-1',
8
+ 'EV_TEST_2' => 'test-2',
9
+ 'EV_FOO' => 'test-bar',
10
+ 'EV_WIBBLE' => 'wobble',
11
+ }
12
+ }
13
+
14
+ prefix 'ev'
15
+
16
+ property 'bar', :key => 'foo'
17
+ property 'wibble', :value => lambda { |val| val.gsub('o', 'ee') }
18
+ property 'ara', :default => 42, :value => lambda { |val| Integer(val) }
19
+
20
+ end
21
+
22
+ class TestPropertyConfig < ::Minitest::Test
23
+ def setup
24
+ @config = PropertyConfig.new
25
+ end
26
+
27
+
28
+ def test_access_key_as_method
29
+ assert_equal( 'test-1', @config.test_1 )
30
+ end
31
+
32
+ def test_respond_to
33
+ assert( @config.respond_to?( :test_2 ) )
34
+ end
35
+
36
+ def test_size
37
+ assert( 4, @config.size )
38
+ end
39
+
40
+ def test_keys
41
+ keys = %w[ test_1 test_2 bar wibble ara].sort
42
+ assert_equal( keys, @config.keys.sort )
43
+ end
44
+
45
+ def test_access
46
+ assert_equal( 'weebble', @config['wibble'] )
47
+ end
48
+
49
+ def test_defaults
50
+ assert_equal( 42, @config['ara'] )
51
+ end
52
+
53
+ end
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+ require 'envolve/version'
3
+
4
+ class TestVersion < ::Minitest::Test
5
+ def test_version_constant_match
6
+ assert_match(/\A\d+\.\d+\.\d+\Z/, Envolve::VERSION)
7
+ end
8
+
9
+ def test_version_string_match
10
+ assert_match(/\A\d+\.\d+\.\d+\Z/, Envolve::VERSION.to_s)
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: envolve
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Hinegardner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.4'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.4'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdoc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.1'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ description: Envolve provides a consistent and validating way to access your application
70
+ configuration that is set via environment variables. This is double beneficial if
71
+ you are configuring your entire application with environment variables. See. http://12factor.net/config
72
+ email: jeremy@copiousfreetime.org
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files:
76
+ - CONTRIBUTING.md
77
+ - HISTORY.md
78
+ - Manifest.txt
79
+ - README.md
80
+ files:
81
+ - CONTRIBUTING.md
82
+ - HISTORY.md
83
+ - LICENSE
84
+ - Manifest.txt
85
+ - README.md
86
+ - Rakefile
87
+ - lib/envolve.rb
88
+ - lib/envolve/config.rb
89
+ - lib/envolve/version.rb
90
+ - tasks/default.rake
91
+ - tasks/this.rb
92
+ - test/test_config.rb
93
+ - test/test_filtered_config.rb
94
+ - test/test_helper.rb
95
+ - test/test_key_separator_config.rb
96
+ - test/test_meta_config.rb
97
+ - test/test_prefix_config.rb
98
+ - test/test_property_config.rb
99
+ - test/test_version.rb
100
+ homepage: http://github.com/copiousfreetime/envolve
101
+ licenses:
102
+ - ISC
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options:
106
+ - "--main"
107
+ - README.md
108
+ - "--markup"
109
+ - tomdoc
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '2.0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.2.2
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Envolve provides a consistent and validating way to access your application
128
+ configuration that is set via environment variables.
129
+ test_files: []