loga 2.8.0 → 2.9.0

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build-and-test.yml +72 -0
  3. data/.github/workflows/publish-gem.yml +28 -0
  4. data/.rubocop.yml +10 -11
  5. data/.rubocop_todo.yml +265 -0
  6. data/Appraisals +83 -22
  7. data/CHANGELOG.md +14 -0
  8. data/Gemfile +24 -0
  9. data/README.md +1 -1
  10. data/gemfiles/rails52.gemfile +23 -1
  11. data/gemfiles/rails60.gemfile +24 -0
  12. data/gemfiles/rails61.gemfile +24 -0
  13. data/gemfiles/rails70.gemfile +27 -0
  14. data/gemfiles/rails71.gemfile +38 -0
  15. data/gemfiles/rails72.gemfile +38 -0
  16. data/gemfiles/rails80.gemfile +38 -0
  17. data/gemfiles/sidekiq51.gemfile +25 -0
  18. data/gemfiles/sidekiq60.gemfile +25 -0
  19. data/gemfiles/sidekiq61.gemfile +25 -0
  20. data/gemfiles/sidekiq62.gemfile +25 -0
  21. data/gemfiles/sidekiq63.gemfile +25 -0
  22. data/gemfiles/sidekiq64.gemfile +25 -0
  23. data/gemfiles/sidekiq65.gemfile +25 -0
  24. data/gemfiles/sidekiq70.gemfile +25 -0
  25. data/gemfiles/sidekiq71.gemfile +25 -0
  26. data/gemfiles/sidekiq72.gemfile +36 -0
  27. data/gemfiles/sidekiq73.gemfile +36 -0
  28. data/gemfiles/sidekiq80.gemfile +36 -0
  29. data/gemfiles/sinatra14.gemfile +25 -0
  30. data/gemfiles/sinatra2.gemfile +36 -0
  31. data/gemfiles/sinatra3.gemfile +36 -0
  32. data/gemfiles/sinatra4.gemfile +36 -0
  33. data/gemfiles/unit.gemfile +24 -0
  34. data/lib/loga/parameter_filter.rb +0 -2
  35. data/lib/loga/rack/request.rb +2 -2
  36. data/lib/loga/rack/request_id.rb +2 -2
  37. data/lib/loga/railtie.rb +3 -3
  38. data/lib/loga/sidekiq.rb +11 -0
  39. data/lib/loga/sidekiq8/job_logger.rb +13 -0
  40. data/lib/loga/tagged_logging.rb +2 -2
  41. data/lib/loga/version.rb +1 -1
  42. data/loga.gemspec +0 -15
  43. data/spec/fixtures/{rails50.rb → rails71.rb} +1 -1
  44. data/spec/fixtures/{rails42.rb → rails72.rb} +3 -3
  45. data/spec/fixtures/rails80.rb +80 -0
  46. data/spec/integration/rails/action_mailer_spec.rb +2 -0
  47. data/spec/integration/rails/railtie_spec.rb +13 -3
  48. data/spec/integration/rails/request_spec.rb +4 -2
  49. data/spec/integration/sidekiq5_spec.rb +15 -13
  50. data/spec/integration/sidekiq60_spec.rb +3 -3
  51. data/spec/integration/sidekiq61_spec.rb +6 -4
  52. data/spec/integration/sidekiq65_spec.rb +1 -1
  53. data/spec/integration/sidekiq8_spec.rb +193 -0
  54. data/spec/integration/sinatra_spec.rb +18 -8
  55. data/spec/loga/sidekiq5/job_logger_spec.rb +2 -0
  56. data/spec/loga/sidekiq6/job_logger_spec.rb +3 -1
  57. data/spec/loga/sidekiq_latest/job_logger_spec.rb +138 -0
  58. data/spec/loga/sidekiq_spec.rb +17 -3
  59. data/spec/spec_helper.rb +14 -5
  60. data/spec/support/gethostname_shared.rb +3 -1
  61. data/spec/support/helpers.rb +10 -0
  62. data/spec/support/request_spec.rb +90 -81
  63. data/spec/support/timecop_shared.rb +3 -1
  64. data/spec/unit/loga/configuration_spec.rb +11 -9
  65. data/spec/unit/loga/event_spec.rb +4 -2
  66. data/spec/unit/loga/formatters/gelf_formatter_spec.rb +22 -14
  67. data/spec/unit/loga/formatters/simple_formatter_spec.rb +27 -10
  68. data/spec/unit/loga/log_subscribers/action_mailer_spec.rb +6 -4
  69. data/spec/unit/loga/parameter_filter_spec.rb +2 -0
  70. data/spec/unit/loga/rack/logger_spec.rb +18 -16
  71. data/spec/unit/loga/rack/request_spec.rb +2 -0
  72. data/spec/unit/loga/service_version_strategies_spec.rb +2 -0
  73. data/spec/unit/loga/utilities_spec.rb +2 -0
  74. data/spec/unit/loga_spec.rb +3 -1
  75. metadata +26 -212
  76. data/.circleci/config.yml +0 -112
  77. data/gemfiles/rails42.gemfile +0 -11
  78. data/gemfiles/rails50.gemfile +0 -11
  79. data/gemfiles/sidekiq7.gemfile +0 -11
  80. data/spec/loga/sidekiq7/job_logger_spec.rb +0 -125
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe 'Sidekiq client logger' do
6
+ let(:target) { StringIO.new }
7
+ let(:config) { Sidekiq.instance_variable_get :@config }
8
+
9
+ before do
10
+ Sidekiq.configure_server do |config|
11
+ config.redis = { pool_name: :default }
12
+ end
13
+
14
+ Loga.reset
15
+
16
+ Loga.configure(
17
+ service_name: 'hello_world_app',
18
+ service_version: '1.0',
19
+ device: target,
20
+ format: :gelf,
21
+ )
22
+ end
23
+
24
+ it 'has the proper job logger' do
25
+ expect(config[:job_logger]).to eq Loga::Sidekiq8::JobLogger
26
+ end
27
+
28
+ it 'has the proper logger for Sidekiq.logger' do
29
+ expect(Sidekiq.logger).to eq Loga.logger
30
+ end
31
+
32
+ context 'with processor' do
33
+ require 'sidekiq/processor'
34
+
35
+ let(:mutex) { Mutex.new }
36
+ let(:cond) { ConditionVariable.new }
37
+ let(:processor) do
38
+ Sidekiq::Processor.new(config.default_capsule) { |pr, ex| result(pr, ex) }
39
+ end
40
+
41
+ before do
42
+ @exception = nil
43
+ end
44
+
45
+ context 'with a successful job' do
46
+ before do
47
+ MySidekiqWorker.perform_async('Bob')
48
+
49
+ await { processor.start }
50
+
51
+ processor.terminate(true)
52
+ end
53
+
54
+ it 'logs the job processing event' do
55
+ test_log_from_worker(read_json_log(line: -2))
56
+ end
57
+
58
+ it 'logs the "done" event' do
59
+ test_job_end_log(read_json_log(line: -1))
60
+ end
61
+ end
62
+
63
+ context 'with an error' do
64
+ before do
65
+ MySidekiqWorker.perform_async('Boom')
66
+
67
+ await { processor.start }
68
+
69
+ processor.terminate(true)
70
+ end
71
+
72
+ it 'logs the "error" event' do
73
+ test_job_fail_log(read_json_log(line: 0))
74
+ end
75
+
76
+ it 're-throws the error' do
77
+ # rubocop:disable RSpec/InstanceVariable
78
+ expect(@exception.message).to eq('Boom')
79
+ # rubocop:enable RSpec/InstanceVariable
80
+ end
81
+ end
82
+
83
+ def result(_processor, exception)
84
+ @exception = exception
85
+ mutex.synchronize { cond.signal }
86
+ end
87
+
88
+ def await(timeout: 0.1)
89
+ mutex.synchronize do
90
+ yield
91
+ cond.wait(mutex, timeout)
92
+ end
93
+ end
94
+
95
+ def common_log_fields
96
+ {
97
+ '_class' => 'MySidekiqWorker',
98
+ '_service.name' => 'hello_world_app',
99
+ '_service.version' => '1.0',
100
+ '_tags' => '',
101
+ 'version' => '1.1',
102
+ }.freeze
103
+ end
104
+
105
+ def job_logger_common_fields
106
+ common_log_fields.merge(
107
+ '_queue' => 'default',
108
+ '_retry' => true,
109
+ '_type' => 'sidekiq',
110
+ )
111
+ end
112
+
113
+ def test_log_from_worker(json_line)
114
+ aggregate_failures do
115
+ expect(json_line).to include(
116
+ common_log_fields.merge(
117
+ 'level' => 6,
118
+ 'short_message' => 'Hello from MySidekiqWorker',
119
+ ),
120
+ )
121
+
122
+ %w[_jid timestamp host].each do |key|
123
+ expect(json_line).to have_key(key)
124
+ end
125
+
126
+ expect(json_line).not_to include('_duration')
127
+ end
128
+ end
129
+
130
+ def test_job_end_log(json_line)
131
+ aggregate_failures do
132
+ expect(json_line).to include(
133
+ job_logger_common_fields.merge(
134
+ '_params' => ['Bob'],
135
+ 'level' => 6,
136
+ ),
137
+ )
138
+
139
+ %w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
140
+ expect(json_line).to have_key(key)
141
+ end
142
+
143
+ expect(json_line['_duration']).to be < 500
144
+ expect(json_line['short_message'])
145
+ .to match(/MySidekiqWorker with jid: '\w+' done/)
146
+ end
147
+ end
148
+
149
+ def test_job_fail_log(json_line)
150
+ aggregate_failures do
151
+ expect(json_line).to include(
152
+ job_logger_common_fields.merge(
153
+ '_params' => ['Boom'],
154
+ 'level' => 4,
155
+ ),
156
+ )
157
+
158
+ %w[_created_at _enqueued_at _jid _duration timestamp host].each do |key|
159
+ expect(json_line).to have_key(key)
160
+ end
161
+
162
+ expect(json_line['_duration']).to be < 500
163
+ expect(json_line['short_message'])
164
+ .to match(/MySidekiqWorker with jid: '\w+' fail/)
165
+ end
166
+ end
167
+ end
168
+
169
+ def dump_log
170
+ offset = target.pos
171
+
172
+ target.rewind
173
+ target.each_line { puts _1 }
174
+
175
+ target.pos = offset
176
+ end
177
+
178
+ def read_json_log(line:)
179
+ target.rewind
180
+
181
+ JSON.parse(target.readlines[line])
182
+ end
183
+ end
184
+
185
+ class MySidekiqWorker
186
+ include Sidekiq::Job
187
+
188
+ def perform(name)
189
+ raise name if name == 'Boom'
190
+
191
+ logger.info('Hello from MySidekiqWorker')
192
+ end
193
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  class MySinatraApp < Sinatra::Base
@@ -21,7 +23,7 @@ class MySinatraApp < Sinatra::Base
21
23
  end
