synapse-core 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/lib/synapse.rb +1 -1
  3. data/lib/synapse/command.rb +1 -1
  4. data/lib/synapse/command/command_handler.rb +1 -1
  5. data/lib/synapse/command/mapping.rb +71 -0
  6. data/lib/synapse/command/message.rb +0 -16
  7. data/lib/synapse/command/simple_command_bus.rb +5 -2
  8. data/lib/synapse/common/message.rb +16 -0
  9. data/lib/synapse/configuration.rb +1 -18
  10. data/lib/synapse/configuration/component/command_bus.rb +7 -24
  11. data/lib/synapse/configuration/component/command_bus/simple_command_bus.rb +31 -7
  12. data/lib/synapse/configuration/component/event_bus.rb +3 -8
  13. data/lib/synapse/configuration/component/event_sourcing.rb +11 -8
  14. data/lib/synapse/configuration/component/event_sourcing/aggregate_snapshot_taker.rb +48 -0
  15. data/lib/synapse/configuration/component/event_sourcing/interval_snapshot_policy.rb +33 -0
  16. data/lib/synapse/configuration/component/event_sourcing/repository.rb +36 -1
  17. data/lib/synapse/configuration/component/repository.rb +4 -8
  18. data/lib/synapse/configuration/component/serialization.rb +5 -16
  19. data/lib/synapse/configuration/component/uow.rb +3 -8
  20. data/lib/synapse/configuration/component/upcasting.rb +3 -8
  21. data/lib/synapse/configuration/container_builder.rb +29 -2
  22. data/lib/synapse/configuration/definition_builder.rb +47 -23
  23. data/lib/synapse/domain/message.rb +0 -16
  24. data/lib/synapse/event_bus.rb +1 -1
  25. data/lib/synapse/event_bus/event_listener.rb +1 -1
  26. data/lib/synapse/event_bus/mapping.rb +47 -0
  27. data/lib/synapse/event_sourcing.rb +3 -2
  28. data/lib/synapse/event_sourcing/aggregate_factory.rb +4 -3
  29. data/lib/synapse/event_sourcing/aggregate_root.rb +17 -0
  30. data/lib/synapse/event_sourcing/conflict_resolver.rb +3 -0
  31. data/lib/synapse/event_sourcing/member.rb +34 -6
  32. data/lib/synapse/event_sourcing/repository.rb +17 -0
  33. data/lib/synapse/event_sourcing/snapshot/aggregate_taker.rb +38 -0
  34. data/lib/synapse/event_sourcing/snapshot/policy.rb +27 -0
  35. data/lib/synapse/event_sourcing/snapshot/taker.rb +2 -37
  36. data/lib/synapse/event_sourcing/snapshot/unit_listener.rb +26 -0
  37. data/lib/synapse/event_sourcing/stream_decorator.rb +8 -6
  38. data/lib/synapse/event_store/errors.rb +2 -2
  39. data/lib/synapse/mapping.rb +2 -0
  40. data/lib/synapse/mapping/mapper.rb +75 -0
  41. data/lib/synapse/{wiring/wire.rb → mapping/mapping.rb} +8 -8
  42. data/lib/synapse/process_manager.rb +2 -2
  43. data/lib/synapse/process_manager/mapping/process.rb +44 -0
  44. data/lib/synapse/process_manager/{wiring → mapping}/process_manager.rb +13 -13
  45. data/lib/synapse/process_manager/process.rb +3 -3
  46. data/lib/synapse/repository/locking.rb +14 -8
  47. data/lib/synapse/upcasting/upcaster_chain.rb +2 -2
  48. data/lib/synapse/version.rb +1 -1
  49. data/test/command/{wiring_test.rb → mapping_test.rb} +11 -11
  50. data/test/configuration/component/command_bus/simple_command_bus_test.rb +30 -0
  51. data/test/configuration/component/event_bus/simple_event_bus_test.rb +2 -2
  52. data/test/configuration/component/event_sourcing/repository_test.rb +71 -0
  53. data/test/configuration/component/repository/simple_repository_test.rb +35 -0
  54. data/test/configuration/component/upcasting/upcaster_chain_test.rb +29 -0
  55. data/test/configuration/container_builder_test.rb +4 -6
  56. data/test/event_bus/{wiring_test.rb → mapping_test.rb} +6 -6
  57. data/test/event_sourcing/aggregate_factory_test.rb +5 -1
  58. data/test/event_sourcing/aggregate_root_test.rb +1 -0
  59. data/test/event_sourcing/fixtures.rb +21 -21
  60. data/test/event_sourcing/repository_test.rb +10 -0
  61. data/test/event_sourcing/snapshot/aggregate_taker_test.rb +1 -1
  62. data/test/event_sourcing/snapshot/interval_policy_test.rb +24 -0
  63. data/test/process_manager/{wiring → mapping}/fixtures.rb +7 -8
  64. data/test/process_manager/{wiring → mapping}/process_manager_test.rb +6 -6
  65. data/test/process_manager/{wiring → mapping}/process_test.rb +3 -3
  66. data/test/serialization/converter/chain_test.rb +2 -2
  67. data/test/serialization/converter/factory_test.rb +2 -2
  68. data/test/serialization/converter/identity_test.rb +1 -1
  69. data/test/serialization/converter/json_test.rb +2 -2
  70. data/test/serialization/converter/ox_test.rb +2 -2
  71. data/test/serialization/lazy_object_test.rb +1 -1
  72. data/test/serialization/message/metadata_test.rb +1 -1
  73. data/test/serialization/message/serialization_aware_message_test.rb +5 -5
  74. data/test/serialization/message/serialized_message_builder_test.rb +1 -1
  75. data/test/serialization/message/serialized_message_test.rb +5 -5
  76. data/test/serialization/message/serializer_test.rb +2 -2
  77. data/test/serialization/revision_resolver_test.rb +1 -1
  78. data/test/serialization/serialized_object_test.rb +2 -2
  79. data/test/serialization/serialized_type_test.rb +2 -2
  80. data/test/serialization/serializer/marshal_test.rb +1 -1
  81. data/test/serialization/serializer/oj_test.rb +1 -1
  82. data/test/serialization/serializer/ox_test.rb +2 -2
  83. data/test/serialization/serializer_test.rb +1 -1
  84. data/test/uow/factory_test.rb +1 -1
  85. data/test/uow/outer_commit_listener_test.rb +4 -4
  86. data/test/uow/provider_test.rb +5 -5
  87. data/test/uow/uow_test.rb +19 -17
  88. data/test/upcasting/chain_test.rb +1 -1
  89. data/test/upcasting/data_test.rb +3 -1
  90. metadata +30 -37
  91. data/lib/synapse/command/wiring.rb +0 -47
  92. data/lib/synapse/event_bus/wiring.rb +0 -20
  93. data/lib/synapse/event_sourcing/snapshot/count_stream.rb +0 -86
  94. data/lib/synapse/event_sourcing/snapshot/count_trigger.rb +0 -91
  95. data/lib/synapse/process_manager/wiring/process.rb +0 -27
  96. data/lib/synapse/wiring.rb +0 -3
  97. data/lib/synapse/wiring/message_wiring.rb +0 -76
  98. data/lib/synapse/wiring/wire_registry.rb +0 -61
  99. data/test/event_sourcing/snapshot/integration_test.rb +0 -65
  100. data/test/wiring/wire_registry_test.rb +0 -60
  101. data/test/wiring/wire_test.rb +0 -51
