karafka-core 2.5.10 → 2.5.12
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 +4 -4
- data/.github/workflows/ci.yml +8 -8
- data/.github/workflows/push.yml +2 -2
- data/.ruby-version +1 -1
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/karafka-core.gemspec +1 -1
- data/lib/karafka/core/configurable/node.rb +8 -6
- data/lib/karafka/core/contractable/contract.rb +35 -9
- data/lib/karafka/core/contractable/result.rb +9 -0
- data/lib/karafka/core/instrumentation/callbacks_manager.rb +6 -1
- data/lib/karafka/core/monitoring/event.rb +21 -4
- data/lib/karafka/core/monitoring/notifications.rb +5 -16
- data/lib/karafka/core/monitoring/statistics_decorator.rb +192 -39
- data/lib/karafka/core/version.rb +1 -1
- metadata +2 -26
- data/test/lib/karafka/core/configurable/leaf_test.rb +0 -3
- data/test/lib/karafka/core/configurable/node_test.rb +0 -3
- data/test/lib/karafka/core/configurable_test.rb +0 -504
- data/test/lib/karafka/core/contractable/contract_test.rb +0 -241
- data/test/lib/karafka/core/contractable/result_test.rb +0 -106
- data/test/lib/karafka/core/contractable/rule_test.rb +0 -5
- data/test/lib/karafka/core/contractable_test.rb +0 -3
- data/test/lib/karafka/core/helpers/time_test.rb +0 -29
- data/test/lib/karafka/core/instrumentation/callbacks_manager_test.rb +0 -81
- data/test/lib/karafka/core/instrumentation_test.rb +0 -35
- data/test/lib/karafka/core/monitoring/event_test.rb +0 -25
- data/test/lib/karafka/core/monitoring/monitor_test.rb +0 -237
- data/test/lib/karafka/core/monitoring/notifications_test.rb +0 -275
- data/test/lib/karafka/core/monitoring/statistics_decorator_test.rb +0 -284
- data/test/lib/karafka/core/monitoring_test.rb +0 -3
- data/test/lib/karafka/core/patches/rdkafka/bindings_test.rb +0 -25
- data/test/lib/karafka/core/taggable/tags_test.rb +0 -66
- data/test/lib/karafka/core/taggable_test.rb +0 -36
- data/test/lib/karafka/core/version_test.rb +0 -5
- data/test/lib/karafka/core_test.rb +0 -13
- data/test/lib/karafka-core_test.rb +0 -3
- data/test/support/class_builder.rb +0 -24
- data/test/support/describe_current_helper.rb +0 -41
- data/test/test_helper.rb +0 -55
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a1ca213972a7e05632f164e423383f6a9172926291140ad6005c822b347855b2
|
|
4
|
+
data.tar.gz: acb97b8887fed4e6534bed1c8c764a19a38462b6a6581893ed80b3bb1efd7a82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0d3ead51c6a1fe7697cb5c13d914acd148c65d0be5b04be88b898d8cb5e220f65095bab181083a993a6085483c2f5815cb69c5cbd31cf92bcd246ea72c2f343a
|
|
7
|
+
data.tar.gz: 1210463f030fe15c691b0d136498327ae9a83128b5f871a256401bd498ebfd4117391d250ee49a1ab53c1f5f4fd427db292755bc792ea73a99435535bb79d115
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -14,7 +14,7 @@ permissions:
|
|
|
14
14
|
contents: read
|
|
15
15
|
|
|
16
16
|
jobs:
|
|
17
|
-
|
|
17
|
+
tests:
|
|
18
18
|
timeout-minutes: 15
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
strategy:
|
|
@@ -37,7 +37,7 @@ jobs:
|
|
|
37
37
|
run: "[ -e $APT_DEPS ] || sudo apt-get install -y --no-install-recommends $APT_DEPS"
|
|
38
38
|
|
|
39
39
|
- name: Set up Ruby
|
|
40
|
-
uses: ruby/setup-ruby@
|
|
40
|
+
uses: ruby/setup-ruby@c515ec17f69368147deb311832da000dd229d338 # v1.297.0
|
|
41
41
|
with:
|
|
42
42
|
ruby-version: ${{matrix.ruby}}
|
|
43
43
|
bundler: 'latest'
|
|
@@ -69,9 +69,9 @@ jobs:
|
|
|
69
69
|
with:
|
|
70
70
|
fetch-depth: 0
|
|
71
71
|
- name: Set up Ruby
|
|
72
|
-
uses: ruby/setup-ruby@
|
|
72
|
+
uses: ruby/setup-ruby@c515ec17f69368147deb311832da000dd229d338 # v1.297.0
|
|
73
73
|
with:
|
|
74
|
-
ruby-version: '4.0.
|
|
74
|
+
ruby-version: '4.0.2'
|
|
75
75
|
bundler-cache: true
|
|
76
76
|
- name: Run rubocop
|
|
77
77
|
run: bundle exec rubocop
|
|
@@ -86,9 +86,9 @@ jobs:
|
|
|
86
86
|
with:
|
|
87
87
|
fetch-depth: 0
|
|
88
88
|
- name: Set up Ruby
|
|
89
|
-
uses: ruby/setup-ruby@
|
|
89
|
+
uses: ruby/setup-ruby@c515ec17f69368147deb311832da000dd229d338 # v1.297.0
|
|
90
90
|
with:
|
|
91
|
-
ruby-version: '4.0.
|
|
91
|
+
ruby-version: '4.0.2'
|
|
92
92
|
bundler-cache: true
|
|
93
93
|
- name: Run yard-lint
|
|
94
94
|
run: bundle exec yard-lint lib/
|
|
@@ -101,7 +101,7 @@ jobs:
|
|
|
101
101
|
with:
|
|
102
102
|
fetch-depth: 0
|
|
103
103
|
- name: Set up Node.js
|
|
104
|
-
uses: actions/setup-node@
|
|
104
|
+
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
105
105
|
with:
|
|
106
106
|
node-version: '20'
|
|
107
107
|
cache: 'npm'
|
|
@@ -116,7 +116,7 @@ jobs:
|
|
|
116
116
|
if: always()
|
|
117
117
|
needs:
|
|
118
118
|
- rubocop
|
|
119
|
-
-
|
|
119
|
+
- tests
|
|
120
120
|
- yard-lint
|
|
121
121
|
- lostconf
|
|
122
122
|
steps:
|
data/.github/workflows/push.yml
CHANGED
|
@@ -24,7 +24,7 @@ jobs:
|
|
|
24
24
|
fetch-depth: 0
|
|
25
25
|
|
|
26
26
|
- name: Set up Ruby
|
|
27
|
-
uses: ruby/setup-ruby@
|
|
27
|
+
uses: ruby/setup-ruby@c515ec17f69368147deb311832da000dd229d338 # v1.297.0
|
|
28
28
|
with:
|
|
29
29
|
bundler-cache: false
|
|
30
30
|
|
|
@@ -32,4 +32,4 @@ jobs:
|
|
|
32
32
|
run: |
|
|
33
33
|
bundle install --jobs 4 --retry 3
|
|
34
34
|
|
|
35
|
-
- uses: rubygems/release-gem@
|
|
35
|
+
- uses: rubygems/release-gem@e9a6361a0b14562539327c2a02373edc56dd3169 # v1.1.4
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.0.
|
|
1
|
+
4.0.2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Karafka Core Changelog
|
|
2
2
|
|
|
3
|
+
## 2.5.12 (2026-04-02)
|
|
4
|
+
- [Fix] Exclude `test/` directory from gem releases to reduce package size.
|
|
5
|
+
|
|
6
|
+
## 2.5.11 (2026-04-02)
|
|
7
|
+
- [Enhancement] Specialize `Contract#dig` for common 1-key and 2-key paths to avoid iterator overhead, yielding ~1.5x faster single-key lookups and ~1.45x faster two-key nested lookups.
|
|
8
|
+
- [Enhancement] Replace `Node#build_accessors` `@local_defs` Array with Hash for O(1) membership checks instead of O(n) `Array#include?`, yielding up to ~5x faster accessor lookups at 50 settings.
|
|
9
|
+
- [Enhancement] Use frozen `EMPTY_ARRAY` constant for `Contract#call` and `#validate!` default `scope` parameter to avoid allocating a new Array on every invocation, yielding ~1.36x faster call dispatch and saving 1 Array allocation per call.
|
|
10
|
+
- [Enhancement] Pre-resolve `@events_methods_map` method name before the listener notification loop in `Notifications#instrument` to avoid repeated Hash lookup per listener, yielding ~1.12x faster event dispatch with multiple listeners.
|
|
11
|
+
- [Enhancement] Cache a frozen success `Result` singleton via `Result.success` to eliminate 1 object allocation per successful `Contract#call` on the happy path.
|
|
12
|
+
- [Enhancement] Skip nestings block re-evaluation in `Node#deep_dup` to avoid recreating children that are immediately overwritten, yielding ~14x faster deep_dup and reducing allocations from ~620 to ~66 objects for large configs.
|
|
13
|
+
- [Enhancement] Cache `CallbacksManager#call` values snapshot and invalidate on `add`/`delete` to avoid allocating a new Array on every invocation while preserving thread-safety snapshot semantics, saving 1 Array allocation per call.
|
|
14
|
+
- [Enhancement] Store execution time separately in `Event` and build the merged payload hash lazily on `#payload` access, eliminating 1 Hash allocation per `Notifications#instrument` call when listeners use `#[]` access (the common pattern), yielding ~1.7x faster event dispatch.
|
|
15
|
+
- [Enhancement] Replace `StatisticsDecorator#diff` pending-writes buffer with `keys.each` direct-write iteration, eliminating the buffer and write-back loop for ~13% faster decoration at scale (10 brokers, 20 topics, 2000 partitions).
|
|
16
|
+
- [Enhancement] Reorder `StatisticsDecorator#diff` type checks to test `Numeric` before `Hash`, matching the ~80% numeric value distribution in librdkafka statistics.
|
|
17
|
+
- [Enhancement] Support `only_keys` option in `StatisticsDecorator` to decorate only specified numeric keys (e.g. `consumer_lag`, `committed_offset`). When combined with `excluded_keys`, reduces decoration cost from ~80ms to ~8.5ms per call on large clusters (10 brokers, 20 topics, 2000 partitions) by using structure-aware navigation of the librdkafka statistics tree and direct key access instead of full-hash iteration.
|
|
18
|
+
|
|
3
19
|
## 2.5.10 (2026-03-02)
|
|
4
20
|
- [Enhancement] Introduce `MinitestLocator` helper for minitest/spec subject class auto-discovery from test file paths.
|
|
5
21
|
|
data/Gemfile.lock
CHANGED
data/karafka-core.gemspec
CHANGED
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
|
|
22
22
|
spec.required_ruby_version = ">= 3.2.0"
|
|
23
23
|
|
|
24
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(
|
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test)/}) }
|
|
25
25
|
spec.require_paths = %w[lib]
|
|
26
26
|
|
|
27
27
|
spec.metadata = {
|
|
@@ -17,14 +17,16 @@ module Karafka
|
|
|
17
17
|
|
|
18
18
|
# @param node_name [Symbol] node name
|
|
19
19
|
# @param nestings [Proc] block for nested settings
|
|
20
|
-
|
|
20
|
+
# @param evaluate [Boolean] when false, skip evaluating the nestings block. Used by
|
|
21
|
+
# deep_dup to avoid re-creating children that will be overwritten immediately.
|
|
22
|
+
def initialize(node_name, nestings = ->(_) {}, evaluate: true)
|
|
21
23
|
@node_name = node_name
|
|
22
24
|
@children = []
|
|
23
25
|
@nestings = nestings
|
|
24
26
|
@compiled = false
|
|
25
27
|
@configs_refs = {}
|
|
26
|
-
@local_defs =
|
|
27
|
-
instance_eval(&nestings)
|
|
28
|
+
@local_defs = {}
|
|
29
|
+
instance_eval(&nestings) if evaluate
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
# Allows for a single leaf or nested node definition
|
|
@@ -83,7 +85,7 @@ module Karafka
|
|
|
83
85
|
# and non-side-effect usage on an instance/inherited.
|
|
84
86
|
# @return [Node] duplicated node
|
|
85
87
|
def deep_dup
|
|
86
|
-
dupped = Node.new(node_name, nestings)
|
|
88
|
+
dupped = Node.new(node_name, nestings, evaluate: false)
|
|
87
89
|
|
|
88
90
|
children.each do |value|
|
|
89
91
|
dupped.children << if value.is_a?(Leaf)
|
|
@@ -189,8 +191,8 @@ module Karafka
|
|
|
189
191
|
# object and then we would not redefine it for nodes. This ensures that we only do not
|
|
190
192
|
# redefine our own definitions but we do redefine any user "accidentally" inherited
|
|
191
193
|
# methods
|
|
192
|
-
if reader_respond ? !@local_defs.
|
|
193
|
-
@local_defs
|
|
194
|
+
if reader_respond ? !@local_defs.key?(reader_name) : true
|
|
195
|
+
@local_defs[reader_name] = true
|
|
194
196
|
|
|
195
197
|
define_singleton_method(reader_name) do
|
|
196
198
|
@configs_refs[reader_name]
|
|
@@ -14,7 +14,12 @@ module Karafka
|
|
|
14
14
|
# prevent additional array allocation
|
|
15
15
|
DIG_MISS = Object.new
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
# Empty array for scope default to avoid allocating a new Array on each
|
|
18
|
+
# `#call` / `#validate!` invocation. Safe because scope is never mutated – it is only
|
|
19
|
+
# used in `scope + rule.path` which creates a new Array.
|
|
20
|
+
EMPTY_ARRAY = [].freeze
|
|
21
|
+
|
|
22
|
+
private_constant :DIG_MISS, :EMPTY_ARRAY
|
|
18
23
|
|
|
19
24
|
# Yaml based error messages data
|
|
20
25
|
setting(:error_messages)
|
|
@@ -80,7 +85,7 @@ module Karafka
|
|
|
80
85
|
# @param scope [Array<String>] scope of this contract (if any) or empty array if no parent
|
|
81
86
|
# scope is needed if contract starts from root
|
|
82
87
|
# @return [Result] validaton result
|
|
83
|
-
def call(data, scope:
|
|
88
|
+
def call(data, scope: EMPTY_ARRAY)
|
|
84
89
|
errors = []
|
|
85
90
|
|
|
86
91
|
self.class.rules.each do |rule|
|
|
@@ -94,6 +99,8 @@ module Karafka
|
|
|
94
99
|
end
|
|
95
100
|
end
|
|
96
101
|
|
|
102
|
+
return Result.success if errors.empty?
|
|
103
|
+
|
|
97
104
|
Result.new(errors, self)
|
|
98
105
|
end
|
|
99
106
|
|
|
@@ -104,7 +111,7 @@ module Karafka
|
|
|
104
111
|
# @return [Boolean] true
|
|
105
112
|
# @raise [StandardError] any error provided in the error_class that inherits from the
|
|
106
113
|
# standard error
|
|
107
|
-
def validate!(data, error_class, scope:
|
|
114
|
+
def validate!(data, error_class, scope: EMPTY_ARRAY)
|
|
108
115
|
result = call(data, scope: scope)
|
|
109
116
|
|
|
110
117
|
return true if result.success?
|
|
@@ -183,15 +190,34 @@ module Karafka
|
|
|
183
190
|
# @param keys [Array<Symbol>]
|
|
184
191
|
# @return [DIG_MISS, Object] found element or DIGG_MISS indicating that not found
|
|
185
192
|
def dig(data, keys)
|
|
186
|
-
|
|
193
|
+
case keys.length
|
|
194
|
+
when 1
|
|
195
|
+
key = keys[0]
|
|
187
196
|
|
|
188
|
-
|
|
189
|
-
return DIG_MISS unless current.key?(nesting)
|
|
197
|
+
return DIG_MISS unless data.key?(key)
|
|
190
198
|
|
|
191
|
-
|
|
192
|
-
|
|
199
|
+
data[key]
|
|
200
|
+
when 2
|
|
201
|
+
key1 = keys[0]
|
|
202
|
+
|
|
203
|
+
return DIG_MISS unless data.key?(key1)
|
|
204
|
+
|
|
205
|
+
mid = data[key1]
|
|
206
|
+
|
|
207
|
+
return DIG_MISS unless mid.is_a?(Hash) && mid.key?(keys[1])
|
|
193
208
|
|
|
194
|
-
|
|
209
|
+
mid[keys[1]]
|
|
210
|
+
else
|
|
211
|
+
current = data
|
|
212
|
+
|
|
213
|
+
keys.each do |nesting|
|
|
214
|
+
return DIG_MISS unless current.key?(nesting)
|
|
215
|
+
|
|
216
|
+
current = current[nesting]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
current
|
|
220
|
+
end
|
|
195
221
|
end
|
|
196
222
|
end
|
|
197
223
|
end
|
|
@@ -12,6 +12,15 @@ module Karafka
|
|
|
12
12
|
|
|
13
13
|
private_constant :EMPTY_HASH
|
|
14
14
|
|
|
15
|
+
class << self
|
|
16
|
+
# @return [Result] cached frozen success result to avoid allocating a new Result on
|
|
17
|
+
# every successful contract validation. Safe because successful results are
|
|
18
|
+
# identical regardless of contract (they all have @errors = EMPTY_HASH).
|
|
19
|
+
def success
|
|
20
|
+
@success ||= new([], nil).freeze
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
15
24
|
# Builds a result object and remaps (if needed) error keys to proper error messages
|
|
16
25
|
#
|
|
17
26
|
# @param errors [Array<Array>] array with sub-arrays with paths and error keys
|
|
@@ -10,6 +10,7 @@ module Karafka
|
|
|
10
10
|
# @return [::Karafka::Core::Instrumentation::CallbacksManager]
|
|
11
11
|
def initialize
|
|
12
12
|
@callbacks = {}
|
|
13
|
+
@values_cache = nil
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
# Invokes all the callbacks registered one after another
|
|
@@ -19,8 +20,10 @@ module Karafka
|
|
|
19
20
|
# callbacks and add new at the same time. Since we don't know when and in what thread
|
|
20
21
|
# things are going to be added to the manager, we need to extract values into an array and
|
|
21
22
|
# run it. That way we can add new things the same time.
|
|
23
|
+
# The values snapshot is cached and invalidated on add/delete to avoid allocating a new
|
|
24
|
+
# Array on every call while preserving the thread-safety snapshot semantics.
|
|
22
25
|
def call(*args)
|
|
23
|
-
@callbacks.values.each { |callback| callback.call(*args) }
|
|
26
|
+
(@values_cache ||= @callbacks.values).each { |callback| callback.call(*args) }
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
# Adds a callback to the manager
|
|
@@ -29,12 +32,14 @@ module Karafka
|
|
|
29
32
|
# @param callable [#call] object that responds to a `#call` method
|
|
30
33
|
def add(id, callable)
|
|
31
34
|
@callbacks[id] = callable
|
|
35
|
+
@values_cache = nil
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
# Removes the callback from the manager
|
|
35
39
|
# @param id [String] id of the callback we want to remove
|
|
36
40
|
def delete(id)
|
|
37
41
|
@callbacks.delete(id)
|
|
42
|
+
@values_cache = nil
|
|
38
43
|
end
|
|
39
44
|
end
|
|
40
45
|
end
|
|
@@ -5,20 +5,37 @@ module Karafka
|
|
|
5
5
|
module Monitoring
|
|
6
6
|
# Single notification event wrapping payload with id
|
|
7
7
|
class Event
|
|
8
|
-
attr_reader :id
|
|
8
|
+
attr_reader :id
|
|
9
9
|
|
|
10
10
|
# @param id [String, Symbol] id of the event
|
|
11
11
|
# @param payload [Hash] event payload
|
|
12
|
-
|
|
12
|
+
# @param time [Float, nil] execution time, stored separately to avoid eager hash
|
|
13
|
+
# allocation. Merged into the payload lazily only when `#payload` is accessed.
|
|
14
|
+
def initialize(id, payload, time = nil)
|
|
13
15
|
@id = id
|
|
14
|
-
@
|
|
16
|
+
@raw_payload = payload
|
|
17
|
+
@time = time
|
|
18
|
+
@payload = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return [Hash] full payload including time (if set). The merged hash is built lazily
|
|
22
|
+
# on first access to avoid allocating a new Hash when listeners only use `#[]`.
|
|
23
|
+
def payload
|
|
24
|
+
@payload ||= if @time
|
|
25
|
+
@raw_payload.empty? ? { time: @time } : @raw_payload.merge(time: @time)
|
|
26
|
+
else
|
|
27
|
+
@raw_payload
|
|
28
|
+
end
|
|
15
29
|
end
|
|
16
30
|
|
|
17
31
|
# Hash access to the payload data (if present)
|
|
32
|
+
# Provides direct access to time without triggering payload hash construction.
|
|
18
33
|
#
|
|
19
34
|
# @param name [String, Symbol]
|
|
20
35
|
def [](name)
|
|
21
|
-
@
|
|
36
|
+
return @time if name == :time && @time
|
|
37
|
+
|
|
38
|
+
@raw_payload.fetch(name)
|
|
22
39
|
end
|
|
23
40
|
end
|
|
24
41
|
end
|
|
@@ -139,36 +139,25 @@ module Karafka
|
|
|
139
139
|
return
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
event = Event.new(event_id, final_payload)
|
|
142
|
+
event = Event.new(event_id, payload, time)
|
|
144
143
|
|
|
145
|
-
notify_listeners(event_id, event, assigned_listeners)
|
|
144
|
+
notify_listeners(@events_methods_map[event_id], event, assigned_listeners)
|
|
146
145
|
|
|
147
146
|
result
|
|
148
147
|
end
|
|
149
148
|
|
|
150
149
|
private
|
|
151
150
|
|
|
152
|
-
# Builds the final payload with time information if available
|
|
153
|
-
# @param payload [Hash] original payload
|
|
154
|
-
# @param time [Float, nil] execution time if block was given
|
|
155
|
-
# @return [Hash] final payload
|
|
156
|
-
def build_payload(payload, time)
|
|
157
|
-
return payload unless time
|
|
158
|
-
|
|
159
|
-
payload.empty? ? { time: time } : payload.merge(time: time)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
151
|
# Notifies all assigned listeners about the event
|
|
163
|
-
# @param
|
|
152
|
+
# @param method_name [Symbol] pre-resolved method name for object listeners
|
|
164
153
|
# @param event [Event] event with payload to broadcast to listeners
|
|
165
154
|
# @param assigned_listeners [Array] list of listeners to notify
|
|
166
|
-
def notify_listeners(
|
|
155
|
+
def notify_listeners(method_name, event, assigned_listeners)
|
|
167
156
|
assigned_listeners.each do |listener|
|
|
168
157
|
if listener.is_a?(Proc)
|
|
169
158
|
listener.call(event)
|
|
170
159
|
else
|
|
171
|
-
listener.send(
|
|
160
|
+
listener.send(method_name, event)
|
|
172
161
|
end
|
|
173
162
|
end
|
|
174
163
|
end
|