22
24
  end
23
25
 
24
- RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
26
+ RSpec.describe 'Structured logging with Sinatra', :timecop, :with_hostname do
25
27
  let(:io) { StringIO.new }
26
28
  let(:format) {}
27
29
  let(:last_log_entry) do
@@ -59,7 +61,7 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
59
61
  end
60
62
  end
61
63
 
62
- # rubocop:disable Metrics/LineLength
64
+ # rubocop:disable Layout/LineLength
63
65
  context 'when RACK_ENV is development', if: ENV['RACK_ENV'].eql?('development') do
64
66
  let(:format) { :simple }
65
67
  let(:last_log_entry) do
@@ -69,12 +71,12 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
69
71
  let(:data) do
70
72
  {
71
73
  'method' => 'GET',
72
- 'path' => '/ok',
74
+ 'path' => '/ok',
73
75
  'params' => { 'username'=>'yoshi' },
74
76
  'request_id' => '700a6a01',
75
77
  'request_ip' => '127.0.0.1',
76
78
  'user_agent' => nil,
77
- 'duration' => 0,
79
+ 'duration' => 0,
78
80
  'status' => 200,
79
81
  }
80
82
  end
@@ -97,7 +99,7 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
97
99
  let(:data) do
98
100
  super().merge(
99
101
  'status' => 302,
100
- 'path' => '/new',
102
+ 'path' => '/new',
101
103
  'params' => {},
102
104
  )
