prefab-cloud-ruby 0.24.1 → 0.24.3

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
  SHA256:
3
- metadata.gz: 9b54e4d5e0b52064cc19b41e33d9c7552cc8534bcab59cc42b48e4fc77fd5b58
4
- data.tar.gz: bd6901fcb99e35fc859ae382fb7d0354c1cd484fb75a1dbba27d0d0e07cdb397
3
+ metadata.gz: 29a3d9257f166fad534f81a44879cd82abc0cdd729714ab6feb2423d7b913a4d
4
+ data.tar.gz: b11f8e29260760dd64a78f26c016bd936fe0a5e75100329c645915bddd094ebc
5
5
  SHA512:
6
- metadata.gz: 96249d926438ccf2b4c239ae3b65a3649bb8b8e279e05774431376188b9ee330e71211f52bb7a51d5edf457be1997bfb5609e2a91debde4a2719f9ff4f34ae4a
7
- data.tar.gz: fd42fd6be60e9572eb5a857253eabbb44693d46927ecef0b57ed72213c1af875511c4b2ad86bd5e7257e75ac063bde78a49f03f62a0d0350348d18d696d4a59d
6
+ metadata.gz: f74cda2cf6f657f72c15d118d9fb622d24d049261436691bff28e42ad5656beb53ec192e148e30e0f025a5becb094682fc68f4860c2cf5b3ed351b7954e3b587
7
+ data.tar.gz: 90125b61cd874c12376eecc726aa2fef0d6158565150830f95f642f97fad849b53570238593ce617dc39b05960abb47d4c2e6cd4a974e30c69fc40ec2720958b
data/README.md CHANGED
@@ -41,7 +41,7 @@ $prefab.set_rails_loggers
41
41
  ```ruby
42
42
  #puma.rb
43
43
  on_worker_boot do
44
- $prefab = Prefab::Client.new
44
+ $prefab = $prefab.fork
45
45
  $prefab.set_rails_loggers
46
46
  end
47
47
  ```
@@ -49,7 +49,7 @@ end
49
49
  ```ruby
50
50
  # unicorn.rb
51
51
  after_fork do |server, worker|
52
- $prefab = Prefab::Client.new(options)
52
+ $prefab = $prefab.fork
53
53
  $prefab.set_rails_loggers
54
54
  end
55
55
  ```
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.24.1
1
+ 0.24.3
data/lib/prefab/client.rb CHANGED
@@ -111,6 +111,15 @@ module Prefab
111
111
  config_client.resolver
112
112
  end
113
113
 
114
+ # When starting a forked process, use this to re-use the options
115
+ # on_worker_boot do
116
+ # $prefab = $prefab.fork
117
+ # $prefab.set_rails_loggers
118
+ # end
119
+ def fork
120
+ Prefab::Client.new(@options)
121
+ end
122
+
114
123
  private
115
124
 
116
125
  def is_ff?(key)
@@ -55,10 +55,11 @@ module Prefab
55
55
  end
56
56
 
57
57
  def get(key, default = NO_DEFAULT_PROVIDED, properties = NO_DEFAULT_PROVIDED)
58
- value = _get(key, properties)
58
+ context = @config_resolver.make_context(properties)
59
+ value = _get(key, context)
59
60
 
60
61
  if value
61
- Prefab::ConfigValueUnwrapper.unwrap(value, key, properties)
62
+ Prefab::ConfigValueUnwrapper.unwrap(value, key, context)
62
63
  else
63
64
  handle_default(key, default)
64
65
  end
@@ -42,7 +42,7 @@ module Prefab
42
42
  project_env_id: @project_env_id,
43
43
  resolver: self,
44
44
  namespace: @base_client.options.namespace,
45
- base_client: @base_client).evaluate(context(properties))
45
+ base_client: @base_client).evaluate(make_context(properties))
46
46
  end
47
47
 
48
48
  def update
@@ -55,9 +55,7 @@ module Prefab
55
55
  @on_update = block
56
56
  end
57
57
 
58
- private
59
-
60
- def context(properties)
58
+ def make_context(properties)
61
59
  if properties == NO_DEFAULT_PROVIDED
62
60
  Context.current
63
61
  elsif properties.is_a?(Context)
@@ -67,6 +65,8 @@ module Prefab
67
65
  end
68
66
  end
69
67
 
68
+ private
69
+
70
70
  def make_local
71
71
  @lock.with_write_lock do
72
72
  @local_store = @config_loader.calc_config