@@ -0,0 +1,29 @@
1
+ module Synapse
2
+ module Configuration
3
+ class UpcasterChainDefinitionBuilderTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @container = Container.new
7
+ @builder = ContainerBuilder.new @container
8
+ end
9
+
10
+ should 'build with sensible defaults' do
11
+ @builder.converter_factory
12
+ @builder.factory :some_upcaster, :tag => :upcaster do
13
+ Object.new
14
+ end
15
+
16
+ @builder.upcaster_chain
17
+
18
+ converter_factory = @container.resolve :converter_factory
19
+ some_upcaster = @container.resolve :some_upcaster
20
+
21
+ upcaster_chain = @container.resolve :upcaster_chain
22
+
23
+ assert_same converter_factory, upcaster_chain.converter_factory
24
+ assert_includes upcaster_chain.upcasters, some_upcaster
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -17,7 +17,7 @@ module Synapse
17
17
  ContainerBuilder.initializers = @initializers
18
18
  end
19
19
 
20
- def test_initializers
20
+ should 'call initializers upon creation' do
21
21
  ContainerBuilder.initializer do
22
22
  definition :test_definition do
23
23
  tag :derp
@@ -29,12 +29,10 @@ module Synapse
29
29
  assert @container.registered? :test_definition
30
30
  end
