Skip to content

Commit

Permalink
Guaranteed synchronous reactor order execution (#64)
Browse files Browse the repository at this point in the history
#### Why <!-- A short description of why this change is required -->
Reactors were stored in a `Set` upon registration. Although ruby `Set`s
preserve order, this is not guaranteed in the future and depends on the
internal implementation of a `Set`. If we want to preserve order we
should be using an array.

Preserving order lets us guarantee the order or execution of synchronous
reactors (the order in which they were registered).


#### What changed <!-- Summary of changes when modifying hundreds of
lines -->
* Use an array to store reactors
* Update shared examples to accept either a single reactor or multiple
reactors
  • Loading branch information
darrunategui authored May 6, 2024
1 parent b4be9ff commit efb4dce
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.2
3.2.3
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## 1.4.3 - 2024-05-06
### Changed
- The order of execution for synchronous reactors is now guaranteed to be the order in which they were registered.
- The shared examples for reactors `'an event which (a)synchronously dispatches'` now accept 1 or multiple arguments. The synchronous version checks that reactors are given in the order in which they are registered.

## 1.4.2 - 2024-04-30
### Changed
- Raise error on missing consumer config
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
eventsimple (1.4.2)
eventsimple (1.4.3)
dry-struct (~> 1.6)
dry-types (~> 1.7)
pg (~> 1.4)
Expand Down
9 changes: 5 additions & 4 deletions lib/eventsimple/event_dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def register(events:, sync:, async:)
end

# Return a ReactorSet containing all Reactors matching an Event
# Reactors will be in the order in which they were registered
def for(event)
reactors = ReactorSet.new

Expand All @@ -77,16 +78,16 @@ class ReactorSet
attr_reader :sync, :async

def initialize
@sync = Set.new
@async = Set.new
@sync = []
@async = []
end

def add_sync(reactors)
@sync += reactors
@sync |= reactors
end

def add_async(reactors)
@async += reactors
@async |= reactors
end
end
end
Expand Down
10 changes: 6 additions & 4 deletions lib/eventsimple/support/spec_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
# frozen_string_literal: true

RSpec.shared_examples 'an event which synchronously dispatches' do |dispatcher_klass|
RSpec.shared_examples 'an event which synchronously dispatches' do |*dispatcher_klasses|
specify do
reactors = Eventsimple::EventDispatcher.rules.for(described_class.new)

expect(reactors.sync).to include(dispatcher_klass)
# Order is important here since the synchronous reactors are executed sequentially
expect(reactors.sync & dispatcher_klasses).to eq(dispatcher_klasses)
end
end

RSpec.shared_examples 'an event which asynchronously dispatches' do |dispatcher_klass|
RSpec.shared_examples 'an event which asynchronously dispatches' do |*dispatcher_klasses|
specify do
reactors = Eventsimple::EventDispatcher.rules.for(described_class.new)

expect(reactors.async).to include(dispatcher_klass)
# Order is _not_ important here since async reactors have no order guarantee
expect(reactors.async).to include(*dispatcher_klasses)
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/eventsimple/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Eventsimple
VERSION = '1.4.2'
VERSION = '1.4.3'
end
9 changes: 8 additions & 1 deletion spec/dummy/app/components/user_component/dispatcher.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
module UserComponent
class Dispatcher < Eventsimple::Dispatcher
on Events::Created, sync: Reactors::Created::SyncReactor
on(
Events::Created,
sync: [
Reactors::Created::SyncReactor,
Reactors::Created::SyncReactor2,
],
async: Reactors::Created::AsyncReactor2,
)
on Events::RescuedInvalidTransition, sync: Reactors::Created::SyncReactor
on Events::RescuedInvalidTransitionWithReraise, sync: Reactors::Created::SyncReactor

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module UserComponent
module Reactors
module Created
class AsyncReactor2 < Eventsimple::Reactor
def call(event)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module UserComponent
module Reactors
module Created
class SyncReactor2 < Eventsimple::Reactor
def call(event)
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
it_behaves_like 'an event which synchronously dispatches',
UserComponent::Reactors::Created::SyncReactor

it_behaves_like 'an event which synchronously dispatches',
UserComponent::Reactors::Created::SyncReactor,
UserComponent::Reactors::Created::SyncReactor2

it 'updates the user properties' do
create_event

Expand Down
20 changes: 20 additions & 0 deletions spec/lib/eventsimple/event_dispatcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,25 @@ module Eventsimple
).with(event).at(:no_wait)
end
end

describe '.rules' do
describe '#for' do
it 'returns the sync reactors in the order in which they were registered' do
expected_reactors = [
UserComponent::Reactors::Created::SyncReactor,
UserComponent::Reactors::Created::SyncReactor2,
]
expect(described_class.rules.for(UserComponent::Events::Created.new).sync).to eq expected_reactors
end

it 'returns the async reactors in the order in which they were registered' do
expected_reactors = [
UserComponent::Reactors::Created::AsyncReactor2,
UserComponent::Reactors::Created::AsyncReactor,
]
expect(described_class.rules.for(UserComponent::Events::Created.new).async).to eq expected_reactors
end
end
end
end
end

0 comments on commit efb4dce

Please sign in to comment.