@@ -14,7 +14,7 @@ module Prefab
14
14
  value = Prefab::WeightedValueResolver.new(
15
15
  config_value.weighted_values.weighted_values,
16
16
  config_key,
17
- context[config_value.weighted_values.hash_by_property_name]
17
+ context.get(config_value.weighted_values.hash_by_property_name)
18
18
  ).resolve
19
19
 
20
20
  unwrap(value.value, config_key, context)
@@ -77,18 +77,10 @@ module Prefab
77
77
  end
78
78
  end
79
79
 
80
- def merge!(name, hash)
81
- @contexts[name.to_s] = context(name).merge!(hash)
82
- end
83
-
84
80
  def set(name, hash)
85
81
  @contexts[name.to_s] = NamedContext.new(name, hash)
86
82
  end
87
83
 
88
- def []=(name, hash)
89
- set(name, hash)
90
- end
91
-
92
84
  def get(property_key)
93
85
  name, key = property_key.split('.', 2)
94
86
 
@@ -100,10 +92,6 @@ module Prefab
100
92
  contexts[name] && contexts[name].get(key)
101
93
  end
102
94
 
103
- def [](property_key)
104
- get(property_key)
105
- end
106
-
107
95
  def to_h
108
96
  contexts.map { |name, context| [name, context.to_h] }.to_h
109
97
  end
@@ -53,8 +53,9 @@ module Prefab
53
53
 
54
54
  return true if @logdev.nil? || severity < level_of(path) || @silences[local_log_id]
55
55
 
56
- progname = "#{path}: #{progname || @progname}"
57
-
56
+ if progname.nil?
57
+ progname = @progname
58
+ end
58
59
  if message.nil?
59
60
  if block_given?
60
61
  message = yield
@@ -65,7 +66,7 @@ module Prefab
65
66
  end
66
67
 
67
68
  @logdev.write(
68
- format_message(format_severity(severity), Time.now, progname, message)
69
+ format_message(format_severity(severity), Time.now, progname, message, path)
69
70
  )
70
71
  true
71
72
  end
@@ -169,6 +170,20 @@ module Prefab
169
170
  path.slice! SEP
170
171
  path
171
172
  end
173
+
174
+ def format_message(severity, datetime, progname, msg, path)
175
+ formatter = (@formatter || @default_formatter)
176
+
177
+ if formatter.arity == 5
178
+ formatter.call(severity, datetime, progname, msg, path)
179
+ else
180
+ formatter.call(severity, datetime, join_path_and_progname(path, progname), msg)
181
+ end
182
+ end
183
+
184
+ def join_path_and_progname(path, progname)
185
+ (progname.nil? || progname.empty?) ? path : "#{progname}: #{path}"
186
+ end
172
187
  end
173
188
 
174
189
  # StubConfigClient to be used while config client initializes
@@ -21,6 +21,15 @@ module Prefab
21
21
  DEFAULT_LOG_FORMATTER = proc { |severity, datetime, progname, msg|
22
22
  "#{severity.ljust(5)} #{datetime}:#{' ' if progname}#{progname} #{msg}\n"
23
23
  }
24
+ JSON_LOG_FORMATTER = proc { |severity, datetime, progname, msg, path|
25
+ {
26
+ type: severity,
27
+ time: datetime,
28
+ progname: progname,
29
+ message: msg,
30
+ path: path
31
+ }.compact.to_json << "\n"
32
+ }
24
33
 
25
34
  module ON_INITIALIZATION_FAILURE
26
35
  RAISE = 1
@@ -45,7 +45,7 @@ module Prefab
45
45
  hash[k] = ConfigRow.new(k, nil, nil, nil)
46
46
  else
47
47
  config = @resolver.evaluate(v[:config])
48
- value = Prefab::ConfigValueUnwrapper.unwrap(config, k, {})
48
+ value = Prefab::ConfigValueUnwrapper.unwrap(config, k, Prefab::Context.new)
49
49
  hash[k] = ConfigRow.new(k, value, v[:match], v[:source])
50
50
  end
51
51
  end
@@ -67,7 +67,7 @@ module Prefab
67
67
  elements << 'tombstone'
68
68
  else
69
69
  config = @resolver.evaluate(v[:config], {})
70
- value = Prefab::ConfigValueUnwrapper.unwrap(config, k, {})
70
+ value = Prefab::ConfigValueUnwrapper.unwrap(config, k, Prefab::Context.new)
71
71
  elements << value.to_s.slice(0..34).ljust(35)
72
72
  elements << value.class.to_s.slice(0..6).ljust(7)