31
31
 
32
- def test_factory
32
+ should 'create a simple definition from a factory' do
33
33
  builder = ContainerBuilder.new @container
34
- builder.build_with do
35
- factory :derp_service, :tag => [:first_tag, :nth_tag] do
36
- 123
37
- end
34
+ builder.factory :derp_service, :tag => [:first_tag, :nth_tag] do
35
+ 123
38
36
  end
39
37
 
40
38
  assert @container.registered? :derp_service
@@ -3,9 +3,9 @@ require 'test_helper'
3
3
  module Synapse
4
4
  module EventBus
5
5
 
6
- class WiringEventListenerTest < Test::Unit::TestCase
6
+ class MappingEventListenerTest < Test::Unit::TestCase
7
7
  should 'use the correct handler when notified of an events' do
8
- listener = ExampleWiringEventListener.new
8
+ listener = ExampleMappingEventListener.new
9
9
 
10
10
  event = Domain::EventMessage.build do |builder|
11
11
  builder.payload = TestEvent.new
@@ -28,16 +28,16 @@ module Synapse
28
28
  class TestEvent; end
29
29
  class TestSubEvent < TestEvent; end
30
30
 
31
- class ExampleWiringEventListener
32
- include WiringEventListener
31
+ class ExampleMappingEventListener
32
+ include MappingEventListener
33
33
 
34
34
  attr_accessor :handled, :sub_handled
35
35
 
36
- wire TestEvent do |event|
36
+ map_event TestEvent do |event|
37
37
  @handled = true
38
38
  end
39
39
 
40
- wire TestSubEvent do |event|
40
+ map_event TestSubEvent do |event|
41
41
  @sub_handled = true
42
42
  end
43
43
  end
@@ -20,13 +20,17 @@ module Synapse
20
20
 
21
21
  should 'use an aggregate snapshot if available' do
22
22
  snapshot = StubAggregate.new 123
23
+ snapshot.change_something
24
+ snapshot.mark_committed
25
+
23
26
  snapshot_event = Domain::DomainEventMessage.build do |m|
24
27
  m.payload = snapshot
25
28
  end
26
29
 
27
30
  aggregate = @factory.create_aggregate 123, snapshot_event
28
31
 
29
- assert aggregate.equal? snapshot
32
+ assert_same snapshot, aggregate
33
+ assert_equal snapshot.version, aggregate.initial_version
30
34
  end
31
35
  end
32
36
 
@@ -32,6 +32,7 @@ module Synapse
32
32
 
33
33
  assert_equal 123, aggregate.id
34
34
  assert_equal 2, aggregate.version
35
+ assert_equal 0, aggregate.initial_version
35
36
  end
36
37
 
37
38
  should 'raise an exception if initialization is attempted when the aggregate has state' do
@@ -9,6 +9,18 @@ module Synapse
9
9
  end
10
10
  end
11
11
 
12
+ class StubCreatedEvent
13
+ attr_reader :id
14
+
15
+ def initialize(id)
16
+ @id = id
17
+ end
18
+ end
19
+
20
+ class StubDeletedEvent; end
21
+
22
+ class StubChangedEvent; end
23
+
12
24
  class StubAggregate
13
25
  include AggregateRoot
14
26
 
@@ -35,16 +47,16 @@ module Synapse
35
47
  @stub_entities = Array.new
36
48
  end
37
49
 
38
- def handle_event(event)
39
- payload = event.payload
40
- type = event.payload_type
50
+ map_event StubCreatedEvent do |event|
51
+ @id = event.id
52
+ end
41
53
 
42
- if type.eql? StubCreatedEvent
43
- @id = payload.id
44
- elsif type.eql? StubDeletedEvent
45
- mark_deleted
46
- end
54
+ map_event StubDeletedEvent do |event|
55
+ mark_deleted
56
+ end
47
57
 
