event_sourcing 0.0.2
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 +7 -0
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/Guardfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +13 -0
- data/event_sourcing.gemspec +28 -0
- data/features/steps/whole_stack.rb +29 -0
- data/features/support/env.rb +5 -0
- data/features/support/logger.log +1 -0
- data/features/support/sample_app.rb +40 -0
- data/features/support/spinach.log +0 -0
- data/features/whole_stack.feature +6 -0
- data/lib/event_sourcing.rb +3 -0
- data/lib/event_sourcing/aggregate.rb +30 -0
- data/lib/event_sourcing/aggregate/actor.rb +29 -0
- data/lib/event_sourcing/aggregate/manager.rb +33 -0
- data/lib/event_sourcing/aggregate/manager/cache.rb +17 -0
- data/lib/event_sourcing/aggregate/manager/instance_of.rb +11 -0
- data/lib/event_sourcing/aggregate/manager/reference.rb +14 -0
- data/lib/event_sourcing/aggregate/message.rb +7 -0
- data/lib/event_sourcing/aggregate/wrapper.rb +18 -0
- data/lib/event_sourcing/application.rb +37 -0
- data/lib/event_sourcing/application/actor.rb +40 -0
- data/lib/event_sourcing/application/actor/reference.rb +23 -0
- data/lib/event_sourcing/command.rb +30 -0
- data/lib/event_sourcing/command/bus.rb +16 -0
- data/lib/event_sourcing/event.rb +29 -0
- data/lib/event_sourcing/event/bus.rb +29 -0
- data/lib/event_sourcing/event/bus/reference.rb +28 -0
- data/lib/event_sourcing/event/publisher.rb +47 -0
- data/lib/event_sourcing/event/publisher/reference.rb +11 -0
- data/lib/event_sourcing/event/store.rb +8 -0
- data/lib/event_sourcing/event/store/memory.rb +43 -0
- data/lib/event_sourcing/event/stream.rb +23 -0
- data/lib/event_sourcing/event/subscriber.rb +16 -0
- data/lib/event_sourcing/version.rb +3 -0
- data/spec/concurrent_logging.rb +6 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/support/actor_helpers.rb +11 -0
- data/spec/support/shared_examples/a_store_implementation.rb +70 -0
- data/spec/unit/aggregate/actor_spec.rb +59 -0
- data/spec/unit/aggregate/manager/cache_spec.rb +26 -0
- data/spec/unit/aggregate/manager/reference_spec.rb +14 -0
- data/spec/unit/aggregate/manager_spec.rb +32 -0
- data/spec/unit/aggregate/wrapper_spec.rb +22 -0
- data/spec/unit/aggregate_spec.rb +75 -0
- data/spec/unit/application/actor/reference_spec.rb +25 -0
- data/spec/unit/application/actor_spec.rb +36 -0
- data/spec/unit/application_spec.rb +41 -0
- data/spec/unit/command/bus_spec.rb +15 -0
- data/spec/unit/command_spec.rb +38 -0
- data/spec/unit/event/bus/reference_spec.rb +48 -0
- data/spec/unit/event/bus_spec.rb +41 -0
- data/spec/unit/event/publisher_spec.rb +28 -0
- data/spec/unit/event/store/memory_spec.rb +6 -0
- data/spec/unit/event/stream_spec.rb +30 -0
- data/spec/unit/event/subscriber_spec.rb +27 -0
- data/spec/unit/event_spec.rb +27 -0
- data/spec/unit_helper.rb +14 -0
- metadata +233 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/aggregate/actor"
|
3
|
+
|
4
|
+
describe EventSourcing::Aggregate::Actor do
|
5
|
+
context "for()" do
|
6
|
+
let(:aggregate) { double("Aggregate class", new: true) }
|
7
|
+
|
8
|
+
subject do
|
9
|
+
EventSourcing::Aggregate::Actor.for(aggregate)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns an actor" do
|
13
|
+
expect(subject < Concurrent::Actor::RestartingContext).to be_truthy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "instance" do
|
18
|
+
subject { actor }
|
19
|
+
let(:aggregate_class) { double("Aggregate class", new: aggregate_instance, instance_methods: [:publish])}
|
20
|
+
let(:aggregate_instance) { double("Aggregate instance", publish: :published)}
|
21
|
+
let(:event_stream) { instance_double("EventSourcing::Event::Stream") }
|
22
|
+
let(:event_bus) { instance_double("EventSourcing::Event::Bus::Reference") }
|
23
|
+
let(:actor) { EventSourcing::Aggregate::Actor.for(aggregate_class).new(event_bus, event_stream) }
|
24
|
+
|
25
|
+
before do
|
26
|
+
allow(aggregate_instance).to receive(:_apply).with(:published)
|
27
|
+
allow(event_bus).to receive(:publish)
|
28
|
+
allow(event_stream).to receive(:append)
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when receiving supported messages" do
|
32
|
+
after do
|
33
|
+
subject.on_message([:publish, :some_arg])
|
34
|
+
end
|
35
|
+
|
36
|
+
it "stores the events using the stream" do
|
37
|
+
expect(event_stream).to receive(:append).with(:published)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "delegates on aggregate and publishes events" do
|
41
|
+
expect(event_bus).to receive(:publish).with(:published)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "gets events applied" do
|
45
|
+
expect(aggregate_instance).to receive(:_apply).with(:published)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when receiving unsupported messages" do
|
50
|
+
after do
|
51
|
+
subject.on_message([:fake])
|
52
|
+
end
|
53
|
+
|
54
|
+
it "does nothing" do
|
55
|
+
expect(event_bus).not_to receive(:publish)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/aggregate/manager/cache"
|
3
|
+
|
4
|
+
describe EventSourcing::Aggregate::Manager::Cache do
|
5
|
+
subject { EventSourcing::Aggregate::Manager::Cache.new(event_bus) }
|
6
|
+
|
7
|
+
let(:aggregate) { Class.new }
|
8
|
+
let(:actor) { double("Actor")}
|
9
|
+
let(:instance) { double("Aggregate instance") }
|
10
|
+
let(:event_bus) { instance_double("EventSourcing::Event::Bus::Reference") }
|
11
|
+
let(:event_stream) { double("Event stream") }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(event_bus).to receive(:get_stream).with("some-id").and_return(event_stream)
|
15
|
+
aggregate.const_set("Actor", actor)
|
16
|
+
allow(actor).to receive(:spawn!).once.with(name: "some-id", args: [event_bus, event_stream], supervise: true).and_return(instance)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns instances" do
|
20
|
+
expect(subject.instance_of(aggregate, "some-id")).to eq(instance)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "memoizes instances" do
|
24
|
+
expect(subject.instance_of(aggregate, "some-id")).to eq(subject.instance_of(aggregate, "some-id"))
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/aggregate/manager/reference"
|
3
|
+
|
4
|
+
describe EventSourcing::Aggregate::Manager::Reference do
|
5
|
+
context "instance_of" do
|
6
|
+
let(:aggregate) { double("Aggregate") }
|
7
|
+
let(:wrapper) { EventSourcing::Aggregate::Wrapper.new(subject, aggregate, "some-id") }
|
8
|
+
subject { EventSourcing::Aggregate::Manager::Reference.new(instance_double("Concurrent::Actor::Core", is_a?: true)) }
|
9
|
+
|
10
|
+
it "returns a wrapper" do
|
11
|
+
expect(subject.instance_of(aggregate, "some-id")).to eq(wrapper)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/aggregate/manager"
|
3
|
+
|
4
|
+
describe EventSourcing::Aggregate::Manager do
|
5
|
+
it "is a restarting actor" do
|
6
|
+
expect(EventSourcing::Aggregate::Manager.ancestors).to include(Concurrent::Actor::RestartingContext)
|
7
|
+
end
|
8
|
+
|
9
|
+
context "on message of kind Aggregate::Message" do
|
10
|
+
let(:aggregate) { double("Aggregate") }
|
11
|
+
let(:instance) { double("Aggregate instance") }
|
12
|
+
let(:cache_class) { class_double("EventSourcing::Aggregate::Manager::Cache").as_stubbed_const }
|
13
|
+
let(:cache_instance) { instance_double("EventSourcing::Aggregate::Manager::Cache") }
|
14
|
+
let(:manager) { EventSourcing::Aggregate::Manager.new(event_bus) }
|
15
|
+
let(:event_bus) { double("EventSourcing::Event::Bus::Reference") }
|
16
|
+
let(:wrapped_message) { EventSourcing::Aggregate::Message.new(aggregate, "some-id", actual_message) }
|
17
|
+
let(:actual_message) { :publish }
|
18
|
+
|
19
|
+
before do
|
20
|
+
allow(cache_class).to receive(:new).with(event_bus).once.and_return(cache_instance)
|
21
|
+
allow(cache_instance).to receive(:instance_of).with(aggregate, "some-id").and_return(instance)
|
22
|
+
end
|
23
|
+
|
24
|
+
after do
|
25
|
+
manager.on_message(wrapped_message)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "redirects the message to the aggregate actor" do
|
29
|
+
expect(instance).to receive(:tell).with(actual_message)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/aggregate/wrapper"
|
3
|
+
|
4
|
+
describe EventSourcing::Aggregate::Wrapper do
|
5
|
+
let(:manager) { instance_double("EventSourcing::Aggregate::Manager::Reference") }
|
6
|
+
let(:wrapper) { EventSourcing::Aggregate::Wrapper.new(manager, aggregate, "some-id") }
|
7
|
+
let(:aggregate) do
|
8
|
+
Class.new do
|
9
|
+
def publish
|
10
|
+
:stuff
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when is sent a message using tell" do
|
16
|
+
after { wrapper.publish }
|
17
|
+
|
18
|
+
it "routes it through the manager" do
|
19
|
+
expect(manager).to receive(:tell).with(EventSourcing::Aggregate::Message.new(aggregate, "some-id", [:publish]))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/aggregate"
|
3
|
+
|
4
|
+
describe EventSourcing::Aggregate do
|
5
|
+
context "once included" do
|
6
|
+
|
7
|
+
let(:actor_class) { class_double("EventSourcing::Aggregate::Actor") }
|
8
|
+
let(:aggregate_class) { Class.new }
|
9
|
+
let(:aggregate_actor) { double("Aggregate actor") }
|
10
|
+
|
11
|
+
before do
|
12
|
+
stub_const("EventSourcing::Aggregate::Actor", actor_class)
|
13
|
+
allow(actor_class).to receive(:for).with(aggregate_class).and_return(aggregate_actor)
|
14
|
+
end
|
15
|
+
|
16
|
+
subject do
|
17
|
+
aggregate_class.class_eval do
|
18
|
+
include EventSourcing::Aggregate
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "defines an actor" do
|
23
|
+
expect(subject::Actor).to eq(aggregate_actor)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when it has defined handlers" do
|
28
|
+
|
29
|
+
|
30
|
+
let(:event) { instance_double("EventSourcing::Event", to_s: "SomethingHappened") }
|
31
|
+
|
32
|
+
let(:aggregate_class) do
|
33
|
+
Class.new do
|
34
|
+
include EventSourcing::Aggregate
|
35
|
+
|
36
|
+
def do_something
|
37
|
+
:did_something unless @something_happened
|
38
|
+
end
|
39
|
+
|
40
|
+
handle "SomethingHappened" do |e|
|
41
|
+
@something_happened = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:clean_aggregate) { aggregate_class.new() }
|
47
|
+
let(:aggregate_with_events) { aggregate_class.new([event]) }
|
48
|
+
|
49
|
+
it "can be initialized without an event stream" do
|
50
|
+
expect(clean_aggregate.do_something).to eq(:did_something)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "can be initialized with an event stream" do
|
54
|
+
expect(aggregate_with_events.do_something).to be_nil
|
55
|
+
end
|
56
|
+
|
57
|
+
it "can have events applied to it" do
|
58
|
+
clean_aggregate._apply(event)
|
59
|
+
expect(clean_aggregate.do_something).to be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
context "with unsupported events" do
|
64
|
+
let(:unsupported_event) { instance_double("EventSourcing::Event", to_s: "WrongEvent") }
|
65
|
+
|
66
|
+
it "fails when initialized with unsupported events" do
|
67
|
+
expect { aggregate_class.new([unsupported_event])}.to raise_error(RuntimeError, "unsupported event WrongEvent")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "fails when unsupported events are applied" do
|
71
|
+
expect { aggregate_class.new._apply(unsupported_event) }.to raise_error(RuntimeError, "unsupported event WrongEvent")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/application/actor/reference"
|
3
|
+
|
4
|
+
describe EventSourcing::Application::Actor::Reference do
|
5
|
+
subject { EventSourcing::Application::Actor::Reference.new(instance_double("Concurrent::Actor::Core", is_a?: true)) }
|
6
|
+
context "terminate!" do
|
7
|
+
after { subject.terminate! }
|
8
|
+
|
9
|
+
it "tells the actor to terminate" do
|
10
|
+
expect(subject).to receive(:tell).with(:terminate!)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "execute_command" do
|
15
|
+
let(:command) { double("Command") }
|
16
|
+
let(:command_bus) { double("Command bus") }
|
17
|
+
|
18
|
+
after { subject.execute_command(command) }
|
19
|
+
before { allow(subject).to receive(:ask!).with(:get_command_bus).once.and_return(command_bus) }
|
20
|
+
|
21
|
+
it "delegates on the command bus" do
|
22
|
+
expect(command_bus).to receive(:tell).with(command)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/application/actor"
|
3
|
+
|
4
|
+
describe EventSourcing::Application::Actor do
|
5
|
+
subject { EventSourcing::Application::Actor.new(event_store) }
|
6
|
+
|
7
|
+
let(:event_store) { instance_double("EventSourcing::Event::Store::Memory") }
|
8
|
+
let(:command_bus) { class_double("EventSourcing::Command::Bus").as_stubbed_const(transfer_nested_constants: true) }
|
9
|
+
let(:command_bus_ref) { double("EventSourcing::Command::Bus::Reference") }
|
10
|
+
let(:event_bus) { class_double("EventSourcing::Event::Bus").as_stubbed_const(transfer_nested_constants: true) }
|
11
|
+
let(:event_bus_ref) { double("EventSourcing::Event::Bus::Reference") }
|
12
|
+
let(:aggregate_manager) { class_double("EventSourcing::Aggregate::Manager").as_stubbed_const(transfer_nested_constants: true) }
|
13
|
+
let(:aggregate_manager_ref) { instance_double("EventSourcing::Aggregate::Manager::Reference") }
|
14
|
+
|
15
|
+
before do
|
16
|
+
allow(command_bus).to receive(:spawn!).with(name: :command_bus, supervise: true, args: [aggregate_manager_ref]).and_return(command_bus_ref)
|
17
|
+
allow(event_bus).to receive(:spawn!).with(name: :event_bus, supervise: true, args: [event_store]).and_return(event_bus_ref)
|
18
|
+
allow(aggregate_manager).to receive(:spawn!).with(name: :aggregate_manager, supervise: true, args: [event_bus_ref]).and_return(aggregate_manager_ref)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "uses Actor::Reference internally" do
|
22
|
+
expect(EventSourcing::Application::Actor.new(event_store).default_reference_class).to eq(EventSourcing::Application::Actor::Reference)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "supervises and returns a command bus" do
|
26
|
+
expect(subject.on_message(:get_command_bus)).to eq(command_bus_ref)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "supervises and returns an event bus" do
|
30
|
+
expect(subject.on_message(:get_event_bus)).to eq(event_bus_ref)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "supervises and returns an event bus" do
|
34
|
+
expect(subject.on_message(:get_aggregate_manager)).to eq(aggregate_manager_ref)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/application"
|
3
|
+
|
4
|
+
describe EventSourcing::Application do
|
5
|
+
let(:sample_app) { EventSourcing::Application.new(:sample_app) }
|
6
|
+
|
7
|
+
it "can be inspected" do
|
8
|
+
expect(sample_app.inspect).to eq("EventSourcing::Application(sample_app)")
|
9
|
+
end
|
10
|
+
|
11
|
+
context "while running" do
|
12
|
+
subject { running_app }
|
13
|
+
|
14
|
+
let(:running_app) { sample_app.run!(event_store: event_store) }
|
15
|
+
let(:event_store) { instance_double("EventSourcing::Event::Store::Memory") }
|
16
|
+
#FIXME Remove to_str. It's only needed for specs to pass under jruby
|
17
|
+
let(:actor_class) { class_double("EventSourcing::Application::Actor", to_str: "EventSourcing::Application::Actor").as_stubbed_const(transfer_nested_constants: true) }
|
18
|
+
let(:actor_reference) { instance_double("EventSourcing::Application::Actor::Reference") }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(actor_class).to receive(:spawn!).with(name: :sample_app, args: [event_store]).and_return(actor_reference)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "shutdown" do
|
25
|
+
after { subject.shutdown }
|
26
|
+
|
27
|
+
it "terminates the application actor" do
|
28
|
+
expect(actor_reference).to receive(:terminate!)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "execute command" do
|
33
|
+
let(:command) { double("Command") }
|
34
|
+
after { subject.execute_command(command)}
|
35
|
+
|
36
|
+
it "delegates on the application actor" do
|
37
|
+
expect(actor_reference).to receive(:execute_command).with(command)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/command/bus"
|
3
|
+
|
4
|
+
describe EventSourcing::Command::Bus do
|
5
|
+
let(:aggregate_manager) { instance_double("EventSourcing::Aggregate::Manager::Reference") }
|
6
|
+
let(:command) { double("EventSourcing::Command") }
|
7
|
+
|
8
|
+
subject { EventSourcing::Command::Bus.new(aggregate_manager) }
|
9
|
+
|
10
|
+
after { subject.on_message(command) }
|
11
|
+
|
12
|
+
it "executes commands" do
|
13
|
+
expect(command).to receive(:execute).with(aggregate_manager)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/command"
|
3
|
+
|
4
|
+
describe EventSourcing::Command do
|
5
|
+
it "expects a block" do
|
6
|
+
expect { EventSourcing::Command.define }.to raise_error("Commands require an execution block")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can't be directly instantiated" do
|
10
|
+
expect { EventSourcing::Command.new }.to raise_error(NoMethodError)
|
11
|
+
end
|
12
|
+
|
13
|
+
context "instance" do
|
14
|
+
subject { EventSourcing::Command.define {} }
|
15
|
+
|
16
|
+
it "is a EventSourcing::Command" do
|
17
|
+
expect(subject.new).to be_a(EventSourcing::Command)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with required parameters" do
|
22
|
+
subject { EventSourcing::Command.define(:title) {} }
|
23
|
+
|
24
|
+
it "can't be instantiated if those are not provided" do
|
25
|
+
expect { subject.new }.to raise_error(ArgumentError, "missing keyword: title")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "execute" do
|
30
|
+
let(:dependency) { double("Dependency", value: 42)}
|
31
|
+
let(:command) { EventSourcing::Command.define(:prop) { |dep| "returning #{dep.value} and #{prop}"} }
|
32
|
+
subject { command.new(prop: "own_stuff") }
|
33
|
+
|
34
|
+
it "runs the passed block under the current command context" do
|
35
|
+
expect(subject.execute(dependency)).to eq("returning 42 and own_stuff")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "unit_helper"
|
2
|
+
require "event_sourcing/event/bus/reference"
|
3
|
+
|
4
|
+
describe EventSourcing::Event::Bus::Reference do
|
5
|
+
let(:bus_ref) { actor_reference(EventSourcing::Event::Bus::Reference) }
|
6
|
+
let(:stream) { instance_double("EventSourcing::Event::Stream") }
|
7
|
+
|
8
|
+
context "get_stream" do
|
9
|
+
let(:store) { instance_double("EventSourcing::Event::Store::Memory") }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(bus_ref).to receive(:ask!).once.with(:get_event_store).and_return(store)
|
13
|
+
allow(store).to receive(:get_stream).with("some-id").and_return(stream)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns a stream from the event store" do
|
17
|
+
expect(bus_ref.get_stream("some-id")).to eq(stream)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "memoizes the store" do
|
21
|
+
expect(bus_ref.get_stream("some-id")).to eq(bus_ref.get_stream("some-id"))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "publish" do
|
26
|
+
after { bus_ref.publish(event) }
|
27
|
+
|
28
|
+
before do
|
29
|
+
allow(bus_ref).to receive(:ask!).with(:get_event_publisher).and_return(publisher)
|
30
|
+
end
|
31
|
+
|
32
|
+
let(:event) { instance_double("EventSourcing::Event") }
|
33
|
+
let(:publisher) { double("Publisher ref") }
|
34
|
+
|
35
|
+
it "delegates on the memoized bus publisher" do
|
36
|
+
expect(publisher).to receive(:publish).with(event)
|
37
|
+
end
|
38
|
+
|
39
|
+
context "multiple times" do
|
40
|
+
after { bus_ref.publish(:event) }
|
41
|
+
before { allow(publisher).to receive(:publish) }
|
42
|
+
|
43
|
+
it "memoizes the publisher reference" do
|
44
|
+
expect(bus_ref).to receive(:ask!).once.with(:get_event_publisher).and_return(publisher)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|