envolve 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 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: []