aggro 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -0
- data/.travis.yml +15 -0
- data/Gemfile +9 -0
- data/README.md +5 -1
- data/Rakefile +10 -0
- data/aggro.gemspec +8 -1
- data/lib/aggro.rb +191 -7
- data/lib/aggro/abstract_store.rb +12 -0
- data/lib/aggro/aggregate.rb +98 -0
- data/lib/aggro/aggregate_ref.rb +68 -6
- data/lib/aggro/attribute_dsl.rb +96 -0
- data/lib/aggro/binding_dsl.rb +45 -0
- data/lib/aggro/block_helper.rb +14 -0
- data/lib/aggro/channel.rb +37 -0
- data/lib/aggro/client.rb +12 -0
- data/lib/aggro/cluster_config.rb +57 -0
- data/lib/aggro/command.rb +16 -0
- data/lib/aggro/concurrent_actor.rb +26 -0
- data/lib/aggro/event_bus.rb +94 -0
- data/lib/aggro/event_dsl.rb +53 -0
- data/lib/aggro/event_proxy.rb +23 -0
- data/lib/aggro/event_serializer.rb +14 -0
- data/lib/aggro/file_store.rb +97 -0
- data/lib/aggro/file_store/reader.rb +21 -0
- data/lib/aggro/file_store/writer.rb +27 -0
- data/lib/aggro/handler/command.rb +60 -0
- data/lib/aggro/handler/create_aggregate.rb +42 -0
- data/lib/aggro/handler/get_events.rb +30 -0
- data/lib/aggro/handler/query.rb +60 -0
- data/lib/aggro/handler/start_saga.rb +56 -0
- data/lib/aggro/local_node.rb +28 -0
- data/lib/aggro/locator.rb +32 -0
- data/lib/aggro/message/ask.rb +16 -0
- data/lib/aggro/message/command.rb +36 -0
- data/lib/aggro/message/create_aggregate.rb +16 -0
- data/lib/aggro/message/endpoint.rb +16 -0
- data/lib/aggro/message/events.rb +24 -0
- data/lib/aggro/message/get_events.rb +16 -0
- data/lib/aggro/message/heartbeat.rb +16 -0
- data/lib/aggro/message/invalid_target.rb +20 -0
- data/lib/aggro/message/ok.rb +20 -0
- data/lib/aggro/message/publisher_endpoint_inquiry.rb +16 -0
- data/lib/aggro/message/query.rb +36 -0
- data/lib/aggro/message/result.rb +16 -0
- data/lib/aggro/message/start_saga.rb +28 -0
- data/lib/aggro/message/unhandled_operation.rb +20 -0
- data/lib/aggro/message/unknown_operation.rb +20 -0
- data/lib/aggro/message_parser.rb +10 -0
- data/lib/aggro/message_router.rb +26 -0
- data/lib/aggro/nanomsg_transport.rb +31 -0
- data/lib/aggro/nanomsg_transport/client.rb +35 -0
- data/lib/aggro/nanomsg_transport/connection.rb +98 -0
- data/lib/aggro/nanomsg_transport/publish.rb +17 -0
- data/lib/aggro/nanomsg_transport/publisher.rb +37 -0
- data/lib/aggro/nanomsg_transport/raw_reply.rb +18 -0
- data/lib/aggro/nanomsg_transport/raw_request.rb +18 -0
- data/lib/aggro/nanomsg_transport/reply.rb +17 -0
- data/lib/aggro/nanomsg_transport/request.rb +17 -0
- data/lib/aggro/nanomsg_transport/server.rb +84 -0
- data/lib/aggro/nanomsg_transport/socket_error.rb +20 -0
- data/lib/aggro/nanomsg_transport/subscribe.rb +27 -0
- data/lib/aggro/nanomsg_transport/subscriber.rb +82 -0
- data/lib/aggro/node.rb +29 -0
- data/lib/aggro/node_list.rb +39 -0
- data/lib/aggro/projection.rb +13 -0
- data/lib/aggro/query.rb +11 -0
- data/lib/aggro/saga.rb +94 -0
- data/lib/aggro/saga_runner.rb +87 -0
- data/lib/aggro/saga_runner/start_saga.rb +12 -0
- data/lib/aggro/saga_status.rb +29 -0
- data/lib/aggro/server.rb +88 -0
- data/lib/aggro/subscriber.rb +48 -0
- data/lib/aggro/subscription.rb +41 -0
- data/lib/aggro/transform/boolean.rb +16 -0
- data/lib/aggro/transform/email.rb +26 -0
- data/lib/aggro/transform/id.rb +34 -0
- data/lib/aggro/transform/integer.rb +22 -0
- data/lib/aggro/transform/money.rb +22 -0
- data/lib/aggro/transform/noop.rb +16 -0
- data/lib/aggro/transform/string.rb +16 -0
- data/lib/aggro/transform/time_interval.rb +24 -0
- data/lib/aggro/version.rb +1 -1
- data/spec/lib/aggro/abstract_store_spec.rb +15 -0
- data/spec/lib/aggro/aggregate_ref_spec.rb +63 -12
- data/spec/lib/aggro/aggregate_spec.rb +207 -0
- data/spec/lib/aggro/channel_spec.rb +87 -0
- data/spec/lib/aggro/client_spec.rb +26 -0
- data/spec/lib/aggro/cluster_config_spec.rb +33 -0
- data/spec/lib/aggro/command_spec.rb +52 -0
- data/spec/lib/aggro/concurrent_actor_spec.rb +44 -0
- data/spec/lib/aggro/event_bus_spec.rb +20 -0
- data/spec/lib/aggro/event_serializer_spec.rb +28 -0
- data/spec/lib/aggro/file_store/reader_spec.rb +32 -0
- data/spec/lib/aggro/file_store/writer_spec.rb +67 -0
- data/spec/lib/aggro/file_store_spec.rb +51 -0
- data/spec/lib/aggro/handler/command_spec.rb +78 -0
- data/spec/lib/aggro/handler/create_aggregate_spec.rb +64 -0
- data/spec/lib/aggro/handler/get_events_handler_spec.rb +45 -0
- data/spec/lib/aggro/handler/query_spec.rb +78 -0
- data/spec/lib/aggro/handler/start_saga_spec.rb +64 -0
- data/spec/lib/aggro/local_node_spec.rb +52 -0
- data/spec/lib/aggro/locator_spec.rb +61 -0
- data/spec/lib/aggro/message/ask_spec.rb +23 -0
- data/spec/lib/aggro/message/command_spec.rb +50 -0
- data/spec/lib/aggro/message/create_aggregate_spec.rb +28 -0
- data/spec/lib/aggro/message/endpoint_spec.rb +23 -0
- data/spec/lib/aggro/message/events_spec.rb +37 -0
- data/spec/lib/aggro/message/get_events_spec.rb +33 -0
- data/spec/lib/aggro/message/heartbeat_spec.rb +23 -0
- data/spec/lib/aggro/message/invalid_target_spec.rb +28 -0
- data/spec/lib/aggro/message/ok_spec.rb +27 -0
- data/spec/lib/aggro/message/publisher_endpoint_inquiry_spec.rb +23 -0
- data/spec/lib/aggro/message/query_spec.rb +50 -0
- data/spec/lib/aggro/message/start_saga_spec.rb +37 -0
- data/spec/lib/aggro/message/unhandled_operation_spec.rb +28 -0
- data/spec/lib/aggro/message/unknown_operation_spec.rb +28 -0
- data/spec/lib/aggro/message_parser_spec.rb +16 -0
- data/spec/lib/aggro/message_router_spec.rb +35 -0
- data/spec/lib/aggro/nanomsg_transport/socket_error_spec.rb +21 -0
- data/spec/lib/aggro/nanomsg_transport_spec.rb +37 -0
- data/spec/lib/aggro/node_list_spec.rb +38 -0
- data/spec/lib/aggro/node_spec.rb +44 -0
- data/spec/lib/aggro/projection_spec.rb +22 -0
- data/spec/lib/aggro/query_spec.rb +47 -0
- data/spec/lib/aggro/saga_runner_spec.rb +84 -0
- data/spec/lib/aggro/saga_spec.rb +126 -0
- data/spec/lib/aggro/saga_status_spec.rb +56 -0
- data/spec/lib/aggro/server_spec.rb +118 -0
- data/spec/lib/aggro/subscriber_spec.rb +59 -0
- data/spec/lib/aggro/subscription_spec.rb +50 -0
- data/spec/lib/aggro/transform/boolean_spec.rb +23 -0
- data/spec/lib/aggro/transform/email_spec.rb +13 -0
- data/spec/lib/aggro/transform/id_spec.rb +70 -0
- data/spec/lib/aggro/transform/integer_spec.rb +30 -0
- data/spec/lib/aggro/transform/money_spec.rb +34 -0
- data/spec/lib/aggro/transform/string_spec.rb +15 -0
- data/spec/lib/aggro/transform/time_interval_spec.rb +29 -0
- data/spec/lib/aggro_spec.rb +63 -19
- data/spec/spec_helper.rb +21 -2
- metadata +283 -3
@@ -0,0 +1,41 @@
|
|
1
|
+
module Aggro
|
2
|
+
# Private: Handles invoking events on a subscriber object.
|
3
|
+
class Subscription
|
4
|
+
def initialize(topic, subscriber, namespace, filters, at_version)
|
5
|
+
@topic = topic
|
6
|
+
@subscriber = subscriber
|
7
|
+
@namespace = namespace
|
8
|
+
@filters = filters
|
9
|
+
@at_version = at_version
|
10
|
+
@canceled = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def cancel
|
14
|
+
Aggro.event_bus.unsubscribe @topic, self unless @canceled
|
15
|
+
@canceled = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_event(event)
|
19
|
+
return if @canceled
|
20
|
+
|
21
|
+
invoke(event) if handles_event?(event) && matches_filter?(event)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def handles_event?(event)
|
27
|
+
@subscriber.handles_event? event.name, @namespace
|
28
|
+
end
|
29
|
+
|
30
|
+
def invoke(event)
|
31
|
+
Invokr.invoke method: "#{@namespace}_#{event.name}",
|
32
|
+
using: event.details, on: @subscriber
|
33
|
+
end
|
34
|
+
|
35
|
+
def matches_filter?(event)
|
36
|
+
@filters.all? do |filter_key, filter_value|
|
37
|
+
event.details[filter_key] == filter_value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms boolean representations.
|
4
|
+
module Boolean
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def deserialize(value)
|
8
|
+
value if value == false || value == true
|
9
|
+
end
|
10
|
+
|
11
|
+
def serialize(value)
|
12
|
+
value if value == false || value == true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms integer representations.
|
4
|
+
module Email
|
5
|
+
EMAIL_REGEX = %r{
|
6
|
+
\A([-a-z0-9!\#$%&'*+/=?^_`{|}~]+\.)*
|
7
|
+
[-a-z0-9!\#$%&'*+/=?^_`{|}~]+
|
8
|
+
@
|
9
|
+
((?:[-a-z0-9]+\.)+
|
10
|
+
[a-z]{2,})\Z
|
11
|
+
}xi
|
12
|
+
|
13
|
+
module_function
|
14
|
+
|
15
|
+
def deserialize(value)
|
16
|
+
value = value.to_s
|
17
|
+
|
18
|
+
value if value.match(EMAIL_REGEX)
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize(value)
|
22
|
+
deserialize value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms integer representations.
|
4
|
+
class ID
|
5
|
+
ID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
|
6
|
+
|
7
|
+
def initialize(generate: false)
|
8
|
+
@generate = generate
|
9
|
+
end
|
10
|
+
|
11
|
+
def deserialize(value)
|
12
|
+
value = value.to_s
|
13
|
+
|
14
|
+
return value if value.match(ID_REGEX)
|
15
|
+
|
16
|
+
generate_id if should_generate_id?
|
17
|
+
end
|
18
|
+
|
19
|
+
def serialize(value)
|
20
|
+
deserialize value
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def generate_id
|
26
|
+
SecureRandom.uuid
|
27
|
+
end
|
28
|
+
|
29
|
+
def should_generate_id?
|
30
|
+
@generate == true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms integer representations.
|
4
|
+
module Integer
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def deserialize(value)
|
8
|
+
if value.is_a?(::String)
|
9
|
+
string = value.gsub(/[^\d\.]/, '')
|
10
|
+
|
11
|
+
string == '' ? nil : string.to_i
|
12
|
+
else
|
13
|
+
value.to_i
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize(value)
|
18
|
+
value.to_i
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms money representations.
|
4
|
+
module Money
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def deserialize(value)
|
8
|
+
if value.is_a? ::String
|
9
|
+
Monetize.parse(value)
|
10
|
+
elsif value.is_a? ::Integer
|
11
|
+
Monetize.parse(value.to_s)
|
12
|
+
elsif value.is_a? ::Money
|
13
|
+
value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize(value)
|
18
|
+
value.format with_currency: true if value.is_a? ::Money
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Aggro
|
2
|
+
module Transform
|
3
|
+
# Private: Transforms money representations.
|
4
|
+
module TimeInterval
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def deserialize(value)
|
8
|
+
if value.is_a? ::String
|
9
|
+
::TimeInterval.parse(value)
|
10
|
+
elsif interval? value
|
11
|
+
value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def interval?(value)
|
16
|
+
value.class.parents.include? ::TimeInterval
|
17
|
+
end
|
18
|
+
|
19
|
+
def serialize(value)
|
20
|
+
value.iso8601 if interval? value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/aggro/version.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe AbstractStore do
|
2
|
+
subject(:store) { AbstractStore.new }
|
3
|
+
|
4
|
+
describe '#read' do
|
5
|
+
it 'should raise not implemented error' do
|
6
|
+
expect { store.read(double) }.to raise_error NotImplementedError
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#write' do
|
11
|
+
it 'should raise not implemented error' do
|
12
|
+
expect { store.write(double) }.to raise_error NotImplementedError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,23 +1,74 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
describe AggregateRef do
|
4
|
-
subject(:ref) { AggregateRef.new(id) }
|
1
|
+
RSpec.describe AggregateRef do
|
2
|
+
subject(:ref) { AggregateRef.new(id, type) }
|
5
3
|
|
6
4
|
let(:id) { SecureRandom.uuid }
|
7
|
-
let(:
|
5
|
+
let(:type) { 'type' }
|
6
|
+
|
7
|
+
let(:response) { Message::OK.new }
|
8
|
+
let(:client) { spy post: response }
|
9
|
+
let(:node) { double(client: client) }
|
10
|
+
let(:fake_locator) { double(primary_node: node) }
|
8
11
|
|
9
12
|
before do
|
10
|
-
allow(
|
13
|
+
allow(ref).to receive(:locator).and_return(fake_locator)
|
11
14
|
end
|
12
15
|
|
13
|
-
describe '#
|
14
|
-
it 'should
|
15
|
-
|
16
|
+
describe '#command' do
|
17
|
+
it 'should send the command to the aggregate via the client' do
|
18
|
+
command = double(to_details: { name: 'TestCommand' })
|
19
|
+
|
20
|
+
ref.command command
|
21
|
+
|
22
|
+
expect(client).to have_received(:post).with kind_of Message::Command
|
16
23
|
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#create' do
|
27
|
+
context 'the node also thinks it is the relavent node' do
|
28
|
+
let(:response) { Message::OK.new }
|
29
|
+
|
30
|
+
it 'should send a CreateAggregate message to the relavent node' do
|
31
|
+
ref.create
|
32
|
+
|
33
|
+
expect(client).to have_received(:post).with(Message::CreateAggregate)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'the node does not think it is the right node for id' do
|
38
|
+
let(:response) { Message::Ask.new(SecureRandom.uuid) }
|
39
|
+
|
40
|
+
it 'should raise an error' do
|
41
|
+
expect { ref.create }.to raise_error
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#query' do
|
47
|
+
let(:query) { double to_details: { name: 'TestQuery' } }
|
48
|
+
|
49
|
+
context 'the response is not an error' do
|
50
|
+
let(:response) { Message::Result.new 'hello' }
|
51
|
+
|
52
|
+
it 'should send the command to the aggregate via the client' do
|
53
|
+
ref.query query
|
54
|
+
|
55
|
+
expect(client).to have_received(:post).with kind_of Message::Query
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return the result value' do
|
59
|
+
result = ref.query(query)
|
60
|
+
|
61
|
+
expect(result).to eq 'hello'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'the response is an error' do
|
66
|
+
let(:error) { RuntimeError.new 'Something went wrong' }
|
67
|
+
let(:response) { Message::Result.new QueryError.new error }
|
17
68
|
|
18
|
-
|
19
|
-
|
20
|
-
|
69
|
+
it 'should return the result value' do
|
70
|
+
expect { ref.query(query) }.to raise_error 'Something went wrong'
|
71
|
+
end
|
21
72
|
end
|
22
73
|
end
|
23
74
|
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
RSpec.describe Aggregate do
|
4
|
+
class GiveSomething
|
5
|
+
include Aggro::Command
|
6
|
+
|
7
|
+
string :thing
|
8
|
+
money :value
|
9
|
+
end
|
10
|
+
|
11
|
+
class CheckThings
|
12
|
+
include Aggro::Query
|
13
|
+
end
|
14
|
+
|
15
|
+
class CatSerializer
|
16
|
+
include Aggro::Projection
|
17
|
+
|
18
|
+
def serialize
|
19
|
+
JSON.dump data
|
20
|
+
end
|
21
|
+
|
22
|
+
events do
|
23
|
+
def gave_thing(thing)
|
24
|
+
things << thing
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def data
|
31
|
+
@data ||= { things: things }
|
32
|
+
end
|
33
|
+
|
34
|
+
def things
|
35
|
+
@things ||= []
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Cat
|
40
|
+
include Aggregate
|
41
|
+
|
42
|
+
attr_reader :executed_self
|
43
|
+
attr_reader :executed_command
|
44
|
+
attr_reader :executed_money
|
45
|
+
|
46
|
+
def things_i_have
|
47
|
+
@things ||= []
|
48
|
+
end
|
49
|
+
|
50
|
+
projection :json, via: CatSerializer
|
51
|
+
|
52
|
+
allows GiveSomething do |command|
|
53
|
+
@executed_self = self
|
54
|
+
@executed_command = command
|
55
|
+
|
56
|
+
did.gave_thing
|
57
|
+
end
|
58
|
+
|
59
|
+
responds_to CheckThings do
|
60
|
+
things_i_have
|
61
|
+
end
|
62
|
+
|
63
|
+
events do
|
64
|
+
def gave_thing(thing, value)
|
65
|
+
things_i_have << thing
|
66
|
+
@executed_money = value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
subject(:aggregate) { Cat.new(id) }
|
72
|
+
|
73
|
+
let(:id) { SecureRandom.uuid }
|
74
|
+
let(:command) { GiveSomething.new thing: 'milk', value: '$100 NZD' }
|
75
|
+
let(:query) { CheckThings.new }
|
76
|
+
|
77
|
+
let(:existing_event) do
|
78
|
+
Event.new(:gave_thing, Time.now, thing: 'cake', value: Money.new(100))
|
79
|
+
end
|
80
|
+
let(:response) { Message::Events.new(id, [existing_event]) }
|
81
|
+
let(:client) { double(post: response) }
|
82
|
+
let(:publisher_endpoint) { 'tcp://127.0.0.1:6000' }
|
83
|
+
let(:node) { double(client: client, publisher_endpoint: publisher_endpoint) }
|
84
|
+
let(:fake_locator) { double primary_node: node }
|
85
|
+
let(:locator_class) { double new: fake_locator }
|
86
|
+
|
87
|
+
let(:id) { SecureRandom.uuid }
|
88
|
+
|
89
|
+
let(:fake_server) { spy publish: true }
|
90
|
+
|
91
|
+
before do
|
92
|
+
stub_const 'Aggro::Locator', locator_class
|
93
|
+
allow(Aggro).to receive(:server).and_return fake_server
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '.allows' do
|
97
|
+
it 'should register a command handler' do
|
98
|
+
Cat.allows(Fixnum) { true }
|
99
|
+
|
100
|
+
expect(Cat.allows?(1)).to be_truthy
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '.allows?' do
|
105
|
+
context 'input command has a registeded handler' do
|
106
|
+
it 'should return true' do
|
107
|
+
expect(Cat.allows?(GiveSomething.new)).to be_truthy
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'input command does not have a registed handler' do
|
112
|
+
it 'should return false' do
|
113
|
+
expect(Cat.allows?('')).to be_falsey
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '.create' do
|
119
|
+
let(:id) { SecureRandom.uuid }
|
120
|
+
let(:aggregate_ref) { double create: true }
|
121
|
+
let(:aggregate_ref_class) { spy(new: aggregate_ref) }
|
122
|
+
let(:response) { Message::OK.new }
|
123
|
+
|
124
|
+
before do
|
125
|
+
stub_const 'Aggro::Locator', locator_class
|
126
|
+
stub_const 'Aggro::AggregateRef', aggregate_ref_class
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should send a CreateAggregate message to the relavent node' do
|
130
|
+
Cat.create id
|
131
|
+
|
132
|
+
expect(aggregate_ref).to have_received(:create)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '.find' do
|
137
|
+
let(:aggregate_ref) { double }
|
138
|
+
let(:aggregate_ref_class) { spy(new: aggregate_ref) }
|
139
|
+
|
140
|
+
let(:id) { SecureRandom.uuid }
|
141
|
+
|
142
|
+
before do
|
143
|
+
stub_const 'Aggro::AggregateRef', aggregate_ref_class
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should return an AggregateRef for the aggregate' do
|
147
|
+
expect(Cat.find(id)).to eq aggregate_ref
|
148
|
+
expect(aggregate_ref_class).to have_received(:new).with id, 'Cat'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '.new' do
|
153
|
+
let(:event_bus) { spy }
|
154
|
+
|
155
|
+
before do
|
156
|
+
allow(Aggro).to receive(:event_bus).and_return(event_bus)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should subscribe itself to events for the given ID' do
|
160
|
+
aggregate
|
161
|
+
|
162
|
+
expect(event_bus).to have_received(:subscribe).with id, aggregate
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#apply_command' do
|
167
|
+
it 'should execute the handler with the aggregate as self' do
|
168
|
+
aggregate.send :apply_command, command
|
169
|
+
|
170
|
+
expect(aggregate.executed_self).to eq aggregate
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should preserve types' do
|
174
|
+
aggregate.send :apply_command, command
|
175
|
+
|
176
|
+
expect(aggregate.executed_money).to be_a Money
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should execute the handler with the command as an argument' do
|
180
|
+
aggregate.send :apply_command, command
|
181
|
+
|
182
|
+
expect(aggregate.executed_command).to eq command
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should have the existing event applied' do
|
186
|
+
expect(aggregate.things_i_have).to include 'cake'
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should apply events via the #did proxy' do
|
190
|
+
aggregate.send :apply_command, command
|
191
|
+
|
192
|
+
expect(aggregate.things_i_have).to include 'milk'
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should apply event to the projections' do
|
196
|
+
aggregate.send :apply_command, command
|
197
|
+
|
198
|
+
expect(aggregate.json.serialize).to eq '{"things":["cake","milk"]}'
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#run_query' do
|
203
|
+
it 'should return the result of the query method' do
|
204
|
+
expect(aggregate.send(:run_query, query)).to eq ['cake']
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|