58
+ def handle_event(event)
59
+ super
48
60
  @event_count = @event_count.next
49
61
  end
50
62
  end
@@ -69,17 +81,5 @@ module Synapse
69
81
  end
70
82
  end
71
83
 
72
- class StubCreatedEvent
73
- attr_reader :id
74
-
75
- def initialize(id)
76
- @id = id
77
- end
78
- end
79
-
80
- class StubDeletedEvent; end
81
-
82
- class StubChangedEvent; end
83
-
84
84
  end
85
- end
85
+ end
@@ -88,6 +88,16 @@ module Synapse
88
88
  @unit.commit
89
89
  end
90
90
 
91
+ should 'register a snapshot listener if a policy and taker are set' do
92
+ @repository.snapshot_policy = IntervalSnapshotPolicy.new 30
93
+ @repository.snapshot_taker = AggregateSnapshotTaker.new @event_store
94
+
95
+ mock(@unit).register_listener(anything)
96
+ mock(@unit).register_listener(is_a(SnapshotUnitOfWorkListener))
97
+
98
+ @repository.add StubAggregate.new 123
99
+ end
100
+
91
101
  private
92
102
 
93
103
  def create_event(aggregate_id, seq, payload)
@@ -5,7 +5,7 @@ module Synapse
5
5
  module EventSourcing
6
6
 
7
7
  class AggregateSnapshotTakerTest < Test::Unit::TestCase
8
- def test_schedule_snapshot
8
+ should 'store a snapshot by serializing the aggregate itself' do
9
9
  event_store = Object.new
10
10
  aggregate_factory = GenericAggregateFactory.new StubAggregate
11
11
 
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+
3
+ module Synapse
4
+ module EventSourcing
5
+ class IntervalSnapshotPolicyTest < Test::Unit::TestCase
6
+
7
+ should 'suggest a snapshot if the threshold is surpassed' do
8
+ aggregate_a = Object.new
9
+ aggregate_b = Object.new
10
+
11
+ stub(aggregate_a).initial_version { nil }
12
+ stub(aggregate_a).version { 35 }
13
+ stub(aggregate_b).initial_version { 0 }
14
+ stub(aggregate_b).version { 20 }
15
+
16
+ policy = IntervalSnapshotPolicy.new 30
17
+
18
+ assert policy.should_snapshot? aggregate_a
19
+ refute policy.should_snapshot? aggregate_b
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -4,7 +4,7 @@ module Synapse
4
4
  module ProcessManager
5
5
 
6
6
  class OrderEvent
7
- attr_accessor :order_id
7
+ attr_reader :order_id
8
8
  def initialize(order_id)
9
9
  @order_id = order_id
10
10
  end
@@ -16,27 +16,26 @@ module Synapse
16
16
  class OrderCanceled < OrderEvent; end
17
17
  class OrderDerped; end
18
18
 
19
- class OrderProcess < WiringProcess
19
+ class OrderProcess < MappingProcess
20
20
  attr_reader :handled
21
21
 
22
- wire OrderCreated, correlate: :order_id, start: true do
22
+ map_event OrderCreated, correlate: :order_id, start: true do
23
23
  @handled = (@handled or 0).next
24
24
  end
25
25
 
26
- wire OrderForceCreated, correlate: :order_id, start: true, force_new: true do
26
+ map_event OrderForceCreated, correlate: :order_id, start: true, force_new: true do
27
27
  @handled = (@handled or 0).next
28
28
  end
29
29
 
30
- wire OrderUpdated, correlate: :order_id do
30
+ map_event OrderUpdated, correlate: :order_id do
31
31
  @handled = (@handled or 0).next
32
32
  end
33
33
 
34
- wire OrderCanceled, correlate: :order_id, finish: true do
34
+ map_event OrderCanceled, correlate: :order_id, finish: true do
35
35
  @handled = (@handled or 0).next
36
36
  end
37
37
 
38
- wire OrderDerped, correlate: :derpy_key do; end
38
+ map_event OrderDerped, correlate: :derpy_key do; end
39
39
  end
