diff --git a/CHANGELOG.md b/CHANGELOG.md index d4e2eaf..c01ee3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [0.5.2] - 2026-05-25 + +### Fixed + +- **Proc/block context in `emits`**: Payload builder blocks and `if:`/`unless:` condition procs now + execute bound to the service instance via `instance_exec`, matching the behaviour of Symbol method + references. Previously, `builder.call(result)` left `self` as the class, so instance variables set + during `#call` (e.g. `@my_flag`) were silently unreadable inside a block or lambda. Both forms now + behave identically — `self` is always the service instance. + ## [0.5.1] - 2026-05-25 ### Added diff --git a/gem/Gemfile.lock b/gem/Gemfile.lock index 2f77f16..d4bda84 100644 --- a/gem/Gemfile.lock +++ b/gem/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - servus (0.5.1) + servus (0.5.2) activesupport (>= 8.0) json-schema (~> 5) diff --git a/gem/lib/servus/events/emitter.rb b/gem/lib/servus/events/emitter.rb index 37945b5..304731d 100644 --- a/gem/lib/servus/events/emitter.rb +++ b/gem/lib/servus/events/emitter.rb @@ -189,7 +189,7 @@ def emission_condition_met?(emission, result) # @return [Object] truthy or falsy value # @api private def evaluate_emission_condition(condition, result) - condition.is_a?(Proc) ? condition.call(result) : send(condition, result) + condition.is_a?(Proc) ? instance_exec(result, &condition) : send(condition, result) end # Validates the payload against the Event class's schema registered for the event. @@ -216,8 +216,7 @@ def build_event_payload(emission, result) builder = emission[:payload_builder] if builder.is_a?(Proc) - # Block-based payload builder - builder.call(result) + instance_exec(result, &builder) elsif builder.is_a?(Symbol) # Method-based payload builder send(builder, result) diff --git a/gem/lib/servus/version.rb b/gem/lib/servus/version.rb index 2851845..b015837 100644 --- a/gem/lib/servus/version.rb +++ b/gem/lib/servus/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Servus - VERSION = '0.5.1' + VERSION = '0.5.2' end diff --git a/gem/spec/servus/base_events_spec.rb b/gem/spec/servus/base_events_spec.rb index caf3953..1ffab1f 100644 --- a/gem/spec/servus/base_events_spec.rb +++ b/gem/spec/servus/base_events_spec.rb @@ -408,6 +408,45 @@ def call .to emit_event(:conditional_event) .with(hash_including(amount: 150, label: 'large')) end + + context 'instance variable access in procs (regression: instance_exec binding)' do + it 'condition proc can read an instance variable set in initialize' do + service_class = stub_const('IvarConditionService', Class.new(Servus::Base) do + emits :ivar_event, on: :success, unless: ->(_result) { @suppress } + + def initialize(suppress: false) + @suppress = suppress + end + + def call + success({}) + end + end) + + expect { service_class.call(suppress: false) }.to emit_event(:ivar_event) + expect { service_class.call(suppress: true) }.not_to emit_event(:ivar_event) + end + + it 'payload builder block can read an instance variable set in initialize' do + service_class = stub_const('IvarPayloadService', Class.new(Servus::Base) do + emits :ivar_payload_event, on: :success do |_result| + { label: @label } + end + + def initialize(label:) + @label = label + end + + def call + success({}) + end + end) + + expect { service_class.call(label: 'vip') } + .to emit_event(:ivar_payload_event) + .with(hash_including(label: 'vip')) + end + end end describe 'automatic event emission' do