invar 0.7.0 → 0.9.0

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
  SHA256:
3
- metadata.gz: 62fcbaf0763469c256af4915bdae35ded8a2691c647356a24bd6e68635bf3030
4
- data.tar.gz: a54dce1295a10292f208318faeefe43b0c279934154f2c6f112e5b22278126a6
3
+ metadata.gz: c7bb218e77d7ce1acc82bf0160cc7386529f7e32e5a56cd2eea608bd2a191ad6
4
+ data.tar.gz: 6b209fa6041be75736d28783d996c8fe0ee1e529bdde7d94dcb8e1bbda23423d
5
5
  SHA512:
6
- metadata.gz: 5c0d9b6ae497f3e1ca0ba6701cdc99c1b8408ceabcdfd1970b67c465576f2cd0439caf92fc6413eb40db21d7289d5f5cab495adeb3499bdeef3c3a4da43a0e2b
7
- data.tar.gz: 206b350e643afabd1892db2cf393b375e7555fe85f65d56705c718757ce172d4360fb9f6c4102b70da64223ee42a9d0ef960298240bce9e755b91512f7d1406a
6
+ metadata.gz: da15b60c94678b1dc9a5e002b3f4bba02d7b2e1a77923483a2a5427839b0cff0fd0e137ce9e1ca847e6b493bb134be97f419a20bef7903adabe8de8b0f163ae9
7
+ data.tar.gz: 2d60ab3a7f5374478fe2ab872083ef4159620097e0cb65575eb6e6956b669c9fceaa86a39c085c637df125cebfc33f9ba52170a1a2389dce713de9d77da09c9d
data/.rubocop.yml CHANGED
@@ -4,17 +4,6 @@ AllCops:
4
4
  Exclude:
5
5
  - 'bin/*'
6
6
 
7
- TargetRubyVersion: 2.7
8
-
9
- Layout/LineLength:
10
- Exclude:
11
- - 'spec/**/*.rb'
12
-
13
- # setting to 6 to match RubyMine autoformat
14
- Layout/FirstArrayElementIndentation:
15
- IndentationWidth: 6
16
-
17
-
18
7
  # rspec blocks are huge by design
19
8
  Metrics/BlockLength:
20
9
  Exclude:
@@ -23,4 +12,3 @@ Metrics/BlockLength:
23
12
  Metrics/ModuleLength:
24
13
  Exclude:
25
14
  - 'spec/**/*.rb'
26
-
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.7.8
1
+ ruby-3.1.4
data/.simplecov ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.start do
4
+ coverage_dir '.coverage'
5
+ enable_coverage :branch
6
+
7
+ root __dir__
8
+ end
data/Gemfile CHANGED
@@ -2,14 +2,19 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
+ # Gem's dependencies in invar.gemspec
6
+ gemspec
7
+
5
8
  group :development do
6
9
  gem 'bundler', '~> 2.3'
7
- gem 'fakefs', '~> 2.5'
8
10
  gem 'rake', '~> 13.0'
9
- gem 'rspec', '~> 3.12'
10
- gem 'simplecov', '~> 0.22'
11
+ gem 'rubocop', '~> 1.56'
12
+ gem 'rubocop-performance', '~> 1.19'
11
13
  gem 'yard', '~> 0.9'
12
14
  end
13
15
 
14
- # Specify your gem's dependencies in invar.gemspec
15
- gemspec
16
+ group :test do
17
+ gem 'fakefs', '~> 2.5'
18
+ gem 'rspec', '~> 3.12'
19
+ gem 'simplecov', '~> 0.22'
20
+ end
data/README.md CHANGED
@@ -26,7 +26,8 @@ variables have some downsides:
26
26
  * Cannot be easily checked against a schema for early error detection
27
27
  * Ruby's core ENV does not accept symbols as keys (a minor nuisance, but it counts)
28
28
 