40
-
41
40
  end
42
41
  end
@@ -1,25 +1,25 @@
1
1
  require 'test_helper'
2
- require 'process_manager/wiring/fixtures'
2
+ require 'process_manager/mapping/fixtures'
3
3
 
4
4
  module Synapse
5
5
  module ProcessManager
6
6
 
7
- class WiringProcessManagerTest < Test::Unit::TestCase
7
+ class MappingProcessManagerTest < Test::Unit::TestCase
8
8
  def setup
9
9
  @repository = InMemoryProcessRepository.new
10
10
  @factory = GenericProcessFactory.new
11
11
  @lock_manager = LockManager.new
12
12
 
13
- @manager = WiringProcessManager.new @repository, @factory, @lock_manager, OrderProcess
13
+ @manager = MappingProcessManager.new @repository, @factory, @lock_manager, OrderProcess
14
14
  end
15
15
 
16
16
  should 'raise an exception if used with a process that does not support wiring' do
17
17
  assert_raise ArgumentError do
18
- WiringProcessManager.new @repository, @factory, @lock_manager, Process
18
+ MappingProcessManager.new @repository, @factory, @lock_manager, Process
19
19
  end
20
20
  end
21
21
 
22
- should 'use wiring attributes to determine correlation keys' do
22
+ should 'use mapping attributes to determine correlation keys' do
23
23
  event = create_event OrderCreated.new 123
24
24
  @manager.notify event
25
25
 
@@ -29,7 +29,7 @@ module Synapse
29
29
  assert_equal 1, processes.count
30
30
  end
31
31
 
32
- should 'use wiring attributes to determine creation policy' do
32
+ should 'use mapping attributes to determine creation policy' do
33
33
  event = create_event OrderCreated.new 123
34
34
 
35
35
  @manager.notify event
@@ -1,10 +1,10 @@
1
1
  require 'test_helper'
2
- require 'process_manager/wiring/fixtures'
2
+ require 'process_manager/mapping/fixtures'
3
3
 
4
4
  module Synapse
5
5
  module ProcessManager
6
6
 
7
- class WiringProcessTest < Test::Unit::TestCase
7
+ class MappingProcessTest < Test::Unit::TestCase
8
8
  def setup
9
9
  @process = OrderProcess.new
10
10
  end
@@ -19,7 +19,7 @@ module Synapse
19
19
  assert_equal 1, @process.handled
20
20
  end
21
21
 
22
- should 'use wiring attributes to determine when to mark itself as finished' do
22
+ should 'use mapping attributes to determine when to mark itself as finished' do
23
23
  event = Domain::EventMessage.build do |builder|
24
24
  builder.payload = OrderCanceled.new 123
25
25
  end
@@ -4,7 +4,7 @@ module Synapse
4
4
  module Serialization
5
5
 
6
6
  class ConverterChainTest < Test::Unit::TestCase
7
- def test_convert
7
+ should 'convert using a chain of converters' do
8
8
  converters = Array.new
9
9
  converters << ObjectToJsonConverter.new << JsonToObjectConverter.new
10
10
 
@@ -13,7 +13,7 @@ module Synapse
13
13
  assert_equal Object, chain.source_type
14
14
  assert_equal Object, chain.target_type
15
15
 
16
- content = { 'foo' => 'bar' }
16
+ content = { foo: 0 }.stringify_keys
17
17
 
18
18
  type = SerializedType.new 'TestType', 1
19
19
  object = SerializedObject.new content, content.class, type
@@ -9,12 +9,12 @@ module Synapse
9
9
  @factory = ConverterFactory.new
10
10
  end
11
11
 
12
- def test_identity
12
+ should 'return an identity converter if the source/target type are the same' do
13
13
  assert @factory.has_converter?(String, String)
14
14
  assert @factory.converter(String, String).is_a?(IdentityConverter)
15
15
  end
16
16
 
17
- def test_converter
17
+ should 'return a converter matching a source/target type' do
18
18
  refute @factory.has_converter?(Object, String)
19
19
 
20
20
  assert_raise ConversionError do
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  module Synapse
4
4
  module Serialization
5
5
  class IdentityConverterTest < Test::Unit::TestCase