73
73
  elements << "Match: #{v[:match]}".slice(0..29).ljust(30)
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: prefab-cloud-ruby 0.24.1 ruby lib
5
+ # stub: prefab-cloud-ruby 0.24.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "0.24.1"
9
+ s.version = "0.24.3"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2023-04-26"
14
+ s.date = "2023-05-15"
15
15
  s.description = "Feature Flags, Live Config, and Dynamic Log Levels as a service".freeze
16
16
  s.email = "jdwyer@prefab.cloud".freeze
17
17
  s.extra_rdoc_files = [
@@ -12,7 +12,9 @@ class IntegrationTest
12
12
  end
13
13
 
14
14
  def test_type
15
- if @expected[:status] == 'raise'
15
+ if @input[0] && @input[0].start_with?('log-level.')
16
+ :log_level
17
+ elsif @expected[:status] == 'raise'
16
18
  :raise
17
19
  elsif @expected[:value].nil?
18
20
  :nil
@@ -4,45 +4,46 @@ require 'test_helper'
4
4
 
5
5
  class TestConfigValueUnwrapper < Minitest::Test
6
6
  CONFIG_KEY = 'config_key'
7
+ EMPTY_CONTEXT = Prefab::Context.new()
7
8
 
8
9
  def test_unwrapping_int
9
10
  config_value = Prefab::ConfigValue.new(int: 123)
10
- assert_equal 123, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
11
+ assert_equal 123, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
11
12
  end
12
13
 
13
14
  def test_unwrapping_string
14
15
  config_value = Prefab::ConfigValue.new(string: 'abc')
15
- assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
16
+ assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
16
17
  end
17
18
 
18
19
  def test_unwrapping_double
19
20
  config_value = Prefab::ConfigValue.new(double: 1.23)
20
- assert_equal 1.23, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
21
+ assert_equal 1.23, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
21
22
  end
22
23
 
23
24
  def test_unwrapping_bool
24
25
  config_value = Prefab::ConfigValue.new(bool: true)
25
- assert_equal true, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
26
+ assert_equal true, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
26
27
 
27
28
  config_value = Prefab::ConfigValue.new(bool: false)
28
- assert_equal false, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
29
+ assert_equal false, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
29
30
  end
30
31
 
31
32
  def test_unwrapping_log_level
32
33
  config_value = Prefab::ConfigValue.new(log_level: :INFO)
33
- assert_equal :INFO, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
34
+ assert_equal :INFO, Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
34
35
  end
35
36
 
36
37
  def test_unwrapping_string_list
37
38
  config_value = Prefab::ConfigValue.new(string_list: Prefab::StringList.new(values: %w[a b c]))
38
- assert_equal %w[a b c], Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
39
+ assert_equal %w[a b c], Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
39
40
  end
40
41
 
41
42
  def test_unwrapping_weighted_values
42
43
  # single value
43
44
  config_value = Prefab::ConfigValue.new(weighted_values: weighted_values([['abc', 1]]))
44
45
 
45
- assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, {})
46
+ assert_equal 'abc', Prefab::ConfigValueUnwrapper.unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
46
47
 
47
48
  # multiple values, evenly distributed
48
49
  config_value = Prefab::ConfigValue.new(weighted_values: weighted_values([['abc', 1], ['def', 1], ['ghi', 1]]))
data/test/test_context.rb CHANGED
@@ -86,7 +86,7 @@ class TestContext < Minitest::Test
86
86
  Prefab::Context.with_context(EXAMPLE_PROPERTIES) do
87
87
  context = Prefab::Context.current
88
88
  assert_equal(stringify(EXAMPLE_PROPERTIES), context.to_h)
89
- assert_equal('some-user-key', context['user.key'])
89
+ assert_equal('some-user-key', context.get('user.key'))
90
90
  end
91
91
  end
92
92
 
@@ -105,34 +105,24 @@ class TestContext < Minitest::Test
105
105
  def test_setting
106
106
  context = Prefab::Context.new({})
107
107
  context.set('user', { key: 'value' })
108
- context[:other] = { key: 'different', something: 'other' }
108
+ context.set(:other, { key: 'different', something: 'other' })
109
109
  assert_equal(stringify({ user: { key: 'value' }, other: { key: 'different', something: 'other' } }), context.to_h)
110
110
  end
111
111
 
112
112
  def test_getting
113
113
  context = Prefab::Context.new(EXAMPLE_PROPERTIES)
114
114
  assert_equal('some-user-key', context.get('user.key'))
