envied 0.7.0 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 46ff25f0ca1accf79ba7095db2e6b0232af3cb8c
4
- data.tar.gz: f6f3572cb136f9d3e68affbfa4bafa839a203b05
3
+ metadata.gz: 501198780ddc5bf4762c604f90938baad9258ee4
4
+ data.tar.gz: 3027a30c7815e8925246e7f14d3e7ffcc7a3bfef
5
5
  SHA512:
6
- metadata.gz: 70d6e4d6fb890c88bbb18c058c487e4c9bedcc0183562e6ef466f54c96c82701f635a2b7aa0af7da32747938ba14caea1098200cbd94a9d43068d9e3d54c8f14
7
- data.tar.gz: a25bdf917df7f8479733e751051a08c9289c6c8beb8acc9c9068ff5b095e33c80c03e8e473e43c1f48083cddf6e8b41dfe94a82a38050d793db49e6d8edb1be8
6
+ metadata.gz: 826a2dfaa0ac7730c2fcaa14e14ea997b3c5b665774eed3ed91110e5950a29d982c6eb761e783a7eee4be6b3d1629483b068e5a1971b0d9eccabb9dc329c41c2
7
+ data.tar.gz: ad53e3a25cb8a9c817eb14fb0a0f0a8d815fd8dd87b54dfdf0653f920dd6e8f963014d2efdb9fd56405b8ec67d40f5df36930752c6d7794bdce41d61df8c2556
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 0.7.1 / 2014-08-29
2
+
3
+ * Total refactor (TM).
4
+
5
+ * Fix bug in Heroku binstub.
6
+
7
+ It checked for group 'default,production' instead of 'default' and 'production'.
8
+
1
9
  # 0.7.0 / 2014-08-24
2
10
 
3
11
  * Add init:rails-task for setup in Rails applications.
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ### TL;DR ensure presence and type of your app's ENV-variables.
4
4
 
5
- Features:
5
+ ## Features:
6
6
 
7
7
  * check for presence and correctness of ENV-variables
8
8
  * access to typed ENV-variables (integers, booleans etc. instead of just strings)