103
105
  end
@@ -112,16 +114,24 @@ RSpec.describe 'Structured logging with Sinatra', :with_hostname, :timecop do
112
114
  let(:data) do
113
115
  super().merge(
114
116
  'status' => 500,
115
- 'path' => '/error',
117
+ 'path' => '/error',
116
118
  'params' => {},
117
119
  )
118
120
  end
119
121
 
120
122
  it 'logs the request with the exception' do
121
123
  get '/error', {}, 'HTTP_X_REQUEST_ID' => '700a6a01'
122
- expect(last_log_entry).to eql("E, #{time_pid_tags} GET /error 500 in 0ms type=request #{data_as_text} exception=undefined method `name' for nil:NilClass\n")
124
+
125
+ exception_line =
126
+ if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('3.4.0dev')
127
+ "E, #{time_pid_tags} GET /error 500 in 0ms type=request #{data_as_text} exception=undefined method 'name' for nil"
128
+ else
129
+ "E, #{time_pid_tags} GET /error 500 in 0ms type=request #{data_as_text} exception=undefined method `name' for nil"
130
+ end
131
+
132
+ expect(last_log_entry).to start_with(exception_line)
123
133
  end
124
134
  end
125
135
  end
126
- # rubocop:enable Metrics/LineLength
136
+ # rubocop:enable Layout/LineLength
127
137
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'loga/sidekiq5/job_logger'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'loga/sidekiq6/job_logger'
3
5
 