115
- assert_equal('some-user-key', context['user.key'])
116
115
  assert_equal('pro', context.get('team.plan'))
117
- assert_equal('pro', context['team.plan'])
118
116
  end
119
117
 
120
118
  def test_dot_notation_getting
121
119
  context = Prefab::Context.new({ 'user' => { 'key' => 'value' } })
122
120
  assert_equal('value', context.get('user.key'))
123
- assert_equal('value', context['user.key'])
124
121
  end
125
122
 
126
123
  def test_dot_notation_getting_with_symbols
127
124
  context = Prefab::Context.new({ user: { key: 'value' } })
128
125
  assert_equal('value', context.get('user.key'))
129
- assert_equal('value', context['user.key'])
130
- end
131
-
132
- def test_merge
133
- context = Prefab::Context.new(EXAMPLE_PROPERTIES)
134
- context.merge!(:other, { key: 'different' })
135
- assert_equal(stringify(EXAMPLE_PROPERTIES.merge(other: { key: 'different' })), context.to_h)
136
126
  end
137
127
 
138
128
  def test_clear
@@ -13,7 +13,7 @@ class TestIntegration < Minitest::Test
13
13
  parent_context = test['context']
14
14
 
15
15
  test['cases'].each do |test_case|
16
- define_method(:"test_#{test_case['name']}") do
16
+ define_method(:"test_#{test['name']}_#{test_case['name']}") do
17
17
  it = IntegrationTest.new(test_case)
18
18
 
19
19
  with_parent_context_maybe(parent_context) do
@@ -30,6 +30,10 @@ class TestIntegration < Minitest::Test
30
30
  assert_equal it.expected[:value], it.test_client.send(it.func, flag, context)
31
31
  when :simple_equality
32
32
  assert_equal it.expected[:value], it.test_client.send(it.func, *it.input)
33
+ when :log_level
34
+ assert_equal it.expected[:value].to_sym, it.test_client.send(it.func, *it.input)
35
+ else
36
+ raise "Unknown test type: #{it.test_type}"
33
37
  end
34
38
  end
35
39
  end
data/test/test_logger.rb CHANGED
@@ -85,46 +85,47 @@ class TestLogger < Minitest::Test
85
85
  end
86
86
 
87
87
  def test_log_internal
88
- logger, mock_logdev = mock_logger_expecting(/W, \[.*\] WARN -- cloud.prefab.client.test.path: : test message/)
89
- logger.log_internal('test message', 'test.path', '', ::Logger::WARN)
90
- mock_logdev.verify
88
+ prefab, io = captured_logger
89
+ prefab.log.log_internal('test message', 'test.path', '', ::Logger::WARN)
90
+ assert_logged io, 'WARN', "cloud.prefab.client.test.path", "test message"
91
91
  end
92
92
 
93
93
  def test_log_internal_unknown
94
- logger, mock_logdev = mock_logger_expecting(/A, \[.*\] ANY -- cloud.prefab.client.test.path: : test message/)
95
- logger.log_internal('test message', 'test.path', '', ::Logger::UNKNOWN)
96
- mock_logdev.verify
94
+ prefab, io = captured_logger
95
+ prefab.log.log_internal('test message', 'test.path', '', ::Logger::UNKNOWN)
96
+ assert_logged io, 'ANY', "cloud.prefab.client.test.path", "test message"
97
97
  end
98
98
 
99
99
  def test_log_internal_silencing
100
- logger, mock_logdev = mock_logger_expecting(/W, \[.*\] WARN -- cloud.prefab.client.test.path: : should log/,
101
- calls: 2)
102
- logger.silence do
103
- logger.log_internal('should not log', 'test.path', '', ::Logger::WARN)
100
+ prefab, io = captured_logger
101
+ prefab.log.silence do
102
+ prefab.log.log_internal('should not log', 'test.path', '', ::Logger::WARN)
104
103
  end
105
- logger.log_internal('should log', 'test.path', '', ::Logger::WARN)
106
- mock_logdev.verify
104
+ prefab.log.log_internal('should log', 'test.path', '', ::Logger::WARN)
105
+ assert_logged io, 'WARN', "cloud.prefab.client.test.path", "should log"
106
+ refute_logged io, 'should not log'
107
107
  end
108
108
 
109
109
  def test_log
110
- logger, mock_logdev = mock_logger_expecting(/W, \[.*\] WARN -- test.path: : test message/)
111
- logger.log('test message', 'test.path', '', ::Logger::WARN)
112
- mock_logdev.verify
110
+ prefab, io = captured_logger
111
+ prefab.log.log('test message', 'test.path', '', ::Logger::WARN)
112
+ assert_logged io, 'WARN', "test.path", "test message"
113
113
  end