@@ -16,6 +16,7 @@ Features:
16
16
  * [Types](#types)
17
17
  * [Groups](#groups)
18
18
  * [Defaults](#defaults)
19
+ * [More examples](#more-examples)
19
20
  * [Rails](#rails)
20
21
  * [Command-line interface](#command-line-interface)
21
22
  * [Testing](#testing)
@@ -135,56 +136,10 @@ As a rule of thumb you should only use defaults:
135
136
  * for local development
136
137
  * for ENV-variables that your application introduces (i.e. for `ENV['STAFF_EMAILS']` not for `ENV['REDIS_URL']`)
137
138
 
138
- ### A more extensive example
139
+ ### More examples
139
140
 
140
- ```ruby
141
- # Envfile
142
- # We allow defaults for local development (and local tests), but want our CI
143
- # to mimic our production as much as possible.
144
- # New developers that don't have RACK_ENV set, will in this way not be presented with a huge
145
- # list of missing variables, as defaults are still enabled.
146
- not_production_nor_ci = ->{ !(ENV['RACK_ENV'] == 'production' || ENV['CI']) }
147
- enable_defaults!(&not_production_nor_ci)
148
-
149
- # Your code will likely not use ENVied.RACK_ENV (better use Rails.env),
150
- # we want it to be present though; heck, we're using it in this file!
151
- variable :RACK_ENV
152
-
153
- variable :FORCE_SSL, :Boolean, default: false
154
- variable :PORT, :Integer, default: 3000
155
- # generate the default value using the value of PORT:
156
- variable :PUBLIC_HOST_WITH_PORT, :String, default: proc {|envied| "localhost:#{envied.PORT}" }
157
-
158
- group :production do
159
- variable :MAIL_PAAS_USERNAME
160
- variable :DATABASE_URL
161
- end
162
-
163
- group :ci do
164
- # ci-only stuff
165
- end
166
-
167
- group :not_ci do
168
- # CI needs no puma-threads, and sidekiq-stuff etc.
169
- # Define that here:
170
- variable :MIN_THREADS, :Integer, default: 1
171
- # more...
172
- end
173
-
174
- # Depending on our situation, we can now require the groups needed:
175
- # At local machines:
176
- ENVied.require(:default, :development, :not_ci) or
177
- ENVied.require(:default, :test, :not_ci)
178
-
179
- # At the server:
180
- ENVied.require(:default, :production, :not_ci)
181
-
182
- # At CI:
183
- ENVied.require(:default, :test, :ci)
184
-
185
- # All in one line:
186
- ENVied.require(:default, ENV['RACK_ENV'], (ENV['CI'] ? :ci : :not_ci))
187
- ```
141
+ * See the [examples](/examples)-folder for a more extensive Envfile
142
+ * See [the Envfile](https://github.com/eval/bunny_drain/blob/c54d7d977afb5e23a92da7a2fd0d39f6a7e29bf1/Envfile) for the bunndy_drain application
188
143
 
189
144
  ## Rails
190
145
 
data/envied.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.required_ruby_version = '>= 1.9.3'
22
- spec.add_dependency "virtus", '~> 1.0'
22
+ spec.add_dependency "coercible", '~> 1.0'
23
23
  spec.add_dependency "rack", "~> 1.4"
24
24
  spec.add_dependency "thor", "~> 0.15"
25
25
  spec.add_development_dependency "bundler", "~> 1.5"
@@ -0,0 +1,46 @@
1
+ # -*- mode: ruby -*-¬
2
+ # We allow defaults for local development (and local tests), but want our CI
3
+ # to mimic our production as much as possible.
4
+ # New developers that don't have RACK_ENV set, will in this way not be presented with a huge
5
+ # list of missing variables, as defaults are still enabled.
6
+ not_production_nor_ci = ->{ !(ENV['RACK_ENV'] == 'production' || ENV['CI']) }
7
+ enable_defaults!(&not_production_nor_ci)
8
+
9
+ # Your code will likely not use ENVied.RACK_ENV (better use Rails.env),
10
+ # we want it to be present though; heck, we're using it in this file!
11
+ variable :RACK_ENV
12
+
13
+ variable :FORCE_SSL, :Boolean, default: false
14
+ variable :PORT, :Integer, default: 3000
15
+ # generate the default value using the value of PORT:
16
+ variable :PUBLIC_HOST_WITH_PORT, :String, default: proc {|envied| "localhost:#{envied.PORT}" }
17
+
18
+ group :production do
19
+ variable :MAIL_PAAS_USERNAME
20
+ variable :DATABASE_URL
21
+ end
22
+
23
+ group :ci do
24
+ # ci-only stuff
25
+ end
26
+
27
+ group :not_ci do
28
+ # CI needs no puma-threads, and sidekiq-stuff etc.
29
+ # Define that here:
30
+ variable :MIN_THREADS, :Integer, default: 1
31
+ # more...
32
+ end
33
+
34
+ # Depending on our situation, we can now require the correct groups in our initialization-file:
35
+ # At local machines:
36
+ # ENVied.require(:default, :development, :not_ci) or
37
+ # ENVied.require(:default, :test, :not_ci)
38
+
39
+ # At the server:
40
+ # ENVied.require(:default, :production, :not_ci)
41
+
42
+ # At CI:
43
+ # ENVied.require(:default, :test, :ci)
44
+
45
+ # All in one line:
46
+ # ENVied.require(:default, ENV['RACK_ENV'], (ENV['CI'] ? :ci : :not_ci))
data/lib/envied.rb CHANGED
@@ -1,238 +1,56 @@
1
1
  require 'envied/version'
2
2
  require 'envied/cli'
3
- require 'virtus'
3
+ require 'envied/coercer'
4
+ require 'envied/variable'
5
+ require 'envied/configuration'
4
6
 
5
7
  class ENVied
6
- module Hashable
7
- def to_hash
8
- require 'rack/utils'
9
- ::Rack::Utils.parse_nested_query(self)
10
- end
11
- end
12
-
13
- module Arrayable
14
- def to_a
15
- self.split(/(?<!\\), ?/).map{|i| i.gsub(/\\,/,',') }
16
- end
17
- end
18
-
19
- class Configuration
20
- include Virtus.model
21
-
22
- def self.variable(name, type = :String, options = {})
23
- options = { default: nil, strict: true, group: self.current_group }.merge(options)
24
- type = Array if type == :Array
25
- attribute(name, type, options)
26
- end
27
-
28
- def self.group(name, &block)
29
- self.current_group = name.to_sym
30
- yield
31
- ensure
32
- self.current_group = :default
33
- end
34
-
35
- def self.enable_defaults
36
- (@enable_defaults ||= false).respond_to?(:call) ?
37
- @enable_defaults.call :
38
- @enable_defaults
39
- end
40
-
41
- def self.enable_defaults!(value = nil, &block)
42
- value ||= block if block_given?
43
- @enable_defaults = value
44
- end
45
-
46
- class << self
47
- alias_method :defaults_enabled?, :enable_defaults
48
- alias_method :enable_defaults=, :enable_defaults!
49
- attr_writer :current_group
50
- end
51
-
52
- def self.current_group
53
- @current_group ||= :default
54
- end
55
- end
56
-
57
- def self.configuration(options = {}, &block)
58
- if block_given?
59
- @configuration = build_configuration(&block).tap do |c|
60
- options.each {|k, v| c.public_send("#{k}=", v) }
61
- end
62
- end
63
- @configuration ||= build_configuration
64
- end
65
-
66
- def self.configure(options = {}, &block)
67
- deprecation_warning "ENVied.configure will be deprecated. Please generate an Envfile instead (see the envied command)."
68
- configuration(options, &block)
69
- end
70
-
71
8
  class << self
72
- attr_accessor :required_groups
73
- end
74
-
75
- def self.build_configuration(&block)
76
- Class.new(Configuration).tap do |c|
77
- c.instance_eval(&block) if block_given?
78
- end
9
+ attr_reader :env, :config
79
10
  end
80
11
 
81
12
  def self.require(*groups)
82
- groups.compact!
83
- @instance = nil
84
- if groups.any?
85
- self.required_groups = groups.map(&:to_sym)
86
- else
87
- self.required_groups = [:default]
88
- end
89
- ensure_configured!
13
+ @config ||= Configuration.load
14
+ @env ||= EnvProxy.new(@config, groups: required_groups(*groups))
15
+
90
16
  error_on_missing_variables!
91
17
  error_on_uncoercible_variables!
92
-
93
- _required_variables = required_variables
94
- group_configuration = build_configuration do
95
- _required_variables.each do |v|
96
- @attribute_set << v
97
- end
98
- end
99
- @instance = group_configuration.new(env)
100
- end
101
-
102
- def self.springified_require(*args)
103
- springify { ENVied.require(*args) }
104
- end
105
-
106
- def self.springify(&block)
107
- if defined?(Spring) && Spring.respond_to?(:watcher)
108
- Spring.after_fork(&block)
109
- else
110
- block.call
111
- end
112
- end
113
-
114
- def self.ensure_configured!
115
- # Backward compat: load Envfile only when it's present
116
- configure_via_envfile if envfile_exist?
117
- end
118
-
119
- def self.envfile
120
- File.expand_path('Envfile')
121
- end
122
-
123
- def self.envfile_exist?
124
- File.exist?(envfile)
125
- end
126
-
127
- def self.configure_via_envfile
128
- configuration { eval(File.read(ENVied.envfile)) }
129
18
  end
130
19
 
131
20
  def self.error_on_missing_variables!
132
- if missing_variable_names.any?
133
- raise "Please set the following ENV-variables: #{missing_variable_names.sort.join(',')}"
134
- end
21
+ names = env.missing_variables.map(&:name)
22
+ raise "The following environment variables should be set: #{names * ', '}" if names.any?
135
23
  end
136
24
 
137
25
  def self.error_on_uncoercible_variables!
138
- # TODO default values should have defined type
139
- if non_coercible_variables.any?
140
- single_error = "ENV['%{name}'] ('%{value}' can't be coerced to %{type})"
141
- errors = non_coercible_variables.map do |v|
142
- var_type = v.type.to_s.split("::").last
143
- single_error % { name: v.name, value: env_value_or_default(v), type: var_type }
144
- end.join ", "
145
-
146
- raise "Some ENV-variables are not coercible: #{errors}"
26
+ errors = env.uncoercible_variables.map do |v|
27
+ "%{name} ('%{value}' can't be coerced to %{type})" % {name: v.name, value: env.value_to_coerce(v), type: v.type }
147
28
  end
29
+ raise "The following environment variables are not coercible: #{errors.join(", ")}" if errors.any?
148
30
  end
149
31
 
150
- def self.env_value(variable)
151
- env[variable.name.to_s]
32
+ def self.required_groups(*groups)
33
+ result = groups.compact
34
+ result.any? ? result.map(&:to_sym) : [:default]
152
35
  end
153
36
 
154
- def self.env
155
- @env ||= begin
156
- Hash[ENV.to_hash.map {|k,v| [k, v.dup.extend(Hashable, Arrayable)] }]
37
+ def self.springify(&block)
38
+ if spring_enabled?
39
+ Spring.after_fork(&block)
40
+ else
41
+ block.call
157
42
  end
158
43
  end
159
44
 
160
- def self.env_value_or_default(variable)
161
- env_value(variable) || default_value(variable)
162
- end
163
-
164
- # Yields the assigned default for the variable.
165
- # When defaults are disabled, nil is returned.
166
- def self.default_value(variable)
167
- defaults_enabled? ? variable.default_value.value : nil
168
- end
169
-
170
- # A list of all configured variable names.
171
- #
172
- # @example
173
- # ENVied.required_variable_names
174
- # # => [:DATABASE_URL]
175
- #
176
- # @return [Array<Symbol>] the list of variable names
177
- def self.required_variable_names
178
- required_variables.map(&:name).map(&:to_sym)
179
- end
180
-
181
- def self.required_variables
182
- from_required_group = ->(var){ self.required_groups.include?(var.options[:group]) }
183
- configured_variables.to_a.keep_if(&from_required_group)
184
- end
185
-
186
- def self.configured_variables
187
- configuration.attribute_set.dup#.to_a.keep_if(&var_from_required_group)
188
- end
189
-
190
- def self.provided_variable_names
191
- ENV.keys.map(&:to_sym)
192
- end
193
-
194
- def self.non_coercible_variables
195
- required_variables.reject(&method(:variable_coercible?))
196
- end
197
-
198
- def self.variable_coercible?(variable)
199
- var_value = env_value_or_default(variable)
200
- return true if var_value.respond_to?(:call)
201
-
202
- !variable.coerce(var_value).nil?
203
- rescue Virtus::CoercionError
204
- return false
205
- end
206
-
207
- def self.missing_variable_names
208
- unprovided = required_variable_names - provided_variable_names
209
- unprovided -= names_of_required_variables_with_defaults if defaults_enabled?
210
- unprovided
211
- end
212
-
213
- def self.names_of_required_variables_with_defaults
214
- required_variables_with_defaults.map(&:name).map(&:to_sym)
215
- end
216
-
217
- def self.required_variables_with_defaults
218
- required_variables.map do |v|
219
- v unless v.default_value.value.nil?
220
- end.compact
221
- end
222
-
223
- def self.defaults_enabled?
224
- configuration.enable_defaults
45
+ def self.spring_enabled?
46
+ defined?(Spring) && Spring.respond_to?(:watcher)
225
47
  end
226
48
 
227
49
  def self.method_missing(method, *args, &block)
228
- respond_to_missing?(method) ? @instance.public_send(method, *args, &block) : super
50
+ respond_to_missing?(method) ? (env && env[method.to_s]) : super
229
51
  end
230
52
 
231
53
  def self.respond_to_missing?(method, include_private = false)
232
- @instance.respond_to?(method) || super
233
- end
234
-
235
- def self.deprecation_warning(msg)
236
- puts "DEPRECATION WARNING: #{msg}"
54
+ (env && env.has_key?(method)) || super
237
55
  end
238
56
  end
@@ -0,0 +1,77 @@
1
+ require 'coercible'
2
+
3
+ # Responsible for all string to type coercions.
4
+ class ENVied::Coercer
5
+ module CoercerExts
6
+ def to_array(str)
7
+ str.split(/(?<!\\),/).map{|i| i.gsub(/\\,/,',') }
8
+ end
9
+
10
+ def to_hash(str)
11
+ require 'rack/utils'
12
+ ::Rack::Utils.parse_query(str)
13
+ end
14
+ end
15
+ Coercible::Coercer::String.send(:include, CoercerExts)
16
+
17
+ # Coerce strings to specific type.
18
+ #
19
+ # @param string [String] the string to be coerced
20
+ # @param type [#to_sym] the type to coerce to
21
+ #
22
+ # @example
23
+ # ENVied::Coercer.new.coerce('1', :Integer)
24
+ # # => 1
25
+ #
26
+ # @return [type] the coerced string.
27
+ def coerce(string, type)
28
+ unless supported_type?(type)
29
+ raise ArgumentError, "#{type.inspect} is not supported type"
30
+ end
31
+ coerce_method_for(type.to_sym)[string]
32
+ end
33
+
34
+ def coerce_method_for(type)
35
+ return nil unless supported_type?(type)
36
+ coercer.method("to_#{type.downcase}")
37
+ end
38
+
39
+ def self.supported_types
40
+ @supported_types ||= begin
41
+ [:hash, :array, :time, :date, :symbol, :boolean, :integer, :string]
42
+ end
43
+ end
44
+
45
+ # Whether or not Coercer can coerce strings to the provided type.
46
+ #
47
+ # @param type [#to_sym] the type (case insensitive)
48
+ #
49
+ # @example
50
+ # ENVied::Coercer.supported_type?('string')
51
+ # # => true
52
+ #
53
+ # @return [Boolean] whether type is supported.
54
+ def self.supported_type?(type)
55
+ supported_types.include?(type.to_sym.downcase)
56
+ end
57
+
58
+ def supported_type?(type)
59
+ self.class.supported_type?(type)
60
+ end
61
+
62
+ def coercer
63
+ @coercer ||= Coercible::Coercer.new[String]
64
+ end
65
+
66
+ def coerced?(value)
67
+ !value.kind_of?(String)
68
+ end
69
+
70
+ def coercible?(string, type)
71
+ return false unless supported_type?(type)
72
+ coerce(string, type)
73
+ true
74
+ rescue Coercible::UnsupportedCoercion
75
+ false
76
+ end
77
+ end
@@ -0,0 +1,109 @@
1
+ class ENVied
2
+ class Configuration
3
+ attr_reader :current_group, :defaults_enabled
4
+
5
+ def initialize(options = {})
6
+ @defaults_enabled = options.fetch(:enable_defaults, false)
7
+ end
8
+
9
+ def self.load
10
+ new.tap do |v|
11
+ v.instance_eval(File.read(File.expand_path('Envfile')))
12
+ end
13
+ end
14
+
15
+ def enable_defaults!(value = nil, &block)
16
+ @defaults_enabled = (value.nil? ? block : value)
17
+ end
18
+
19
+ def defaults_enabled?
20
+ @defaults_enabled.respond_to?(:call) ?
21
+ @defaults_enabled.call :
22
+ @defaults_enabled
23
+ end
24
+
25
+ def variable(name, type = :String, options = {})
26
+ options[:group] = current_group if current_group
27
+ variables << ENVied::Variable.new(name, type, options)
28
+ end
29
+
30
+ def group(name, &block)
31
+ @current_group = name.to_sym
32
+ yield
33
+ ensure
34
+ @current_group = nil
35
+ end
36
+
37
+ def variables
38
+ @variables ||= []
39
+ end
40
+ end
41
+
42
+ # Responsible for anything related to the ENV.
43
+ class EnvProxy
44
+ attr_reader :config, :coercer, :groups
45
+
46
+ def initialize(config, options = {})
47
+ @config = config
48
+ @coercer = options.fetch(:coercer, ENVied::Coercer.new)
49
+ @groups = options.fetch(:groups, [])
50
+ end
51
+
52
+ def missing_variables
53
+ variables.select(&method(:missing?))
54
+ end
55
+
56
+ def uncoercible_variables
57
+ variables.reject(&method(:coerced?)).reject(&method(:coercible?))
58
+ end
59
+
60
+ def variables
61
+ @variables ||= begin
62
+ config.variables.select {|v| groups.include?(v.group) }
63
+ end
64
+ end
65
+
66
+ def variables_by_name
67
+ Hash[variables.map {|v| [v.name, v] }]
68
+ end
69
+
70
+ def [](name)
71
+ coerce(variables_by_name[name.to_sym])
72
+ end
73
+
74
+ def has_key?(name)
75
+ variables_by_name[name.to_sym]
76
+ end
77
+
78
+ def env_value_of(var)
79
+ ENV[var.name.to_s]
80
+ end
81
+
82
+ def default_value_of(var)
83
+ var.default_value(ENVied, var)
84
+ end
85
+
86
+ def value_to_coerce(var)
87
+ return env_value_of(var) unless env_value_of(var).nil?
88
+ config.defaults_enabled? ? default_value_of(var) : nil
89
+ end
90
+
91
+ def coerce(var)
92
+ coerced?(var) ?
93
+ value_to_coerce(var) :
94
+ coercer.coerce(value_to_coerce(var), var.type)
95
+ end
96
+
97
+ def coercible?(var)
98
+ coercer.coercible?(value_to_coerce(var), var.type)
99
+ end
100
+
101
+ def missing?(var)
102
+ value_to_coerce(var).nil?
103
+ end
104
+
105
+ def coerced?(var)
106
+ coercer.coerced?(value_to_coerce(var))
107
+ end
108
+ end
109
+ end
@@ -3,7 +3,7 @@
3
3
  # Check the config of a Heroku app against the defined variables in `Envfile`
4
4
 
5
5
  <%- if @app %>
6
- HEROKU_APP=<%= @app %> exec heroku config | bundle exec envied check:heroku --groups <%= @groups.join(",") %>
6
+ HEROKU_APP=<%= @app %> exec heroku config | bundle exec envied check:heroku --groups <%= @groups.join(" ") %>
7
7
  <%- else %>
8
- exec heroku config | bundle exec envied check:heroku --groups <%= @groups.join(",") %>
8
+ exec heroku config | bundle exec envied check:heroku --groups <%= @groups.join(" ") %>
9
9
  <%- end %>
@@ -0,0 +1,18 @@
1
+ class ENVied::Variable
2
+ attr_reader :name, :type, :group, :default
3
+
4
+ def initialize(name, type, options = {})
5
+ @name = name.to_sym
6
+ @type = type.to_sym
7
+ @group = options.fetch(:group, :default).to_sym
8
+ @default = options[:default]
9
+
10
+ #if !@default.is_a? String
11
+ # raise ArgumentError, "Default values should be strings (variable #{@name})"
12
+ #end
13
+ end
14
+
15
+ def default_value(*args)
16
+ default.respond_to?(:call) ? default[*args] : default
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  class ENVied
2
- VERSION = '0.7.0'
2
+ VERSION = '0.7.1'
3
3
  end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe ENVied::Coercer do
4
+ it { is_expected.to respond_to :coerce }
5
+
6
+ describe '#coerce' do
7
+ let(:coercer){ described_class.new }
8
+
9
+ def coerce_to(type)
10
+ ->(str){ coercer.coerce(str, type) }
11
+ end
12
+
13
+ describe 'string coercion' do
14
+ let(:coerce){ coerce_to(:String) }
15
+
16
+ it 'yields the input untouched' do
17
+ expect(coerce['1']).to eq '1'
18
+ expect(coerce[' 1']).to eq ' 1'
19
+ end
20
+ end
21
+
22
+ describe 'integer coercion' do
23
+ let(:coerce){ coerce_to(:Integer) }
24
+
25
+ it 'converts strings to integers' do
26
+ expect(coerce['1']).to eq 1
27
+ expect(coerce['-1']).to eq(-1)
28
+ end
29
+ end
30
+
31
+ describe 'boolean coercion' do
32
+ let(:coerce){ coerce_to(:Boolean) }
33
+
34
+ it "converts 'true' and 'false'" do
35
+ expect(coerce['true']).to eq true
36
+ expect(coerce['false']).to eq false
37
+ end
38
+
39
+ it "converts '1' and '0'" do
40
+ expect(coerce['1']).to eq true
41
+ expect(coerce['0']).to eq false
42
+ end
43
+ end
44
+
45
+ describe 'symbol coercion' do
46
+ let(:coerce){ coerce_to(:Symbol) }
47
+
48
+ it 'converts strings to symbols' do
49
+ expect(coerce['a']).to eq :a
50
+ expect(coerce['nice_symbol']).to eq :nice_symbol
51
+ end
52
+ end
53
+
54
+ describe 'date coercion' do
55
+ let(:coerce){ coerce_to(:Date) }
56
+
57
+ it 'converts strings to date' do
58
+ expect(coerce['2014-12-25']).to eq Date.parse('2014-12-25')
59
+ end
60
+ end
61
+
62
+ describe 'time coercion' do
63
+ let(:coerce){ coerce_to(:Time) }
64
+
65
+ it 'converts strings to time' do
66
+ expect(coerce['4:00']).to eq Time.parse('4:00')
67
+ end
68
+ end
69
+
70
+ describe 'array coercion' do
71
+ let(:coerce){ coerce_to(:Array) }
72
+
73
+ it 'converts strings to array' do
74
+ {
75
+ 'a,b' => ['a','b'],
76
+ ' a, b' => [' a',' b'],
77
+ 'apples,and\, of course\, pears' => ['apples','and, of course, pears'],
78
+ }.each do |i, o|
79
+ expect(coerce[i]).to eq o
80
+ end
81
+ end
82
+ end
83
+
84
+ describe 'hash coercion' do
85
+ let(:coerce){ coerce_to(:Hash) }
86
+
87
+ it 'converts strings to hashes' do
88
+ {
89
+ 'a=1' => {'a' => '1'},
90
+ 'a=1&b=2' => {'a' => '1', 'b' => '2'},
91
+ 'a=&b=2' => {'a' => '', 'b' => '2'},
92
+ 'a&b=2' => {'a' => nil, 'b' => '2'},
93
+ }.each do |i, o|
94
+ expect(coerce[i]).to eq o
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe ENVied::Configuration do
4
+ it { is_expected.to respond_to :variable }
5
+ it { is_expected.to respond_to :enable_defaults! }
6
+ it { is_expected.to respond_to :defaults_enabled? }
7
+
8
+ describe '#variable' do
9
+ it 'results in an added variable' do
10
+
11
+ end
12
+ end
13
+
14
+ describe 'defaults' do
15
+ it 'is disabled by default' do
16
+ expect(subject.defaults_enabled?).to_not be
17
+ end
18
+
19
+ describe '#enable_defaults!' do
20
+ it 'can be passed a value' do
21
+ expect {
22
+ subject.enable_defaults!(true)
23
+ }.to change { subject.defaults_enabled? }
24
+ end
25
+
26
+ it 'can be passed a block' do
27
+ expect {
28
+ subject.enable_defaults! { true }
29
+ }.to change { subject.defaults_enabled? }.to(true)
30
+ end
31
+ end
32
+ end
33
+ end
data/spec/envied_spec.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ENVied do
4
- subject { described_class }
4
+ describe 'class' do
5
+ subject { described_class }
5
6
 
6
- it { should respond_to :require }
7
- it { should respond_to :configure }
7
+ it { is_expected.to respond_to :require }
8
+ end
9
+
10
+ describe 'responding to methods that are variables' do
11
+ end
8
12
 
9
13
  before do
10
14
  reset_env
@@ -12,7 +16,7 @@ describe ENVied do
12
16
  end
13
17
 
14
18
  def reset_configuration
15
- ENVied.instance_eval { @configuration = nil }
19
+ ENVied.instance_eval { @config = nil }
16
20
  end
17
21
 
18
22
  def reset_env
@@ -27,16 +31,19 @@ describe ENVied do
27
31
  end
28
32
 
29
33
  def configure(options = {}, &block)
30
- described_class.configuration(options, &block)
34
+ ENVied.instance_eval do
35
+ @config = ENVied::Configuration.new(options).tap{|c| c.instance_eval(&block)}
36
+ end
31
37
  self
32
38
  end
33
39
 
34
40
  def configured_with(hash = {})
35
- described_class.configuration do
41
+ config = ENVied::Configuration.new.tap do |c|
36
42
  hash.each do |name, type|
37
- variable(name, *type)
43
+ c.variable(name, *type)
38
44
  end
39
45
  end
46
+ ENVied.instance_eval{ @config = config }
40
47
  self
41
48
  end
42
49
 
@@ -53,14 +60,14 @@ describe ENVied do
53
60
  configured_with(a: :Integer).and_ENV({'a' => '1'})
54
61
  described_class.require
55
62
 
56
- is_expected.to respond_to :a
63
+ expect(described_class).to respond_to :a
57
64
  end
58
65
 
59
66
  it 'responds not to unconfigured variables' do
60
67
  unconfigured.and_ENV({'A' => '1'})
61
68
  described_class.require
62
69
 
63
- is_expected.to_not respond_to :B
70
+ expect(described_class).to_not respond_to :B
64
71
  end
65
72
 
66
73
  context 'ENV contains not all configured variables' do
@@ -69,7 +76,7 @@ describe ENVied do
69
76
  specify do
70
77
  expect {
71
78
  ENVied.require
72
- }.to raise_error(/set the following ENV-variables: a/)
79
+ }.to raise_error(/The following environment variables should be set: a/)
73
80
  end
74
81
  end
75
82
 
@@ -79,14 +86,14 @@ describe ENVied do
79
86
  specify do
80
87
  expect {
81
88
  ENVied.require
82
- }.to raise_error(/ENV\['A'\] \('NaN' can't be coerced to Integer/)
89
+ }.to raise_error(/A \('NaN' can't be coerced to Integer/)
83
90
  end
84
91
  end
85
92
 
86
93
  context 'bug: default value "false" is not coercible' do
87
94
  before {
88
95
  configure(enable_defaults: true) do
89
- variable :FORCE_SSL, :Boolean, default: false
96
+ variable :FORCE_SSL, :Boolean, default: true
90
97
  end
91
98
  }
92
99
 
@@ -99,35 +106,36 @@ describe ENVied do
99
106
 
100
107
  describe 'defaults' do
101
108
  describe 'setting' do
102
- subject { described_class.configuration }
109
+ subject { described_class.config }
110
+ #subject { ENVied::Configuration.new }
103
111
 
104
- it 'is disabled by default' do
105
- expect(subject.enable_defaults).to_not be
106
- end
112
+ #it 'is disabled by default' do
113
+ # expect(subject.defaults_enabled?).to_not be
114
+ #end
107
115
 
108
116
  it 'can be enabled via #configure' do
109
117
  configure(enable_defaults: true){ }
110
118
 
111
- expect(subject.enable_defaults).to be
119
+ expect(subject.defaults_enabled?).to be
112
120
  end
113
121
 
114
122
  it 'can be enabled via a configure-block' do
115
- configure { self.enable_defaults = true }
123
+ configure { self.enable_defaults!(true) }
116
124
 
117
- expect(subject.enable_defaults).to be
125
+ expect(subject.defaults_enabled?).to be
118
126
  end
119
127
 
120
128
  it 'can be assigned a Proc' do
121
- configure { self.enable_defaults = -> { true } }
129
+ configure { self.enable_defaults! { true } }
122
130
 
123
- expect(subject.enable_defaults).to be
131
+ expect(subject.defaults_enabled?).to be
124
132
  end
125
133
  end
126
134
 
127
135
  describe 'assigning' do
128
136
  it 'can be a value' do
129
137
  configure(enable_defaults: true) do
130
- variable :A, :Integer, default: 1
138
+ variable :A, :Integer, default: '1'
131
139
  end
132
140
  described_class.require
133
141
 
@@ -136,7 +144,7 @@ describe ENVied do
136
144
 
137
145
  it 'can be a Proc' do
138
146
  configure(enable_defaults: true) do
139
- variable :A, :Integer, default: proc { 1 }
147
+ variable :A, :Integer, default: proc { "1" }
140
148
  end
141
149
  described_class.require
142
150
 
@@ -145,7 +153,7 @@ describe ENVied do
145
153
 
146
154
  it 'is ignored if defaults are disabled' do
147
155
  configure(enable_defaults: false) do
148
- variable :A, :Integer, default: 1
156
+ variable :A, :Integer, default: "1"
149
157
  end.and_no_ENV
150
158
 
151
159
  expect {
@@ -153,9 +161,9 @@ describe ENVied do
153
161
  }.to raise_error
154
162
  end
155
163
 
156
- it 'is is ignored if ENV is provided' do
164
+ it 'is ignored if ENV is provided' do
157
165
  configure(enable_defaults: true) do
158
- variable :A, :Integer, default: 1
166
+ variable :A, :Integer, default: "1"
159
167
  end.and_ENV('A' => '2')
160
168
  described_class.require
161
169
 
@@ -257,7 +265,7 @@ describe ENVied do
257
265
  end
258
266
 
259
267
  it 'yields array from string' do
260
- expect(ENVied.moar).to eq ['a','b','and, c']
268
+ expect(ENVied.moar).to eq ['a',' b',' and, c']
261
269
  end
262
270
  end
263
271
  end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe ENVied::Variable do
4
+ def variable(*args)
5
+ described_class.new(*args)
6
+ end
7
+
8
+ def set_env(env)
9
+ stub_const("ENV", env)
10
+ end
11
+
12
+ describe 'an instance' do
13
+ subject { variable(:A, :String) }
14
+ end
15
+ end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: envied
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gert Goet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-23 00:00:00.000000000 Z
11
+ date: 2014-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: virtus
14
+ name: coercible
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
@@ -112,14 +112,21 @@ files:
112
112
  - Rakefile
113
113
  - bin/envied
114
114
  - envied.gemspec
115
+ - examples/extensive_envfile
115
116
  - lib/envied.rb
116
117
  - lib/envied/cli.rb
118
+ - lib/envied/coercer.rb
119
+ - lib/envied/configuration.rb
117
120
  - lib/envied/templates/Envfile.tt
118
121
  - lib/envied/templates/heroku-env-check.tt
119
122
  - lib/envied/templates/rails-initializer.tt
123
+ - lib/envied/variable.rb
120
124
  - lib/envied/version.rb
125
+ - spec/coercer_spec.rb
126
+ - spec/configuration_spec.rb
121
127
  - spec/envied_spec.rb
122
128
  - spec/spec_helper.rb
129
+ - spec/variable_spec.rb
123
130
  homepage: https://github.com/eval/envied
124
131
  licenses:
125
132
  - MIT
@@ -145,5 +152,8 @@ signing_key:
145
152
  specification_version: 4
146
153
  summary: Ensure presence and type of ENV-variables
147
154
  test_files:
155
+ - spec/coercer_spec.rb
156
+ - spec/configuration_spec.rb
148
157
  - spec/envied_spec.rb
149
158
  - spec/spec_helper.rb
159
+ - spec/variable_spec.rb