@@ -24,7 +26,7 @@ RSpec.describe Loga::Sidekiq6::JobLogger do
24
26
 
25
27
  # https://github.com/mperham/sidekiq/blob/v6.1.2/lib/sidekiq/job_logger.rb
26
28
  it 'inherits from ::Sidekiq::JobLogger' do
27
- expect(subject).to be_a(::Sidekiq::JobLogger)
29
+ expect(subject).to be_a(Sidekiq::JobLogger)
28
30
  end
29
31
 
30
32
  describe '#call' do
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'loga/sidekiq7/job_logger'
5
+ require 'loga/sidekiq8/job_logger'
6
+
7
+ logger_klass =
8
+ if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('8.0.0')
9
+ Loga::Sidekiq8::JobLogger
10
+ elsif Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('7.0.0')
11
+ Loga::Sidekiq7::JobLogger
12
+ else
13
+ raise 'No suitable Sidekiq JobLogger implementation found'
14
+ end
15
+
16
+ RSpec.describe logger_klass, 'unified (latest) Sidekiq JobLogger spec' do
17
+ # Sidekiq < 7.3: initializer expects a raw logger
18
+ # Sidekiq >= 7.3: initializer expects a config object responding to #logger and #[] (Sidekiq 8 keeps this)
19
+ subject(:job_logger) do
20
+ if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new('7.3.0')
21
+ described_class.new(sidekiq_config)
22
+ else
23
+ described_class.new(logger)
24
+ end
25
+ end
26
+
27
+ let(:target) { StringIO.new }
28
+
29
+ let(:json_line) do
30
+ target.rewind
31
+ raw = target.read
32
+ line = raw.split("\n").last
33
+ JSON.parse(line) if line
34
+ end
35
+
36
+ let(:logger) { Loga.logger }
37
+ let(:sidekiq_config) { instance_double(Sidekiq::Config, logger: logger, :[] => nil) }
38
+
39
+ before do
40
+ Loga.reset
41
+ Loga.configure(
42
+ service_name: 'hello_world_app',
43
+ service_version: '1.0',
44
+ device: target,
45
+ format: :gelf,
46
+ )
47
+ end
48
+
49
+ it 'inherits from ::Sidekiq::JobLogger' do
50
+ expect(job_logger).to be_a(Sidekiq::JobLogger)
51
+ end
52
+
53
+ describe '#call' do
54
+ let(:item_data) do
55
+ {
56
+ 'class' => 'HardWorker',
57
+ 'args' => ['asd'],
58
+ 'retry' => true,
59
+ 'queue' => 'default',
60
+ 'jid' => '591f6f66ee0d218fb451dfb6',
61
+ 'created_at' => 1_528_799_582.904939,
62
+ 'enqueued_at' => 1_528_799_582.9049861,
63
+ }
64
+ end
65
+
66
+ context 'when the job passes successfully' do
67
+ it 'logs the expected structured event' do
68
+ job_logger.call(item_data, 'default') do
69
+ # simulate job body
70
+ end
71
+
72
+ expected = {
73
+ 'version' => '1.1',
74
+ 'level' => 6, # INFO -> 6 (syslog mapping)
75
+ '_type' => 'sidekiq',
76
+ '_created_at' => item_data['created_at'],
77
+ '_enqueued_at' => item_data['enqueued_at'],
78
+ '_jid' => item_data['jid'],
79
+ '_retry' => true,
80
+ '_queue' => 'default',
81
+ '_service.name' => 'hello_world_app',
82
+ '_service.version' => '1.0',
83
+ '_class' => 'HardWorker',
84
+ '_params' => ['asd'],
85
+ '_tags' => '',
86
+ }
87
+
88
+ aggregate_failures do
89
+ expect(json_line).to include(expected)
90
+ expect(json_line['_duration']).to be_a(Float).or be_a(Integer)
91
+ expect(json_line['timestamp']).to be_a(Float)
92
+ expect(json_line['host']).to be_a(String)
93
+ expect(json_line['short_message'])
94
+ .to match(/HardWorker with jid: '591f6f66ee0d218fb451dfb6' (done|executed)/)
95
+ end
96
+ end
97
+ end
98
+
99
+ context 'when the job fails' do
100
+ it 'logs the expected structured event with exception info' do
101
+ failing = lambda do
102
+ job_logger.call(item_data, 'default') do
103
+ raise StandardError, 'Boom'
104
+ end
105
+ end
106
+
107
+ expect(&failing).to raise_error(StandardError, 'Boom')
108
+
109
+ expected = {
110
+ 'version' => '1.1',
111
+ # WARN -> syslog 4 (sidekiq job failure logged at warn)
112
+ 'level' => 4,
113
+ '_type' => 'sidekiq',
114
+ '_created_at' => item_data['created_at'],
115
+ '_enqueued_at' => item_data['enqueued_at'],
116
+ '_jid' => item_data['jid'],
117
+ '_retry' => true,
118
+ '_queue' => 'default',
119
+ '_service.name' => 'hello_world_app',
120
+ '_service.version' => '1.0',
121
+ '_class' => 'HardWorker',
122
+ '_params' => ['asd'],
123
+ '_tags' => '',
124
+ '_exception' => 'Boom',
125
+ }
126
+
127
+ aggregate_failures do
128
+ expect(json_line).to include(expected)
129
+ expect(json_line['_duration']).to be_a(Float).or be_a(Integer)
130
+ expect(json_line['timestamp']).to be_a(Float)
131
+ expect(json_line['host']).to be_a(String)
132
+ expect(json_line['short_message'])
133
+ .to match(/HardWorker with jid: '591f6f66ee0d218fb451dfb6' fail/)
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  RSpec.describe Loga::Sidekiq do
@@ -30,11 +32,23 @@ RSpec.describe Loga::Sidekiq do
30
32
 