29
- > **Fun Fact:** Invar is named for an [alloy used in clockmaking](https://en.wikipedia.org/wiki/Invar) - it's short for "**invar**iable".
29
+ > **Fun Fact:** Invar is named for an [alloy used in clockmaking](https://en.wikipedia.org/wiki/Invar) - it's short
30
+ > for "**invar**iable".
30
31
 
31
32
  ### Features
32
33
 
@@ -216,6 +217,10 @@ To open the file your system's default text editor (eg. nano):
216
217
 
217
218
  bundle exec rake invar:config
218
219
 
220
+ > **Automation tip** You can provide new content over STDIN
221
+ >
222
+ > bundle exec rake invar:config < config_template.yml
223
+
219
224
  #### Edit Secrets File
220
225
 
221
226
  To edit the secrets file, run this and provide the file's encryption key:
@@ -223,7 +228,27 @@ To edit the secrets file, run this and provide the file's encryption key:
223
228
  bundle exec rake invar:secrets
224
229
 
225
230
  The file will be decrypted and opened in your default editor like the config file. Once you have exited the editor, it
226
- will be re-encrypted. Remember to save your changes!
231
+ will be re-encrypted. **Remember to save your changes!**
232
+
233
+ > **Automation tip** You can set the current encryption key in the `LOCKBOX_MASTER_KEY` environment variable:
234
+ >
235
+ > LOCKBOX_MASTER_KEY=sooper_sekret_key_here bundle exec rake invar:secrets
236
+
237
+ > **Automation tip** Like invar:config, you can provide new content over STDIN
238
+ >
239
+ > bundle exec rake invar:secrets < secrets_template.yml
240
+
241
+ #### Rotating the Secrets File Encryption Key
242
+
243
+ To re-encrypt the secrets file, run this and provide the file's current encryption key:
244
+
245
+ bundle exec rake invar:rotate
246
+
247
+ A new encryption key will be generated just like using `invar:init`.
248
+
249
+ > **Automation tip** you can set the current encryption key in the `LOCKBOX_MASTER_KEY` environment variable:
250
+ >
251
+ > LOCKBOX_MASTER_KEY=sooper_sekret_key_here bundle exec rake invar:rotate
227
252
 
228
253
  ### Code
229
254
 
data/RELEASE_NOTES.md CHANGED
@@ -13,6 +13,49 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
13
13
 
14
14
  ### Minor Changes
15
15
 
16
+ * none
17
+
18
+ ### Bugfixes
19
+
20
+ * none
21
+
22
+ ## [0.9.0] - 2023-09-24
23
+
24
+ ### Major Changes
25
+
26
+ * none
27
+
28
+ ### Minor Changes
29
+
30
+ * Added Rake task for secrets file key rotation
31
+ * Added ability for invar:config and invar:secrets to take replacement content over `STDIN` instead of live editing
32
+
33
+ ### Bugfixes
34
+
35
+ * none
36
+
37
+ ## [0.8.0] - 2023-09-13
38
+
39
+ ### Major Changes
40
+
41
+ * none
42
+
43
+ ### Minor Changes
44
+
45
+ * Increased minimum Ruby to 3.1
46
+
47
+ ### Bugfixes
48
+
49
+ * none
50
+
51
+ ## [0.7.0] - 2023-08-06
52
+
53
+ ### Major Changes
54
+
55
+ * none
56
+
57
+ ### Minor Changes
58
+
16
59
  * Tweaked file missing error messages
17
60
  * Extracted testing helper features into a separate module
18
61
  * Added support for multiple after_load hooks
data/invar.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ['lib']
29
29
 
30
- spec.required_ruby_version = '>= 2.7'
30
+ spec.required_ruby_version = '>= 3.1'
31
31
 
32
32
  spec.add_dependency 'dry-schema', '>= 1.0'
33
33
  spec.add_dependency 'lockbox', '>= 1.0'
data/lib/invar/errors.rb CHANGED
@@ -1,5 +1,7 @@
1
- module Invar
1
+ # frozen_string_literal: true
2
2
 
3
+ # :nodoc:
4
+ module Invar
3
5
  # Raised when no config file can be found within the search paths.
4
6
  class MissingConfigFileError < RuntimeError
5
7
  end
@@ -41,11 +43,11 @@ module Invar
41
43
  require 'invar/test'
42
44
  HINT
43
45
 
44
- PRETEND_MSG = "Method 'Invar::Scope#pretend' is defined in the testing extension. #{ HINT }"
45
- HOOK_MSG = "Methods 'Invar.after_load and clear_hooks' are defined in the testing extension. #{ HINT }"
46
+ PRETEND_MSG = "Method 'Invar::Scope#pretend' is defined in the testing extension. #{ HINT }".freeze
47
+ HOOK_MSG = "Methods 'Invar.after_load and clear_hooks' are defined in the testing extension. #{ HINT }".freeze
46
48
  end
47
49
 
48
50
  # Raised when schema validation fails
49
51
  class SchemaValidationError < RuntimeError
50
52
  end
51
- end
53
+ end
@@ -9,7 +9,7 @@ module Invar
9
9
  # Verifies a file is secure
10
10
  class PrivateFile
11
11
  extend Forwardable
12
- def_delegators :@delegate_sd_obj, :stat, :to_s, :basename, :==, :chmod
12
+ def_delegators :@delegate_sd_obj, :stat, :to_s, :basename, :dirname, :extname, :==, :chmod, :rename, :write
13
13
 
14
14
  # Mask for limiting to the lowest three octal digits (which store permissions)
15
15
  PERMISSIONS_MASK = 0o777
@@ -30,8 +30,8 @@ module Invar
30
30
  #
31
31
  # @param (see #define)
32
32
  # @see Tasks#define
33
- def self.define(**args, &block)
34
- new.define(**args, &block)
33
+ def self.define(...)
34
+ new.define(...)
35
35
  end
36
36
 
37
37
  # Defines helpful Rake tasks for the given namespace.
@@ -51,7 +51,7 @@ module Invar
51
51
  define_init_task(app_namespace)
52
52
 
53
53
  define_config_task(app_namespace)
54
- define_secrets_task(app_namespace)
54
+ define_secrets_tasks(app_namespace)
55
55
 
56
56
  define_info_tasks(app_namespace)
57
57
  end
@@ -62,8 +62,8 @@ module Invar
62
62
  task :init, [:mode] do |_task, args|
63
63
  mode = args.mode
64
64
 
65
- config = ::Invar::Rake::Tasks::ConfigTask.new(app_namespace)
66
- secrets = ::Invar::Rake::Tasks::SecretTask.new(app_namespace)
65
+ config = ::Invar::Rake::Tasks::ConfigFileHandler.new(app_namespace)
66
+ secrets = ::Invar::Rake::Tasks::SecretsFileHandler.new(app_namespace)
67
67
 
68
68
  case mode
69
69
  when 'config'
@@ -71,7 +71,7 @@ module Invar
71
71
  when 'secrets'
72
72
  config = nil
73
73
  else
74
- raise "unknown mode #{ mode }. Must be one of 'config' or 'secrets'" unless mode.nil?
74
+ raise ArgumentError, "unknown mode '#{ mode }'. Must be one of 'config' or 'secrets'" unless mode.nil?
75
75
  end
76
76
 
77
77
  assert_init_conditions(config&.file_path, secrets&.file_path)
@@ -84,39 +84,44 @@ module Invar
84
84
  def define_config_task(app_namespace)
85
85
  desc 'Edit the config in your default editor'
86
86
  task :configs do
87
- ::Invar::Rake::Tasks::ConfigTask.new(app_namespace).edit
87
+ ::Invar::Rake::Tasks::ConfigFileHandler.new(app_namespace).edit
88
88
  end
89
89
 
90
90
  # alias
91
91
  task config: ['configs']
92
92
  end
93
93
 
94
- def define_secrets_task(app_namespace)
94
+ def define_secrets_tasks(app_namespace)
95
95
  desc 'Edit the encrypted secrets file in your default editor'
96
96
  task :secrets do
97
- ::Invar::Rake::Tasks::SecretTask.new(app_namespace).edit
97
+ ::Invar::Rake::Tasks::SecretsFileHandler.new(app_namespace).edit
98
98
  end
99
99
 
100
100
  # alias
101
101
  task secret: ['secrets']
102
+
103
+ desc 'Encrypt the secrets file with a new generated key'
104
+ task :rotate do
105
+ ::Invar::Rake::Tasks::SecretsFileHandler.new(app_namespace).rotate
106
+ end
102
107
  end
103
108
 
104
109
  def define_info_tasks(app_namespace)
105
110
  desc 'Show directories to be searched for the given namespace'
106
111
  task :paths do
107
- ::Invar::Rake::Tasks::StateTask.new(app_namespace).show_paths
112
+ ::Invar::Rake::Tasks::StatusHandler.new(app_namespace).show_paths
108
113
  end
109
114
  end
110
115
 
111
116
  def assert_init_conditions(config_file, secrets_file)
112
117
  return unless config_file&.exist? || secrets_file&.exist?
113
118
 
114
- msg = if !config_file&.exist?
119
+ msg = if !config_file.exist?
115
120
  <<~MSG
116
121
  Abort: Secrets file already exists (#{ secrets_file })
117
122
  Run this to init only the config file: bundle exec rake tasks invar:init[config]
118
123
  MSG
119
- elsif !secrets_file&.exist?
124
+ elsif !secrets_file.exist?
120
125
  <<~MSG
121
126
  Abort: Config file already exists (#{ config_file })
122
127
  Run this to init only the secrets file: bundle exec rake tasks invar:init[secrets]
@@ -133,7 +138,7 @@ module Invar
133
138
  end
134
139
 
135
140
  # Tasks that use a namespace for file searching
136
- class NamespacedTask
141
+ class NamespacedFileTask
137
142
  def initialize(namespace)
138
143
  @locator = FileLocator.new(namespace)
139
144
  end
@@ -150,11 +155,9 @@ module Invar
150
155
  end
151
156
 
152
157
  # Configuration file actions.
153
- class ConfigTask < NamespacedTask
158
+ class ConfigFileHandler < NamespacedFileTask
154
159
  # Creates a config file in the appropriate location
155
160
  def create
156
- raise 'File already exists' if file_path.exist?
157
-
158
161
  config_dir.mkpath
159
162
  file_path.write CONFIG_TEMPLATE
160
163
  file_path.chmod 0o600
@@ -164,79 +167,108 @@ module Invar
164
167
 
165
168
  # Edits the existing config file in the appropriate location
166
169
  def edit
167
- configs_file = begin
168
- @locator.find('config.yml')
169
- rescue ::Invar::FileLocator::FileNotFoundError => e
170
- warn <<~ERR
171
- Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
172
- #{ CREATE_SUGGESTION }
173
- ERR
174
- exit 1
175
- end
176
-
177
- system(ENV.fetch('EDITOR', 'editor'), configs_file.to_s, exception: true)
178
-
179
- warn "File saved to: #{ configs_file }"
170
+ content = $stdin.tty? ? nil : $stdin.read
171
+ file_path = configs_file
172
+
173
+ if content
174
+ file_path.write content
175
+ else
176
+ system ENV.fetch('EDITOR', 'editor'), file_path.to_s, exception: true
177
+ end
178
+
179
+ warn "File saved to: #{ file_path }"
180
180
  end
181
181
 
182
182
  private
183
183
 
184
+ def configs_file
185
+ @locator.find 'config.yml'
186
+ rescue ::Invar::FileLocator::FileNotFoundError => e
187
+ warn <<~ERR
188
+ Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
189
+ #{ CREATE_SUGGESTION }
190
+ ERR
191
+ exit 1
192
+ end
193
+
184
194
  def filename
185
195
  'config.yml'
186
196
  end
187
197
  end
188
198
 
189
199
  # Secrets file actions.
190
- class SecretTask < NamespacedTask
200
+ class SecretsFileHandler < NamespacedFileTask
191
201
  # Instructions hint for how to handle secret keys.
192
202
  SECRETS_INSTRUCTIONS = <<~INST
193
- Save this key to a secure password manager. You will need it to edit the secrets.yml file.
203
+ Generated key. Save this key to a secure password manager, you will need it to edit the secrets.yml file:
194
204
  INST
195
205
 
196
- # Creates a new encrypted secrets file and prints the generated encryption key to STDOUT
197
- def create
198
- raise 'File already exists' if file_path.exist?
206
+ SWAP_EXT = 'tmp'
199
207
 
208
+ # Creates a new encrypted secrets file and prints the generated encryption key to STDOUT
209
+ def create(content: SECRETS_TEMPLATE)
200
210
  encryption_key = Lockbox.generate_key
201
211
 
202
212
  write_encrypted_file(file_path,
203
213
  encryption_key: encryption_key,
204
- content: SECRETS_TEMPLATE,
214
+ content: content,
205
215
  permissions: PrivateFile::DEFAULT_PERMISSIONS)
206
216
 
207
- warn "Created file: #{ file_path }"
208
-
209
217
  warn SECRETS_INSTRUCTIONS
210
- warn 'Generated key is:'
211
218
  puts encryption_key
212
219
  end
213
220
 
214
- # Opens an editor for the decrypted contents of the secrets file. After closing the editor, the file will be
215
- # updated with the new encrypted contents.
221
+ # Updates the file with new content.
222
+ #
223
+ # Either the content is provided over STDIN or the default editor is opened with the decrypted contents of
224
+ # the secrets file. After closing the editor, the file will be updated with the new encrypted contents.
216
225
  def edit
217
- secrets_file = begin
218
- @locator.find('secrets.yml')
219
- rescue ::Invar::FileLocator::FileNotFoundError => e
220
- warn <<~ERR
221
- Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
222
- #{ CREATE_SUGGESTION }
223
- ERR
224
- exit 1
225
- end
226
-
227
- edit_encrypted_file(secrets_file)
226
+ content = $stdin.tty? ? nil : $stdin.read
227
+
228
+ edit_encrypted_file(secrets_file, content: content)
228
229
 
229
230
  warn "File saved to #{ secrets_file }"
230
231
  end
231
232
 
233
+ def rotate
234
+ file_path = secrets_file
235
+
236
+ decrypted = read_encrypted_file(file_path, encryption_key: determine_key(file_path))
237
+
238
+ swap_file = file_path.dirname / [file_path.basename, SWAP_EXT].join('.')
239
+ file_path.rename swap_file
240
+
241
+ begin
242
+ create content: decrypted
243
+ swap_file.delete
244
+ rescue StandardError
245
+ swap_file.rename file_path.to_s
246
+ end
247
+ end
248
+
232
249
  private
233
250
 
251
+ def secrets_file
252
+ @locator.find 'secrets.yml'
253
+ rescue ::Invar::FileLocator::FileNotFoundError => e
254
+ warn <<~ERR
255
+ Abort: #{ e.message }. Searched in: #{ @locator.search_paths.join(', ') }
256
+ #{ CREATE_SUGGESTION }
257
+ ERR
258
+ exit 1
259
+ end
260
+
234
261
  def filename
235
262
  'secrets.yml'
236
263
  end
237
264
 
265
+ def read_encrypted_file(file_path, encryption_key:)
266
+ lockbox = build_lockbox(encryption_key)
267
+ lockbox.decrypt(file_path.binread)
268
+ end
269
+
238
270
  def write_encrypted_file(file_path, encryption_key:, content:, permissions: nil)
239
- lockbox = Lockbox.new(key: encryption_key)
271
+ lockbox = build_lockbox(encryption_key)
240
272
 
241
273
  encrypted_data = lockbox.encrypt(content)
242
274
 
@@ -244,23 +276,27 @@ module Invar
244
276
  # TODO: replace File.opens with photo_path.binwrite(uri.data) once FakeFS can handle it
245
277
  File.open(file_path.to_s, 'wb') { |f| f.write encrypted_data }
246
278
  file_path.chmod permissions if permissions
279
+
280
+ warn "Saved file: #{ file_path }"
247
281
  end
248
282
 
249
- def edit_encrypted_file(file_path)
283
+ def edit_encrypted_file(file_path, content: nil)
250
284
  encryption_key = determine_key(file_path)
251
285
 
252
- lockbox = build_lockbox(encryption_key)
286
+ content ||= invoke_editor(file_path, encryption_key: encryption_key)
287
+
288
+ write_encrypted_file(file_path, encryption_key: encryption_key, content: content)
289
+ end
253
290
 
254
- file_str = Tempfile.create(file_path.basename.to_s) do |tmp_file|
255
- decrypted = lockbox.decrypt(file_path.binread)
291
+ def invoke_editor(file_path, encryption_key:)
292
+ Tempfile.create(file_path.basename.to_s) do |tmp_file|
293
+ decrypted = read_encrypted_file(file_path, encryption_key: encryption_key)
256
294
 
257
295
  tmp_file.write(decrypted)
258
296
  tmp_file.rewind # rewind needed because file does not get closed after write
259
297
  system(ENV.fetch('EDITOR', 'editor'), tmp_file.path, exception: true)
260
298
  tmp_file.read
261
299
  end
262
-
263
- write_encrypted_file(file_path, encryption_key: encryption_key, content: file_str)
264
300
  end
265
301
 
266
302
  def determine_key(file_path)
@@ -282,7 +318,7 @@ module Invar
282
318
  end
283
319
 
284
320
  # General status tasks
285
- class StateTask < NamespacedTask
321
+ class StatusHandler < NamespacedFileTask
286
322
  # Prints the current paths to be searched in
287
323
  def show_paths
288
324
  warn @locator.search_paths.join("\n")
data/lib/invar/reality.rb CHANGED
@@ -78,8 +78,9 @@ module Invar
78
78
  begin
79
79
  @secrets = Scope.new(load_secrets(locator, decryption_keyfile || DEFAULT_KEY_FILE_NAME))
80
80
  rescue FileLocator::FileNotFoundError
81
+ hint = "Create encrypted secrets.yml in one of these locations: #{ search_paths }"
81
82
  raise MissingSecretsFileError,
82
- "No Invar secrets file found. Create encrypted secrets.yml in one of these locations: #{ search_paths }"
83
+ "No Invar secrets file found. #{ hint }"
83
84
  end
84
85
 
85
86
  freeze
data/lib/invar/scope.rb CHANGED
@@ -26,11 +26,9 @@ module Invar
26
26
  alias [] fetch
27
27
 
28
28
  def method_missing(symbol, *args)
29
- if symbol == :pretend
30
- raise ::Invar::ImmutableRealityError, ::Invar::ImmutableRealityError::PRETEND_MSG
31
- else
32
- super
33
- end
29
+ raise ::Invar::ImmutableRealityError, ::Invar::ImmutableRealityError::PRETEND_MSG if symbol == :pretend
30
+
31
+ super
34
32
  end
35
33
 
36
34
  # Returns a hash representation of this scope and subscopes.
data/lib/invar/test.rb CHANGED
@@ -1,16 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Specifically not calling require 'invar' here to force applications to need to include it themselves,
4
- # preventing the situation where test suites include application dependencies for them and breaking when
3
+ # Specifically not calling require 'invar' in this file to force applications to need to include it themselves,
4
+ # avoiding the situation where test suites include application dependencies for them and breaking when
5
5
  # the app is run without the test suite
6
6
 
7
+ # :nodoc:
7
8
  module Invar
8
9
  # Namespace module containing mixins for parts of the main gem to enable modifications and data control
9
10
  # in automated testing, while remaining immutable in the main gem and real runtime usage.
10
11
  module TestExtension
12
+ # Methods to extend the Reality class
11
13
  module RealityMethods
12
14
  class << self
13
15
  attr_accessor :__after_load_hooks__
16
+
14
17
  RealityMethods.__after_load_hooks__ = []
15
18
  end
16
19
 
data/lib/invar/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Invar
4
4
  # Current version of the gem
5
- VERSION = '0.7.0'
5
+ VERSION = '0.9.0'
6
6
  end
data/lib/invar.rb CHANGED
@@ -18,9 +18,9 @@ module Invar
18
18
  def method_missing(meth)
19
19
  if [:after_load, :clear_hooks].include? meth
20
20
  raise ::Invar::ImmutableRealityError, ::Invar::ImmutableRealityError::HOOK_MSG
21
- else
22
- super
23
21
  end
22
+
23
+ super
24
24
  end
25
25
  end
26
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: invar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robin Miller
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-06 00:00:00.000000000 Z
11
+ date: 2023-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-schema
@@ -51,6 +51,7 @@ files:
51
51
  - ".rspec"
52
52
  - ".rubocop.yml"
53
53
  - ".ruby-version"
54
+ - ".simplecov"
54
55
  - CODE_OF_CONDUCT.md
55
56
  - Gemfile
56
57
  - LICENSE.txt
@@ -80,14 +81,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
80
81
  requirements:
81
82
  - - ">="
82
83
  - !ruby/object:Gem::Version
83
- version: '2.7'
84
+ version: '3.1'
84
85
  required_rubygems_version: !ruby/object:Gem::Requirement
85
86
  requirements:
86
87
  - - ">="
87
88
  - !ruby/object:Gem::Version
88
89
  version: '0'
89
90
  requirements: []
90
- rubygems_version: 3.4.10
91
+ rubygems_version: 3.4.18
91
92
  signing_key:
92
93
  specification_version: 4
93
94
  summary: Single source of truth for environmental configuration.