6
- def test_conversion
6
+ should 'pass through objects unchanged' do
7
7
  converter = IdentityConverter.new Object
8
8
  content = Hash.new
9
9
 
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  module Synapse
4
4
  module Serialization
5
5
  class JsonToObjectConverterTest < Test::Unit::TestCase
6
- def test_convert
6
+ should 'convert content from a JSON string to a Ruby data structure' do
7
7
  converter = JsonToObjectConverter.new
8
8
 
9
9
  assert_equal String, converter.source_type
@@ -16,7 +16,7 @@ module Synapse
16
16
  end
17
17
 
18
18
  class ObjectToJsonConverterTest < Test::Unit::TestCase
19
- def test_convert
19
+ should 'convert a Ruby data structure to a JSON string' do
20
20
  converter = ObjectToJsonConverter.new
21
21
 
22
22
  assert_equal Object, converter.source_type
@@ -7,7 +7,7 @@ module Synapse
7
7
  skip 'Ox not supported on JRuby' if defined? JRUBY_VERSION
8
8
  end
9
9
 
10
- def test_convert
10
+ should 'convert an Ox document to an XML string' do
11
11
  converter = OxDocumentToXmlConverter.new
12
12
 
13
13
  assert_equal Ox::Document, converter.source_type
@@ -25,7 +25,7 @@ module Synapse
25
25
  skip 'Ox not supported on JRuby' if defined? JRUBY_VERSION
26
26
  end
27
27
 
28
- def test_convert
28
+ should 'convert an XML string to an Ox document' do
29
29
  converter = XmlToOxDocumentConverter.new
30
30
 
31
31
  assert_equal String, converter.source_type
@@ -4,7 +4,7 @@ require 'serialization/fixtures'
4
4
  module Synapse
5
5
  module Serialization
6
6
  class LazyObjectTest < Test::Unit::TestCase
7
- def test_deserialize_once
7
+ should 'only deserialize an object once' do
8
8
  serializer = MarshalSerializer.new ConverterFactory.new
9
9
  event = TestEvent.new 'a', 'b'
10
10
 
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  module Synapse
4
4
  module Serialization
5
5
  class SerializedMetadataTest < Test::Unit::TestCase
6
- def test_initialize
6
+ should 'act like a serialized object' do
7
7
  content = 'test-metadata'
8
8
  content_type = String
9
9
 
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  module Synapse
4
4
  module Serialization
5
5
  class SerializationAwareDomainEventMessageTest < Test::Unit::TestCase
6
- def test_delegation
6
+ should 'delegate fields to the original message' do
7
7
  message = Domain::DomainEventMessage.build do |builder|
8
8
  builder.payload = Object.new
9
9
  builder.aggregate_id = 123
@@ -23,7 +23,7 @@ module Synapse
23
23
  assert_same message.sequence_number, aware.sequence_number
24
24
  end
25
25
 
26
- def test_caching
26
+ should 'cache serialization operations' do
27
27
  message = Domain::DomainEventMessage.build do |builder|
28
28
  builder.payload = Object.new
29
29
  builder.aggregate_id = 123
@@ -47,7 +47,7 @@ module Synapse
47
47
  end
48
48
  end
49
49
 
50
- def test_decorate
50
+ should 'not further wrap messages that are already serialization aware' do
51
51
  message = Domain::DomainEventMessage.build
52
52
 
53
53
  aware = SerializationAwareDomainEventMessage.decorate message
@@ -56,7 +56,7 @@ module Synapse
56
56
  assert_same aware, new_aware
57
57
  end
58
58
 
59
- def test_and_metadata
59
+ should 'wrap messages that are duplicated to add metadata' do
60
60
  message = Domain::DomainEventMessage.build do |builder|
61
61
  builder.metadata = { foo: 0 }
62
62
  end
@@ -71,7 +71,7 @@ module Synapse
71
71
  assert new_aware.is_a? SerializationAwareDomainEventMessage
72
72
  end
73
73
 
74
- def test_with_metadata
74
+ should 'wrap messages that are duplicated to replace metadata' do
75
75
  message = Domain::DomainEventMessage.build do |builder|
76
76
  builder.metadata = { foo: 0 }
77
77
  end