karafka-core 2.5.1 → 2.5.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: '01592d62e014e631fffd3ae9477c8979e2df94ce3958b068d6ab5a79755e69aa'
4
- data.tar.gz: 65ce9e27480ec280e3b9f568ba09bf74e64298c7c14e796872a8f8bc40dd2541
3
+ metadata.gz: 8893405950939cce7d2c5f908984363927426b166cfe3afbd3a4ace9895acbe7
4
+ data.tar.gz: 19db627e288c6ae32af18be68691ca9cf4754447a6e4ee3f3a47ac8f8025ffb1
5
5
  SHA512:
6
- metadata.gz: f785c2934587b78e42226b798bfa3023f4ed6985bb5e2048920fd5173df3e920f367c15a66e160d257c4d01c8265d7125ccfdcd3022ff1bc73685b6dcee0dde2
7
- data.tar.gz: 7f38ce66ec1e49c993e207e1b3967637d99f04c6e62b1c852fb5788fb4348ff6942a8c59c2d23d4735e982db2a4a820773653f292edc2ce022aef61b2cf6abb1
6
+ metadata.gz: 5c680542fb2a726ec1339a2c157d6579a3bb76fac4204676721fc2ed75f52ef6ef1bd3c2ab03983e066603014d74e804a2e737656dfba55ad9858ae57cceda57
7
+ data.tar.gz: 4f27f566b94e246ff989edf08680574a336f3a9a85d46008d22c3de8abfbd7a7adac87158f51639d50a03efb502232bc20fc3ba29d081db596688b635985dac8
@@ -1,4 +1,4 @@
1
- name: ci
1
+ name: CI
2
2
 
3
3
  concurrency:
4
4
  group: ${{ github.workflow }}-${{ github.ref }}
@@ -24,6 +24,7 @@ jobs:
24
24
  fail-fast: false
25
25
  matrix:
26
26
  ruby:
27
+ - '3.5.0-preview1'
27
28
  - '3.4'
28
29
  - '3.3'
29
30
  - '3.2'
@@ -40,7 +41,7 @@ jobs:
40
41
  run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends $APT_DEPS"
41
42
 
42
43
  - name: Set up Ruby
43
- uses: ruby/setup-ruby@1a0ff446f5856bdfec298b61a09727c860d9d480 # v1.240.0
44
+ uses: ruby/setup-ruby@bb6434c747fa7022e12fa1cae2a0951fcffcff26 # v1.253.0
44
45
  with:
45
46
  ruby-version: ${{matrix.ruby}}
46
47
  bundler: 'latest'
@@ -71,7 +72,7 @@ jobs:
71
72
  with:
72
73
  fetch-depth: 0
73
74
  - name: Set up Ruby
74
- uses: ruby/setup-ruby@1a0ff446f5856bdfec298b61a09727c860d9d480 # v1.240.0
75
+ uses: ruby/setup-ruby@bb6434c747fa7022e12fa1cae2a0951fcffcff26 # v1.253.0
75
76
  with:
76
77
  ruby-version: 3.4
77
78
  - name: Install latest bundler
@@ -24,7 +24,7 @@ jobs:
24
24
  fetch-depth: 0
25
25
 
26
26
  - name: Set up Ruby
27
- uses: ruby/setup-ruby@1a0ff446f5856bdfec298b61a09727c860d9d480 # v1.240.0
27
+ uses: ruby/setup-ruby@bb6434c747fa7022e12fa1cae2a0951fcffcff26 # v1.253.0
28
28
  with:
29
29
  bundler-cache: false
30
30
 
@@ -32,5 +32,4 @@ jobs:
32
32
  run: |
33
33
  bundle install --jobs 4 --retry 3
34
34
 
35
- # Release
36
- - uses: rubygems/release-gem@9e85cb11501bebc2ae661c1500176316d3987059 # v1
35
+ - uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1
@@ -0,0 +1,30 @@
1
+ name: Trigger Wiki Refresh
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ push:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ trigger-wiki-refresh:
11
+ runs-on: ubuntu-latest
12
+ environment: wiki-trigger
13
+ if: github.repository_owner == 'karafka'
14
+ steps:
15
+ - name: Trigger wiki refresh
16
+ uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0
17
+ with:
18
+ token: ${{ secrets.WIKI_REPO_TOKEN }}
19
+ repository: karafka/wiki
20
+ event-type: sync-trigger
21
+ client-payload: |
22
+ {
23
+ "repository": "${{ github.repository }}",
24
+ "event_name": "${{ github.event_name }}",
25
+ "release_tag": "${{ github.event.release.tag_name || '' }}",
26
+ "release_name": "${{ github.event.release.name || '' }}",
27
+ "commit_sha": "${{ github.sha }}",
28
+ "commit_message": "Trigger Wiki Refresh",
29
+ "triggered_by": "${{ github.actor }}"
30
+ }
@@ -4,7 +4,7 @@ on:
4
4
  paths:
