envied 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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