event_sourcing 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|