31
33
  m = ENV['BUNDLE_GEMFILE'].match(/sidekiq(?<version>\d+)/)
32
34
 
35
+ job_logger =
36
+ if Sidekiq.respond_to?(:options)
37
+ Sidekiq.options[:job_logger]
38
+ else
39
+ sidekiq_config = Sidekiq.instance_variable_get(:@config)
40
+ sidekiq_config && sidekiq_config[:job_logger]
41
+ end
42
+
33
43
  case m['version']
34
44
  when '51'
35
- expect(::Sidekiq.options[:job_logger]).to eq(Loga::Sidekiq5::JobLogger)
36
- when '6'
37
- expect(::Sidekiq.options[:job_logger]).to eq(Loga::Sidekiq6::JobLogger)
45
+ expect(job_logger).to eq(Loga::Sidekiq5::JobLogger)
46
+ when '6', '60', '61', '62', '63', '64', '65'
47
+ expect(job_logger).to eq(Loga::Sidekiq6::JobLogger)
48
+ when '7', '70', '71', '72', '73'
49
+ expect(job_logger).to eq(Loga::Sidekiq7::JobLogger)
50
+ when '8', '80'
51
+ expect(job_logger).to eq(Loga::Sidekiq8::JobLogger)
38
52
  end
39
53
  end
