diff --git a/lib/sequent/core/helpers/message_handler.rb b/lib/sequent/core/helpers/message_handler.rb index 0708522b..8917d0f7 100644 --- a/lib/sequent/core/helpers/message_handler.rb +++ b/lib/sequent/core/helpers/message_handler.rb @@ -42,14 +42,24 @@ module Helpers # module MessageHandler module ClassMethods - def on(*args, **opts, &block) + def on(*args, **opts, &handler) OnArgumentsValidator.validate_arguments!(*args) message_matchers = args.map { |arg| MessageMatchers::ArgumentCoercer.coerce_argument(arg) } + unbound_method = begin + handler_method_name = :"__sequent_handler_#{handler.object_id}" + fail "duplicate method name #{handler_method_name}" if method_defined?(handler_method_name) + + define_method(handler_method_name, &handler) + instance_method(handler_method_name) + ensure + undef_method(handler_method_name) + end + message_router.register_matchers( *message_matchers, - block, + unbound_method, ) opts.each do |name, value| @@ -113,7 +123,9 @@ def dispatch_message(message, handlers) if Sequent.logger.debug? Sequent.logger.debug("[MessageHandler] Handler #{self.class} handling #{message.class}") end - instance_exec(message, &handler) + + args = handler.arity == 0 ? [] : [message] + handler.bind_call(self, *args) end end end diff --git a/spec/lib/sequent/core/helpers/message_handler_spec.rb b/spec/lib/sequent/core/helpers/message_handler_spec.rb index b4289f16..f75b6af4 100644 --- a/spec/lib/sequent/core/helpers/message_handler_spec.rb +++ b/spec/lib/sequent/core/helpers/message_handler_spec.rb @@ -153,11 +153,15 @@ class UnrelatedHandlerWithOption describe '.message_mapping' do subject { MyHandler.message_mapping } - it 'returns a mapping of message classes to handlers' do - expect(subject).to eq( - MessageHandlerEvent => Set[MyHandler::FIRST_HANDLER, MyHandler::LAST_HANDLER], - MessageHandlerEventOtherEvent => Set[MyHandler::FIRST_HANDLER], + it 'returns a mapping of message classes to unbound methods' do + expect(subject.keys).to eq( + [ + MessageHandlerEvent, + MessageHandlerEventOtherEvent, + ], ) + expect(subject[MessageHandlerEvent].size).to eq(2) + expect(subject[MessageHandlerEventOtherEvent].size).to eq(1) end context 'given a non-class/module argument' do @@ -211,4 +215,36 @@ class OtherHandler end end end + + describe 'handlers that use `return`' do + class HandlerWithReturn + include Sequent::Core::Helpers::MessageHandler + + attr_reader :first_block_called, :second_block_called, :last_block_called + + on MessageHandlerEvent do + @first_block_called = true + return + end + + on MessageHandlerEvent do |_event| + @second_block_called = true + next + end + + on MessageHandlerEvent do + @last_block_called = true + end + end + + let(:handler) { HandlerWithReturn.new } + + it 'should call subsequent handlers when a handler uses `return` or `next`' do + handler.handle_message(MessageHandlerEvent.new) + + expect(handler.first_block_called).to be_truthy + expect(handler.second_block_called).to be_truthy + expect(handler.last_block_called).to be_truthy + end + end end