5
5
  - '.github/workflows/**'
6
6
  jobs:
7
- verify:
7
+ verify_action_pins:
8
8
  runs-on: ubuntu-latest
9
9
  steps:
10
10
  - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.4
1
+ 3.4.5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Karafka Core Changelog
2
2
 
3
+ ## 2.5.3 (2025-08-04)
4
+ - [Enhancement] Optimize code to mitigate the Ruby performance warning from `Karafka::Core::Configurable::Node` (#208)
5
+ - [Enhancement] Raise errors on detected Ruby warnings.
6
+ - [Change] Remove unused Ruby 2.7 code.
7
+ - [Change] Remove `funding_uri` from the gemspec to minimize double-funding info.
8
+
9
+ ## 2.5.2 (2025-06-11)
10
+ - [Enhancement] Support `#unsubscribe`.
11
+ - [Enhancement] Allow for providing a root scope path for error keys.
12
+ - [Fix] Fix a bug where on no errors the result would be an array instead of a hash.
13
+ - [Fix] Fix spec hanging when Kafka cluster on 9092 is running.
14
+
3
15
  ## 2.5.1 (2025-05-23)
4
16
  - [Change] Move to trusted-publishers and remove signing since no longer needed.
5
17
 
data/Gemfile CHANGED
@@ -10,4 +10,5 @@ group :test do
10
10
  gem 'byebug'
11
11
  gem 'rspec'
12
12
  gem 'simplecov'
13
+ gem 'warning'
13
14
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka-core (2.5.1)
4
+ karafka-core (2.5.3)
5
5
  karafka-rdkafka (>= 0.19.2, < 0.21.0)
6
6
  logger (>= 1.6.0)
7
7
 
@@ -22,32 +22,33 @@ GEM
22
22
  ffi (1.17.2-x86_64-darwin)
23
23
  ffi (1.17.2-x86_64-linux-gnu)
24
24
  ffi (1.17.2-x86_64-linux-musl)
25
- karafka-rdkafka (0.19.2)
25
+ karafka-rdkafka (0.19.5)
26
26
  ffi (~> 1.15)
27
27
  mini_portile2 (~> 2.6)
28
28
  rake (> 12)
29
29
  logger (1.7.0)
30
30
  mini_portile2 (2.8.9)
31
- rake (13.2.1)
32
- rspec (3.13.0)
31
+ rake (13.3.0)
32
+ rspec (3.13.1)
33
33
  rspec-core (~> 3.13.0)
34
34
  rspec-expectations (~> 3.13.0)
35
35
  rspec-mocks (~> 3.13.0)
36
- rspec-core (3.13.3)
36
+ rspec-core (3.13.4)
37
37
  rspec-support (~> 3.13.0)
38
- rspec-expectations (3.13.4)
38
+ rspec-expectations (3.13.5)
39
39
  diff-lcs (>= 1.2.0, < 2.0)
40
40
  rspec-support (~> 3.13.0)
41
- rspec-mocks (3.13.4)
41
+ rspec-mocks (3.13.5)
42
42
  diff-lcs (>= 1.2.0, < 2.0)
43
43
  rspec-support (~> 3.13.0)
44
- rspec-support (3.13.3)
44
+ rspec-support (3.13.4)
45
45
  simplecov (0.22.0)
46
46
  docile (~> 1.1)
47
47
  simplecov-html (~> 0.11)
48
48
  simplecov_json_formatter (~> 0.1)
49
49
  simplecov-html (0.13.1)
50
50
  simplecov_json_formatter (0.1.4)
51
+ warning (1.5.0)
51
52
 
52
53
  PLATFORMS
53
54
  aarch64-linux-gnu
@@ -67,6 +68,7 @@ DEPENDENCIES
67
68
  karafka-core!
68
69
  rspec
69
70
  simplecov
71
+ warning
70
72
 
71
73
  BUNDLED WITH
72
74
  2.6.9
data/karafka-core.gemspec CHANGED
@@ -25,7 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = %w[lib]
26
26
 
27
27
  spec.metadata = {
28
- 'funding_uri' => 'https://karafka.io/#become-pro',
29
28
  'homepage_uri' => 'https://karafka.io',
30
29
  'changelog_uri' => 'https://karafka.io/docs/Changelog-Karafka-Core',
31
30
  'bug_tracker_uri' => 'https://github.com/karafka/karafka-core/issues',
@@ -22,6 +22,7 @@ module Karafka
22
22
  @children = []
23
23
  @nestings = nestings
24
24
  @compiled = false
25
+ @configs_refs = {}
25
26
  instance_eval(&nestings)
26
27
  end
27
28
 
@@ -58,7 +59,14 @@ module Karafka
58
59
 
59
60
  @children.each do |value|
60
61
  config[value.node_name] = if value.is_a?(Leaf)
61
- result = public_send(value.node_name)
62
+ result = if @configs_refs.key?(value.node_name)
63
+ @configs_refs[value.node_name]
64
+ elsif value.constructor
65
+ value.constructor.call
66
+ elsif value.default
67
+ value.default
68
+ end
69
+
62
70
  # We need to check if value is not a result node for cases
63
71
  # where we merge additional config
64
72
  result.is_a?(Node) ? result.to_h : result
@@ -96,12 +104,12 @@ module Karafka
96
104
  @children.each do |value|
97
105
  # Do not redefine something that was already set during compilation
98
106
  # This will allow us to reconfigure things and skip override with defaults
99
- skippable = respond_to?(value.node_name) || (value.is_a?(Leaf) && value.compiled?)
107
+ skippable = @configs_refs.key?(value.node_name) || (value.is_a?(Leaf) && value.compiled?)
100
108
  lazy_leaf = value.is_a?(Leaf) && value.lazy?
101
109
 
102
110
  # Do not create accessor for leafs that are lazy as they will get a custom method
103
111
  # created instead
104
- singleton_class.attr_accessor(value.node_name) unless lazy_leaf
112
+ build_accessors(value) unless lazy_leaf
105
113
 
106
114
  next if skippable
107
115
 
@@ -123,7 +131,7 @@ module Karafka
123
131
  if lazy_leaf && !initialized
124
132
  build_dynamic_accessor(value)
125
133
  else
126
- public_send("#{value.node_name}=", initialized)
134
+ @configs_refs[value.node_name] = initialized
127
135
  end
128
136
  end
129
137
 
@@ -139,16 +147,18 @@ module Karafka
139
147
  #
140
148
  # @param value [Leaf]
141
149
  def build_dynamic_accessor(value)
142
- singleton_class.attr_writer(value.node_name)
143
-
144
150
  define_singleton_method(value.node_name) do
145
- existing = instance_variable_get("@#{value.node_name}")
151
+ existing = @configs_refs.fetch(value.node_name, false)
146
152
 
147
- return existing if existing
153
+ return existing unless existing == false
148
154
 
149
155
  built = call_constructor(value)
150
156
 
151
- instance_variable_set("@#{value.node_name}", built)
157
+ @configs_refs[value.node_name] = built
158
+ end
159
+
160
+ define_singleton_method(:"#{value.node_name}=") do |new_value|
161
+ @configs_refs[value.node_name] = new_value
152
162
  end
153
163
  end
154
164
 
@@ -165,6 +175,23 @@ module Karafka
165
175
  constructor.call(value.default)
166
176
  end
167
177
  end
178
+
179
+ # Builds regular accessors for value fetching
180
+ #
181
+ # @param value [Leaf]
182
+ def build_accessors(value)
183
+ unless respond_to?(value.node_name.to_sym)
184
+ define_singleton_method(value.node_name) do
185
+ @configs_refs[value.node_name]
186
+ end
187
+ end
188
+
189
+ return if respond_to?(:"#{value.node_name}=")
190
+
191
+ define_singleton_method(:"#{value.node_name}=") do |new_value|
192
+ @configs_refs[value.node_name] = new_value
193
+ end
194
+ end
168
195
  end
169
196
  end
170
197
  end
@@ -63,23 +63,9 @@ module Karafka
63
63
  config.configure(&block)
64
64
  end
65
65
 
66
- # Two versions are needed to pass arguments in the correct way
67
- if RUBY_VERSION >= '2.7'
68
- class_eval <<~CODE, __FILE__, __LINE__ + 1
69
- # Pipes the settings setup to the config root node
70
- def setting(...)
71
- config.setting(...)
72
- end
73
- CODE
74
- else
75
- class_eval <<~CODE, __FILE__, __LINE__ + 1
76
- # Pipes the settings setup to the config root node
77
- # @param args [Object] anything provided to settings
78
- # @param block [Proc] block for settings
79
- def setting(*args, &block)
80
- config.setting(*args, &block)
81
- end
82
- CODE
66
+ # Pipes the settings setup to the config root node
67
+ def setting(...)
68
+ config.setting(...)
83
69
  end
84
70
  end
85
71
  end
@@ -78,18 +78,20 @@ module Karafka
78
78
  # Runs the validation
79
79
  #
80
80
  # @param data [Hash] hash with data we want to validate
81
+ # @param scope [Array<String>] scope of this contract (if any) or empty array if no parent
82
+ # scope is needed if contract starts from root
81
83
  # @return [Result] validaton result
82
- def call(data)
84
+ def call(data, scope: [])
83
85
  errors = []
84
86
 
85
87
  self.class.rules.each do |rule|
86
88
  case rule.type
87
89
  when :required
88
- validate_required(data, rule, errors)
90
+ validate_required(data, rule, errors, scope)
89
91
  when :optional
90
- validate_optional(data, rule, errors)
92
+ validate_optional(data, rule, errors, scope)
91
93
  when :virtual
92
- validate_virtual(data, rule, errors)
94
+ validate_virtual(data, rule, errors, scope)
93
95
  end
94
96
  end
95
97
 
@@ -98,11 +100,13 @@ module Karafka
98
100
 
99
101
  # @param data [Hash] data for validation
100
102
  # @param error_class [Class] error class that should be used when validation fails
103
+ # @param scope [Array<String>] scope of this contract (if any) or empty array if no parent
104
+ # scope is needed if contract starts from root
101
105
  # @return [Boolean] true
102
106
  # @raise [StandardError] any error provided in the error_class that inherits from the
103
107
  # standard error
104
- def validate!(data, error_class)
105
- result = call(data)
108
+ def validate!(data, error_class, scope: [])
109
+ result = call(data, scope: scope)
106
110
 
107
111
  return true if result.success?
108
112
 
@@ -117,19 +121,20 @@ module Karafka
117
121
  # @param data [Hash] input hash
118
122
  # @param rule [Rule] validation rule
119
123
  # @param errors [Array] array with errors from previous rules (if any)
120
- def validate_required(data, rule, errors)
124
+ # @param scope [Array<String>]
125
+ def validate_required(data, rule, errors, scope)
121
126
  for_checking = dig(data, rule.path)
122
127
 
123
128
  # We need to compare `DIG_MISS` against stuff because of the ownership of the `#==`
124
129
  # method
125
130
  if DIG_MISS == for_checking
126
- errors << [rule.path, :missing]
131
+ errors << [scope + rule.path, :missing]
127
132
  else
128
133
  result = rule.validator.call(for_checking, data, errors, self)
129
134
 
130
135
  return if result == true
131
136
 
132
- errors << [rule.path, result || :format]
137
+ errors << [scope + rule.path, result || :format]
133
138
  end
134
139
  end
135
140
 
@@ -139,7 +144,8 @@ module Karafka
139
144
  # @param data [Hash] input hash
140
145
  # @param rule [Rule] validation rule
141
146
  # @param errors [Array] array with errors from previous rules (if any)
142
- def validate_optional(data, rule, errors)
147
+ # @param scope [Array<String>]
148
+ def validate_optional(data, rule, errors, scope)
143
149
  for_checking = dig(data, rule.path)
144
150
 
145
151
  return if DIG_MISS == for_checking
@@ -148,7 +154,7 @@ module Karafka
148
154
 
149
155
  return if result == true
150
156
 
151
- errors << [rule.path, result || :format]
157
+ errors << [scope + rule.path, result || :format]
152
158
  end
153
159
 
154
160
  # Runs validation for rules on virtual fields (aggregates, etc) and adds errors (if any) to
@@ -157,11 +163,18 @@ module Karafka
157
163
  # @param data [Hash] input hash
158
164
  # @param rule [Rule] validation rule
159
165
  # @param errors [Array] array with errors from previous rules (if any)
160
- def validate_virtual(data, rule, errors)
166
+ # @param scope [Array<String>]
167
+ def validate_virtual(data, rule, errors, scope)
161
168
  result = rule.validator.call(data, errors, self)
162
169
 
163
170
  return if result == true
164
171
 
172
+ if result
173
+ result.each do |sub_result|
174
+ sub_result[0] = scope + sub_result[0]
175
+ end
176
+ end
177
+
165
178
  errors.push(*result)
166
179
  end
167
180
 
@@ -14,7 +14,7 @@ module Karafka
14
14
  def initialize(errors, contract)
15
15
  # Short track to skip object allocation for the happy path
16
16
  if errors.empty?
17
- @errors = errors
17
+ @errors = {}
18
18
  return
19
19
  end
20
20
 
@@ -41,7 +41,8 @@ module Karafka
41
41
 
42
42
  private
43
43
 
44
- # Builds message based on the error messages
44
+ # Builds message based on the error messages and scope
45
+ #
45
46
  # @param contract [Object] contract for which we build the result
46
47
  # @param scope [Symbol] path to the key that has an error
47
48
  # @param error_key [Symbol] error key for yaml errors lookup
@@ -49,6 +50,25 @@ module Karafka
49
50
  def build_message(contract, scope, error_key)
50
51
  messages = contract.class.config.error_messages
51
52
 
53
+ # Split scope into parts for progressive checking
54
+ scope_parts = scope.to_s.split('.')
55
+
56
+ # Try full scope first, then progressively remove from beginning
57
+ # This allows us to have full path scoped errors but can also be used as a fallback,
58
+ # when scopes are dynamic. For example 'consumer_group_name.topic_name.name_format'
59
+ (0..scope_parts.length).each do |i|
60
+ current_scope_parts = scope_parts[i..]
61
+
62
+ key = if current_scope_parts.empty?
63
+ error_key.to_s
64
+ else
65
+ "#{current_scope_parts.join('.')}_#{error_key}"
66
+ end
67
+
68
+ return messages[key] if messages.key?(key)
69
+ end
70
+
71
+ # If nothing found, raise the original error
52
72
  messages.fetch(error_key.to_s) do
53
73
  messages.fetch("#{scope}_#{error_key}")
54
74
  end
@@ -42,6 +42,14 @@ module Karafka
42
42
  @notifications_bus.subscribe(*args, &block)
43
43
  end
44
44
 
45
+ # Allows for removal of whatever was subscribed
46
+ #
47
+ # @param listener_or_block [Object] object that is subscribed whether this is a listener
48
+ # instance or a block.
49
+ def unsubscribe(listener_or_block)
50
+ @notifications_bus.unsubscribe(listener_or_block)
51
+ end
52
+
45
53
  # @return [Hash<String, Array>] hash where keys are events and values are arrays with
46
54
  # listeners subscribed to particular events. Since different events may have different
47
55
  # listeners, this is returned that way.
@@ -89,6 +89,21 @@ module Karafka
89
89
  end
90
90
  end
91
91
 
92
+ # Allows for unsubscription from events
93
+ # This method will remove the listener/block from all events where it's currently subscribed.
94
+ #
95
+ # @param listener_or_block [Object] listener object or block to remove from all events
96
+ #
97
+ # @example Unsubscribe using listener (removes from all events where it's subscribed)
98
+ # unsubscribe(my_listener)
99
+ def unsubscribe(listener_or_block)
100
+ @mutex.synchronize do
101
+ @listeners.each_value do |event_listeners|
102
+ event_listeners.delete(listener_or_block)
103
+ end
104
+ end
105
+ end
106
+
92
107
  # Allows for code instrumentation
93
108
  # Runs the provided code and sends the instrumentation details to all registered listeners
94
109
  #
@@ -4,6 +4,6 @@ module Karafka
4
4
  module Core
5
5
  # Current Karafka::Core version
6
6
  # We follow the versioning schema of given Karafka version
7
- VERSION = '2.5.1'
7
+ VERSION = '2.5.3'
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: karafka-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.1
4
+ version: 2.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Mensfeld
@@ -59,6 +59,7 @@ files:
59
59
  - ".github/ISSUE_TEMPLATE/feature_request.md"
60
60
  - ".github/workflows/ci.yml"
61
61
  - ".github/workflows/push.yml"
62
+ - ".github/workflows/trigger-wiki-refresh.yml"
62
63
  - ".github/workflows/verify-action-pins.yml"
63
64
  - ".gitignore"
64
65
  - ".rspec"
@@ -101,7 +102,6 @@ homepage: https://karafka.io
101
102
  licenses:
102
103
  - MIT
103
104
  metadata:
104
- funding_uri: https://karafka.io/#become-pro
105
105
  homepage_uri: https://karafka.io
106
106
  changelog_uri: https://karafka.io/docs/Changelog-Karafka-Core
107
107
  bug_tracker_uri: https://github.com/karafka/karafka-core/issues
@@ -122,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
122
  - !ruby/object:Gem::Version
123
123
  version: '0'
124
124
  requirements: []
125
- rubygems_version: 3.6.7
125
+ rubygems_version: 3.6.9
126
126
  specification_version: 4
127
127
  summary: Karafka ecosystem core modules
128
128
  test_files: []