40
54
  end
data/spec/spec_helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'byebug'
2
4
  require 'pry'
3
5
  require 'support/gethostname_shared'
@@ -8,16 +10,17 @@ require 'rack/test'
8
10
  require 'simplecov'
9
11
 
10
12
  SimpleCov.start do
11
- command_name "ruby-#{RUBY_VERSION}-#{File.basename(ENV['BUNDLE_GEMFILE'], '.gemfile')}"
13
+ command_name "ruby-#{RUBY_VERSION}-#{File.basename(ENV.fetch('BUNDLE_GEMFILE', nil),
14
+ '.gemfile')}"
12
15
 
13
16
  # Exclude specs from showing up in the code coverage report.
14
17
  add_filter 'spec/'
15
18
  end
16
19
 
17
- case ENV['BUNDLE_GEMFILE']
20
+ case ENV.fetch('BUNDLE_GEMFILE', nil)
18
21
  when /rails/
19
22
  rspec_pattern = 'integration/rails/**/*_spec.rb'
20
- /(?<appraisal>rails\d{2})\.gemfile/ =~ ENV['BUNDLE_GEMFILE']
23
+ /(?<appraisal>rails\d{2})\.gemfile/ =~ ENV.fetch('BUNDLE_GEMFILE', nil)
21
24
  require 'rails'
22
25
  require 'action_mailer'
23
26
  require File.expand_path("../fixtures/#{appraisal}.rb", __FILE__)
@@ -56,10 +59,16 @@ when /sidekiq(?<version>\d+)/
56
59
  'spec/loga/sidekiq6/**/*_spec.rb',
57
60
  'spec/loga/sidekiq_spec.rb',
58
61
  ].join(',')
59
- when '7', '70', '71'
62
+ when '7', '70', '71', '72', '73'
60
63
  rspec_pattern = [
61
64
  'spec/integration/sidekiq7_spec.rb',
62
- 'spec/loga/sidekiq7/**/*_spec.rb',
65
+ 'spec/loga/sidekiq_latest/**/*_spec.rb',
66
+ 'spec/loga/sidekiq_spec.rb',
67
+ ].join(',')
68
+ when '8', '80'
69
+ rspec_pattern = [
70
+ 'spec/integration/sidekiq8_spec.rb',
71
+ 'spec/loga/sidekiq_latest/**/*_spec.rb',
63
72
  'spec/loga/sidekiq_spec.rb',
64
73
  ].join(',')
65
74
  else
@@ -1,4 +1,6 @@
1
- shared_context 'gethostname', with_hostname: true do
1
+ # frozen_string_literal: true
2
+
3
+ shared_context 'gethostname', :with_hostname do
2
4
  let(:hostname) { 'bird.example.com' }
3
5
 
4
6
  before do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bigdecimal'
2
4
 
3
5
  module Helpers
@@ -16,4 +18,12 @@ module Helpers
16
18
  allow(loga).to receive(:logger).and_return(logger)
17
19
  loga
18
20
  end
21
+
22
+ def with_new_ruby(**data)
23
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4')
24
+ data[:test]
25
+ else
26
+ data[:else]
27
+ end
28
+ end
19
29
  end