114
114
 
115
115
  def test_log_unknown
116
- logger, mock_logdev = mock_logger_expecting(/A, \[.*\] ANY -- test.path: : test message/)
117
- logger.log('test message', 'test.path', '', ::Logger::UNKNOWN)
118
- mock_logdev.verify
116
+ prefab, io = captured_logger
117
+ prefab.log.log('test message', 'test.path', '', ::Logger::UNKNOWN)
118
+ assert_logged io, 'ANY', "test.path", "test message"
119
119
  end
120
120
 
121
121
  def test_log_silencing
122
- logger, mock_logdev = mock_logger_expecting(/W, \[.*\] WARN -- test.path: : should log/, calls: 2)
123
- logger.silence do
124
- logger.log('should not log', 'test.path', '', ::Logger::WARN)
122
+ prefab, io = captured_logger
123
+ prefab.log.silence do
124
+ prefab.log.log('should not log', 'test.path', '', ::Logger::WARN)
125
125
  end
126
- logger.log('should log', 'test.path', '', ::Logger::WARN)
127
- mock_logdev.verify
126
+ prefab.log.log('should log', 'test.path', '', ::Logger::WARN)
127
+ assert_logged io, 'WARN', "test.path", "should log"
128
+ refute_logged io, 'should not log'
128
129
  end
129
130
 
130
131
  def test_logging_with_prefix
@@ -163,7 +164,7 @@ class TestLogger < Minitest::Test
163
164
  prefab.log.progname = 'MY_PROGNAME'
164
165
  prefab.log.error message
165
166
 
166
- assert_logged io, 'ERROR', 'MY_PROGNAME test.test_logger.test_logging_with_a_progname', message
167
+ assert_logged io, 'ERROR', 'MY_PROGNAME: test.test_logger.test_logging_with_a_progname', message
167
168
  end
168
169
 
169
170
  def test_logging_with_a_progname_and_no_message
@@ -172,7 +173,7 @@ class TestLogger < Minitest::Test
172
173
  prefab.log.progname = 'MY_PROGNAME'
173
174
  prefab.log.error
174
175
 
175
- assert_logged io, 'ERROR', 'MY_PROGNAME test.test_logger.test_logging_with_a_progname_and_no_message', 'MY_PROGNAME'
176
+ assert_logged io, 'ERROR', 'MY_PROGNAME: test.test_logger.test_logging_with_a_progname_and_no_message', 'MY_PROGNAME'
176
177
  end
177
178
 
178
179
  def test_logging_with_criteria_on_top_level_key
@@ -399,35 +400,19 @@ class TestLogger < Minitest::Test
399
400
  raise 'THIS WILL NEVER BE EVALUATED'
400
401
  end
401
402
 
402
- assert_logged io, 'ERROR', 'test.test_logger.test_logging_with_a_block', ' ' + message
403
+ assert_logged io, 'ERROR', 'test.test_logger.test_logging_with_a_block', message
403
404
  end
404
405
 
405
406
  private
406
407
 
407
408
  def assert_logged(logged_io, level, path, message)
408
- assert_match(/#{level}\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+]?\d+:\s+#{path}: #{message}\n/, logged_io.string)
409
+ assert_match(/#{level}\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [-+]?\d+:\s+#{path} #{message}\n/, logged_io.string)
409
410
  end
410
411
 
411
412
  def refute_logged(logged_io, message)
412
413
  refute_match(/#{message}/, logged_io.string)
413
414
  end
414
415
 
415
- def mock_logger_expecting(pattern, configs = {}, calls: 1)
416
- mock_logdev = Minitest::Mock.new
417
- mock_logdev.expect :write, nil do |arg|
418
- pattern.match(arg)
419
- end
420
-
421
- calls.times.each do
422
- mock_logdev.expect(:nil?, false)
423
- end
424
-
425
- logger = Prefab::LoggerClient.new($stdout)
426
- logger.instance_variable_set('@logdev', mock_logdev)
427
- logger.set_config_client(MockConfigClient.new(configs))
428
- [logger, mock_logdev]
429
- end
430
-
431
416
  def captured_logger(options = {})
432
417
  io = StringIO.new
433
418
  options = Prefab::Options.new(**options.merge(
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prefab-cloud-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.1
4
+ version: 0.24.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-26 00:00:00.000000000 Z
11
+ date: 2023-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby