ruby_event_store 0.30.0 → 1.3.1

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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +9 -1
  3. data/Makefile +15 -62
  4. data/lib/ruby_event_store/batch_enumerator.rb +15 -5
  5. data/lib/ruby_event_store/broker.rb +45 -0
  6. data/lib/ruby_event_store/client.rb +220 -130
  7. data/lib/ruby_event_store/composed_dispatcher.rb +24 -0
  8. data/lib/ruby_event_store/constants.rb +2 -0
  9. data/lib/ruby_event_store/correlated_commands.rb +42 -0
  10. data/lib/ruby_event_store/dispatcher.rb +20 -0
  11. data/lib/ruby_event_store/errors.rb +16 -16
  12. data/lib/ruby_event_store/event.rb +70 -14
  13. data/lib/ruby_event_store/expected_version.rb +2 -0
  14. data/lib/ruby_event_store/immediate_async_dispatcher.rb +17 -0
  15. data/lib/ruby_event_store/in_memory_repository.rb +45 -17
  16. data/lib/ruby_event_store/instrumented_dispatcher.rb +23 -0
  17. data/lib/ruby_event_store/instrumented_repository.rb +63 -0
  18. data/lib/ruby_event_store/link_by_metadata.rb +57 -0
  19. data/lib/ruby_event_store/mappers/default.rb +10 -26
  20. data/lib/ruby_event_store/mappers/encryption_key.rb +74 -0
  21. data/lib/ruby_event_store/mappers/encryption_mapper.rb +16 -0
  22. data/lib/ruby_event_store/mappers/forgotten_data.rb +30 -0
  23. data/lib/ruby_event_store/mappers/in_memory_encryption_key_repository.rb +34 -0
  24. data/lib/ruby_event_store/mappers/instrumented_mapper.rb +28 -0
  25. data/lib/ruby_event_store/mappers/json_mapper.rb +16 -0
  26. data/lib/ruby_event_store/mappers/null_mapper.rb +5 -8
  27. data/lib/ruby_event_store/mappers/pipeline.rb +31 -0
  28. data/lib/ruby_event_store/mappers/pipeline_mapper.rb +22 -0
  29. data/lib/ruby_event_store/mappers/protobuf.rb +13 -67
  30. data/lib/ruby_event_store/mappers/transformation/domain_event.rb +26 -0
  31. data/lib/ruby_event_store/mappers/transformation/encryption.rb +128 -0
  32. data/lib/ruby_event_store/mappers/transformation/event_class_remapper.rb +24 -0
  33. data/lib/ruby_event_store/mappers/transformation/item.rb +56 -0
  34. data/lib/ruby_event_store/mappers/transformation/proto_event.rb +17 -0
  35. data/lib/ruby_event_store/mappers/transformation/protobuf_encoder.rb +30 -0
  36. data/lib/ruby_event_store/mappers/transformation/protobuf_nested_struct_metadata.rb +30 -0
  37. data/lib/ruby_event_store/mappers/transformation/serialization.rb +34 -0
  38. data/lib/ruby_event_store/mappers/transformation/serialized_record.rb +27 -0
  39. data/lib/ruby_event_store/mappers/transformation/stringify_metadata_keys.rb +24 -0
  40. data/lib/ruby_event_store/mappers/transformation/symbolize_metadata_keys.rb +24 -0
  41. data/lib/ruby_event_store/metadata.rb +4 -2
  42. data/lib/ruby_event_store/projection.rb +34 -12
  43. data/lib/ruby_event_store/serialized_record.rb +3 -1
  44. data/lib/ruby_event_store/spec/broker_lint.rb +92 -0
  45. data/lib/ruby_event_store/spec/dispatcher_lint.rb +4 -36
  46. data/lib/ruby_event_store/spec/event_lint.rb +71 -0
  47. data/lib/ruby_event_store/spec/event_repository_lint.rb +1092 -962
  48. data/lib/ruby_event_store/spec/mapper_lint.rb +17 -0
  49. data/lib/ruby_event_store/spec/scheduler_lint.rb +9 -0
  50. data/lib/ruby_event_store/spec/subscriptions_lint.rb +111 -0
  51. data/lib/ruby_event_store/specification.rb +201 -56
  52. data/lib/ruby_event_store/specification_reader.rb +43 -0
  53. data/lib/ruby_event_store/specification_result.rb +212 -0
  54. data/lib/ruby_event_store/stream.rb +2 -0
  55. data/lib/ruby_event_store/subscriptions.rb +110 -0
  56. data/lib/ruby_event_store/transform_keys.rb +31 -0
  57. data/lib/ruby_event_store/version.rb +3 -1
  58. data/lib/ruby_event_store.rb +34 -4
  59. data/ruby_event_store.gemspec +1 -10
  60. metadata +47 -126
  61. data/exe/res-deprecated-read-api-migrator +0 -19
  62. data/lib/ruby_event_store/deprecated_read_api_rewriter.rb +0 -67
  63. data/lib/ruby_event_store/deprecated_read_api_runner.rb +0 -64
  64. data/lib/ruby_event_store/deprecations.rb +0 -7
  65. data/lib/ruby_event_store/pub_sub/broker.rb +0 -73
  66. data/lib/ruby_event_store/pub_sub/dispatcher.rb +0 -25
  67. data/lib/ruby_event_store/spec/event_broker_lint.rb +0 -211
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4df1be4d7e983bb77d1c069496cdca690f72d593a0c0d83f9d3d7519d63ed115
4
- data.tar.gz: 0d156019cb436a9fb262b680e3e46d007d7c64ec46f1186bae82c5e4344b03c8
3
+ metadata.gz: 88193e00bf9ebd58347de1cb3cf58848cc4b09abc603ffef5fb85b2565bf0b74
4
+ data.tar.gz: 744d44d76c00843f897c321919eed85b9597063d44a52328f7616c6682063d90
5
5
  SHA512:
6
- metadata.gz: 490e25c72237a78d241df447ad97d55f2d245d40ea4d3b7665b5c699e5273d3b8257f06d2a0a3a1d8d3bc0940158819f433d72b47460620a5c4ed756fad5d646
7
- data.tar.gz: 4355818327a762070c48a8121728c9914b74881187f028c8dc8c440d46c9b51556621b5fb697cdbbd97fc93c3eaff03cafa69fefa6d84f6655023cb053336ae4
6
+ metadata.gz: ffd5f11e365d901d7bdf7a6c5d78cad674166416dcd44cd8448982dbe23d8a243ea72b3f66e02c4cab134ef87e3f5d0e519e4de0a913bf26390f7867cc5318d8
7
+ data.tar.gz: 3044ebfc2ca93c1b5c5fa40afd250c8aac8edc1c5299444b6094df17d1b5214b19400070761a408f8816dbe507fb52c2063587fdd428f911470310ef26434778
data/Gemfile CHANGED
@@ -1,3 +1,11 @@
1
1
  source 'https://rubygems.org'
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
2
4
  gemspec
3
- gem 'protobuf_nested_struct'
5
+
6
+ eval_gemfile File.expand_path('../support/bundler/Gemfile.shared', __dir__)
7
+
8
+ gem 'protobuf_nested_struct'
9
+ gem 'google-protobuf', '~> 3.12.2', '>= 3.12.2'
10
+ gem 'activesupport', '~> 5.0'
11
+ gem 'concurrent-ruby', github: 'ruby-concurrency/concurrent-ruby', ref: 'c4cbc968c55e5b983dae953095761896220c46d1'
data/Makefile CHANGED
@@ -1,69 +1,22 @@
1
1
  GEM_VERSION = $(shell cat ../RES_VERSION)
2
2
  GEM_NAME = ruby_event_store
3
3
  REQUIRE = $(GEM_NAME)
4
- IGNORE = RubyEventStore.const_missing \
5
- RubyEventStore::InMemoryRepository\#append_with_synchronize \
6
- RubyEventStore::InMemoryRepository\#normalize_to_array \
7
- RubyEventStore::Client\#normalize_to_array \
8
- RubyEventStore::Client::Within\#normalize_to_array \
4
+ IGNORE = RubyEventStore::InMemoryRepository\#append_with_synchronize \
9
5
  RubyEventStore::Client::Within\#add_thread_subscribers \
10
6
  RubyEventStore::Client::Within\#add_thread_global_subscribers \
11
- RubyEventStore::Client\#read_all_streams_forward \
12
- RubyEventStore::Client\#read_all_streams_backward \
13
- RubyEventStore::Client\#read_stream_events_forward \
14
- RubyEventStore::Client\#read_stream_events_backward \
15
- RubyEventStore::Client\#read_events_forward \
16
- RubyEventStore::Client\#read_events_backward \
17
- RubyEventStore::DeprecatedReadAPIRunner* \
18
- RubyEventStore::DeprecatedReadAPIRewriter*
19
- SUBJECT ?= RubyEventStore*
20
-
21
- install: ## Install gem dependencies
22
- @echo "Installing gem dependencies"
23
- @bundle install
24
-
25
- remove-lock:
26
- @echo "Removing resolved dependency versions"
27
- -rm Gemfile.lock
28
-
29
- reinstall: remove-lock install ## Removing resolved dependency versions
30
-
31
- test: ## Run unit tests
32
- @echo "Running unit tests"
33
- @bundle exec rspec
34
-
35
- mutate: test ## Run mutation tests
36
- @echo "Running mutation tests"
37
- @MUTATING=true bundle exec mutant --include lib \
38
- $(addprefix --require ,$(REQUIRE)) \
39
- $(addprefix --ignore-subject ,$(IGNORE)) \
40
- --use rspec "$(SUBJECT)"
7
+ RubyEventStore::Client::Within\#call \
8
+ RubyEventStore::Client\#default_correlation_id_generator \
9
+ RubyEventStore::Mappers::InMemoryEncryptionKeyRepository\#prepare_encrypt \
10
+ RubyEventStore::Mappers::EncryptionKey\#prepare_encrypt \
11
+ RubyEventStore::Mappers::EncryptionKey\#prepare_decrypt \
12
+ RubyEventStore::Mappers::EncryptionKey\#prepare_auth_data \
13
+ RubyEventStore::Mappers::EncryptionKey\#encrypt_authenticated \
14
+ RubyEventStore::Mappers::EncryptionKey\#ciphertext_from_authenticated
41
15
 
42
- mutate-fast: ## Run mutation tests with --fail-fast
43
- @echo "Running mutation tests"
44
- @MUTATING=true bundle exec mutant --include lib \
45
- $(addprefix --require ,$(REQUIRE)) \
46
- $(addprefix --ignore-subject ,$(IGNORE)) \
47
- --fail-fast \
48
- --use rspec "$(SUBJECT)"
49
-
50
- build:
51
- @echo "Building gem package"
52
- @gem build -V $(GEM_NAME).gemspec
53
- @mkdir -p pkg/
54
- @mv $(GEM_NAME)-$(GEM_VERSION).gem pkg/
55
-
56
- push:
57
- @echo "Pushing package to RubyGems"
58
- @gem push -k dev_arkency pkg/$(GEM_NAME)-$(GEM_VERSION).gem
59
-
60
- clean:
61
- @echo "Removing previously built package"
62
- -rm pkg/$(GEM_NAME)-$(GEM_VERSION).gem
63
-
64
- help:
65
- @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
66
-
67
- .PHONY: help
68
- .DEFAULT_GOAL := help
16
+ SUBJECT ?= RubyEventStore*
69
17
 
18
+ include ../support/make/install.mk
19
+ include ../support/make/test.mk
20
+ include ../support/make/mutant.mk
21
+ include ../support/make/gem.mk
22
+ include ../support/make/help.mk
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RubyEventStore
2
4
  class BatchEnumerator
3
5
  def initialize(batch_size, total_limit, reader)
@@ -8,18 +10,26 @@ module RubyEventStore
8
10
 
9
11
  def each
10
12
  return to_enum unless block_given?
11
- (0...total_limit).step(batch_size) do |batch_offset|
12
- batch_offset = Integer(batch_offset)
13
- batch_limit = [batch_size, total_limit - batch_offset].min
14
- result = reader.call(batch_offset, batch_limit)
13
+
14
+ 0.step(total_limit - 1, batch_size) do |batch_offset|
15
+ batch_limit = [batch_size, total_limit - batch_offset].min
16
+ result = reader.call(batch_offset, batch_limit)
15
17
 
16
18
  break if result.empty?
17
19
  yield result
18
20
  end
19
21
  end
20
22
 
23
+ def first
24
+ each.first
25
+ end
26
+
27
+ def to_a
28
+ each.to_a
29
+ end
30
+
21
31
  private
22
32
 
23
- attr_accessor :batch_size, :total_limit, :reader
33
+ attr_reader :batch_size, :total_limit, :reader
24
34
  end
25
35
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ class Broker
5
+ def initialize(subscriptions:, dispatcher:)
6
+ @subscriptions = subscriptions
7
+ @dispatcher = dispatcher
8
+ end
9
+
10
+ def call(event, serialized_event)
11
+ subscribers = subscriptions.all_for(event.event_type)
12
+ subscribers.each do |subscriber|
13
+ dispatcher.call(subscriber, event, serialized_event)
14
+ end
15
+ end
16
+
17
+ def add_subscription(subscriber, event_types)
18
+ verify_subscription(subscriber)
19
+ subscriptions.add_subscription(subscriber, event_types)
20
+ end
21
+
22
+ def add_global_subscription(subscriber)
23
+ verify_subscription(subscriber)
24
+ subscriptions.add_global_subscription(subscriber)
25
+ end
26
+
27
+ def add_thread_subscription(subscriber, event_types)
28
+ verify_subscription(subscriber)
29
+ subscriptions.add_thread_subscription(subscriber, event_types)
30
+ end
31
+
32
+ def add_thread_global_subscription(subscriber)
33
+ verify_subscription(subscriber)
34
+ subscriptions.add_thread_global_subscription(subscriber)
35
+ end
36
+
37
+ private
38
+ attr_reader :subscriptions, :dispatcher
39
+
40
+ def verify_subscription(subscriber)
41
+ raise SubscriberNotExist, "subscriber must be first argument or block" unless subscriber
42
+ raise InvalidHandler.new("Handler #{subscriber} is invalid for dispatcher #{dispatcher}") unless dispatcher.verify(subscriber)
43
+ end
44
+ end
45
+ end
@@ -1,231 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent'
4
+
1
5
  module RubyEventStore
2
6
  class Client
3
7
  def initialize(repository:,
4
8
  mapper: Mappers::Default.new,
5
- event_broker: PubSub::Broker.new,
6
- page_size: PAGE_SIZE,
7
- metadata_proc: nil,
8
- clock: ->{ Time.now.utc })
9
+ subscriptions: Subscriptions.new,
10
+ dispatcher: Dispatcher.new,
11
+ clock: default_clock,
12
+ correlation_id_generator: default_correlation_id_generator)
9
13
  @repository = repository
10
14
  @mapper = mapper
11
- @event_broker = event_broker
12
- @page_size = page_size
13
- warn "`RubyEventStore::Client#metadata_proc` has been deprecated. Use `RubyEventStore::Client#with_metadata` instead." if metadata_proc
14
- @metadata_proc = metadata_proc
15
+ @broker = Broker.new(subscriptions: subscriptions, dispatcher: dispatcher)
15
16
  @clock = clock
17
+ @metadata = Concurrent::ThreadLocalVar.new
18
+ @correlation_id_generator = correlation_id_generator
16
19
  end
17
20
 
18
- def publish_events(events, stream_name: GLOBAL_STREAM, expected_version: :any)
19
- append_to_stream(events, stream_name: stream_name, expected_version: expected_version)
20
- events.each do |ev|
21
- event_broker.notify_subscribers(ev)
22
- end
23
- :ok
24
- end
25
21
 
26
- def publish_event(event, stream_name: GLOBAL_STREAM, expected_version: :any)
27
- publish_events([event], stream_name: stream_name, expected_version: expected_version)
22
+ # Persists events and notifies subscribed handlers about them
23
+ #
24
+ # @param events [Array<Event, Proto>, Event, Proto] event(s)
25
+ # @param stream_name [String] name of the stream for persisting events.
26
+ # @param expected_version [:any, :auto, :none, Integer] controls optimistic locking strategy. {http://railseventstore.org/docs/expected_version/ Read more}
27
+ # @return [self]
28
+ def publish(events, stream_name: GLOBAL_STREAM, expected_version: :any)
29
+ enriched_events = enrich_events_metadata(events)
30
+ serialized_events = serialize_events(enriched_events)
31
+ append_to_stream_serialized_events(serialized_events, stream_name: stream_name, expected_version: expected_version)
32
+ enriched_events.zip(serialized_events) do |event, serialized_event|
33
+ with_metadata(
34
+ correlation_id: event.metadata.fetch(:correlation_id),
35
+ causation_id: event.event_id,
36
+ ) do
37
+ broker.(event, serialized_event)
38
+ end
39
+ end
40
+ self
28
41
  end
29
42
 
30
- def append_to_stream(events, stream_name: GLOBAL_STREAM, expected_version: :any)
31
- events = normalize_to_array(events)
32
- events.each{|event| enrich_event_metadata(event) }
33
- repository.append_to_stream(serialized_events(events), Stream.new(stream_name), ExpectedVersion.new(expected_version))
34
- :ok
43
+ # Persists new event(s) without notifying any subscribed handlers
44
+ #
45
+ # @param (see #publish)
46
+ # @return [self]
47
+ def append(events, stream_name: GLOBAL_STREAM, expected_version: :any)
48
+ serialized_events = serialize_events(enrich_events_metadata(events))
49
+ append_to_stream_serialized_events(serialized_events, stream_name: stream_name, expected_version: expected_version)
50
+ self
35
51
  end
36
52
 
37
- def link_to_stream(event_ids, stream_name:, expected_version: :any)
53
+ # Links already persisted event(s) to a different stream.
54
+ # Does not notify any subscribed handlers.
55
+ #
56
+ # @param event_ids [String, Array<String>] ids of events
57
+ # @param stream_name (see #publish)
58
+ # @param expected_version (see #publish)
59
+ # @return [self]
60
+ def link(event_ids, stream_name:, expected_version: :any)
38
61
  repository.link_to_stream(event_ids, Stream.new(stream_name), ExpectedVersion.new(expected_version))
39
62
  self
40
63
  end
41
64
 
65
+ # Deletes a stream.
66
+ # All events from the stream remain intact but they are no
67
+ # longer linked to the stream.
68
+ #
69
+ # @param stream_name [String] name of the stream to be cleared.
70
+ # @return [self]
42
71
  def delete_stream(stream_name)
43
72
  repository.delete_stream(Stream.new(stream_name))
44
- :ok
45
- end
46
-
47
- def read_events_forward(stream_name, start: :head, count: page_size)
48
- warn <<~EOW
49
- RubyEventStore::Client#read_events_forward has been deprecated.
50
-
51
- Use following fluent API to receive exact results:
52
- client.read.stream(stream_name).limit(count).from(start).each.to_a
53
- EOW
54
- read.stream(stream_name).limit(count).from(start).each.to_a
55
- end
56
-
57
- def read_events_backward(stream_name, start: :head, count: page_size)
58
- warn <<~EOW
59
- RubyEventStore::Client#read_events_backward has been deprecated.
60
-
61
- Use following fluent API to receive exact results:
62
- client.read.stream(stream_name).limit(count).from(start).backward.each.to_a
63
- EOW
64
- read.stream(stream_name).limit(count).from(start).backward.each.to_a
65
- end
66
-
67
- def read_stream_events_forward(stream_name)
68
- warn <<~EOW
69
- RubyEventStore::Client#read_stream_events_forward has been deprecated.
70
-
71
- Use following fluent API to receive exact results:
72
- client.read.stream(stream_name).each.to_a
73
- EOW
74
- read.stream(stream_name).each.to_a
75
- end
76
-
77
- def read_stream_events_backward(stream_name)
78
- warn <<~EOW
79
- RubyEventStore::Client#read_stream_events_backward has been deprecated.
80
-
81
- Use following fluent API to receive exact results:
82
- client.read.stream(stream_name).backward.each.to_a
83
- EOW
84
- read.stream(stream_name).backward.each.to_a
85
- end
86
-
87
- def read_all_streams_forward(start: :head, count: page_size)
88
- warn <<~EOW
89
- RubyEventStore::Client#read_all_streams_forward has been deprecated.
90
-
91
- Use following fluent API to receive exact results:
92
- client.read.limit(count).from(start).each.to_a
93
- EOW
94
- read.limit(count).from(start).each.to_a
95
- end
96
-
97
- def read_all_streams_backward(start: :head, count: page_size)
98
- warn <<~EOW
99
- RubyEventStore::Client#read_all_streams_backward has been deprecated.
100
-
101
- Use following fluent API to receive exact results:
102
- client.read.limit(count).from(start).backward.each.to_a
103
- EOW
104
- read.limit(count).from(start).backward.each.to_a
73
+ self
105
74
  end
106
75
 
107
- def read_event(event_id)
108
- deserialize_event(repository.read_event(event_id))
76
+ # Starts building a query specification for reading events.
77
+ # {http://railseventstore.org/docs/read/ More info.}
78
+ #
79
+ # @return [Specification]
80
+ def read
81
+ Specification.new(SpecificationReader.new(repository, mapper))
109
82
  end
110
83
 
111
- def read
112
- Specification.new(repository, mapper)
84
+ # Gets list of streams where event is stored or linked
85
+ #
86
+ # @return [Array<Stream>] where event is stored or linked
87
+ def streams_of(event_id)
88
+ repository.streams_of(event_id)
113
89
  end
114
90
 
115
- # subscribe(subscriber, to:)
116
- # subscribe(to:, &subscriber)
91
+ # Subscribes a handler (subscriber) that will be invoked for published events of provided type.
92
+ #
93
+ # @overload subscribe(subscriber, to:)
94
+ # @param to [Array<Class>] types of events to subscribe
95
+ # @param subscriber [Object, Class] handler
96
+ # @return [Proc] - unsubscribe proc. Call to unsubscribe.
97
+ # @raise [ArgumentError, SubscriberNotExist]
98
+ # @overload subscribe(to:, &subscriber)
99
+ # @param to [Array<Class>] types of events to subscribe
100
+ # @param subscriber [Proc] handler
101
+ # @return [Proc] - unsubscribe proc. Call to unsubscribe.
102
+ # @raise [ArgumentError, SubscriberNotExist]
117
103
  def subscribe(subscriber = nil, to:, &proc)
118
104
  raise ArgumentError, "subscriber must be first argument or block, cannot be both" if subscriber && proc
119
- raise SubscriberNotExist, "subscriber must be first argument or block" unless subscriber || proc
120
105
  subscriber ||= proc
121
- event_broker.add_subscriber(subscriber, to)
106
+ broker.add_subscription(subscriber, to)
122
107
  end
123
108
 
124
- # subscribe_to_all_events(subscriber)
125
- # subscribe_to_all_events(&subscriber)
109
+ # Subscribes a handler (subscriber) that will be invoked for all published events
110
+ #
111
+ # @overload subscribe_to_all_events(subscriber)
112
+ # @param subscriber [Object, Class] handler
113
+ # @return [Proc] - unsubscribe proc. Call to unsubscribe.
114
+ # @raise [ArgumentError, SubscriberNotExist]
115
+ # @overload subscribe_to_all_events(&subscriber)
116
+ # @param subscriber [Proc] handler
117
+ # @return [Proc] - unsubscribe proc. Call to unsubscribe.
118
+ # @raise [ArgumentError, SubscriberNotExist]
126
119
  def subscribe_to_all_events(subscriber = nil, &proc)
127
120
  raise ArgumentError, "subscriber must be first argument or block, cannot be both" if subscriber && proc
128
- raise SubscriberNotExist, "subscriber must be first argument or block" unless subscriber || proc
129
- event_broker.add_global_subscriber(subscriber || proc)
121
+ broker.add_global_subscription(subscriber || proc)
130
122
  end
131
123
 
124
+ # Builder object for collecting temporary handlers (subscribers)
125
+ # which are active only during the invocation of the provided
126
+ # block of code.
132
127
  class Within
133
- def initialize(block, event_broker)
128
+ def initialize(block, broker)
134
129
  @block = block
135
- @event_broker = event_broker
130
+ @broker = broker
136
131
  @global_subscribers = []
137
132
  @subscribers = Hash.new {[]}
138
133
  end
139
134
 
135
+ # Subscribes temporary handlers that
136
+ # will be called for all published events.
137
+ # The subscription is active only during the invocation
138
+ # of the block of code provided to {Client#within}.
139
+ # {http://railseventstore.org/docs/subscribe/#temporary-subscriptions Read more.}
140
+ #
141
+ # @param handlers [Object, Class] handlers passed as objects or classes
142
+ # @param handler2 [Proc] handler passed as proc
143
+ # @return [self]
140
144
  def subscribe_to_all_events(*handlers, &handler2)
141
145
  handlers << handler2 if handler2
142
146
  @global_subscribers += handlers
143
147
  self
144
148
  end
145
149
 
150
+ # Subscribes temporary handlers that
151
+ # will be called for published events of provided type.
152
+ # The subscription is active only during the invocation
153
+ # of the block of code provided to {Client#within}.
154
+ # {http://railseventstore.org/docs/subscribe/#temporary-subscriptions Read more.}
155
+ #
156
+ # @overload subscribe(handler, to:)
157
+ # @param handler [Object, Class] handler passed as objects or classes
158
+ # @param to [Array<Class>] types of events to subscribe
159
+ # @return [self]
160
+ # @overload subscribe(to:, &handler)
161
+ # @param to [Array<Class>] types of events to subscribe
162
+ # @param handler [Proc] handler passed as proc
163
+ # @return [self]
146
164
  def subscribe(handler=nil, to:, &handler2)
147
165
  raise ArgumentError if handler && handler2
148
- @subscribers[handler || handler2] += normalize_to_array(to)
166
+ @subscribers[handler || handler2] += Array(to)
149
167
  self
150
168
  end
151
169
 
170
+ # Invokes the block of code provided to {Client#within}
171
+ # and then unsubscribes temporary handlers.
172
+ # {http://railseventstore.org/docs/subscribe/#temporary-subscriptions Read more.}
173
+ #
174
+ # @return [Object] value returned by the invoked block of code
152
175
  def call
153
176
  unsubs = add_thread_global_subscribers
154
177
  unsubs += add_thread_subscribers
155
178
  @block.call
156
179
  ensure
157
- unsubs.each(&:call)
180
+ unsubs.each(&:call) if unsubs
158
181
  end
159
182
 
160
183
  private
161
184
 
162
185
  def add_thread_subscribers
163
- @subscribers.map do |handler, types|
164
- @event_broker.add_thread_subscriber(handler, types)
186
+ @subscribers.map do |subscriber, types|
187
+ @broker.add_thread_subscription(subscriber, types)
165
188
  end
166
189
  end
167
190
 
168
191
  def add_thread_global_subscribers
169
- @global_subscribers.map do |s|
170
- @event_broker.add_thread_global_subscriber(s)
192
+ @global_subscribers.map do |subscriber|
193
+ @broker.add_thread_global_subscription(subscriber)
171
194
  end
172
195
  end
173
-
174
- def normalize_to_array(objs)
175
- return *objs
176
- end
177
196
  end
178
197
 
198
+ # Use for starting temporary subscriptions.
199
+ # {http://railseventstore.org/docs/subscribe/#temporary-subscriptions Read more}
200
+ #
201
+ # @param block [Proc] block of code during which the temporary subscriptions will be active
202
+ # @return [Within] builder object which collects temporary subscriptions
179
203
  def within(&block)
180
204
  raise ArgumentError if block.nil?
181
- Within.new(block, event_broker)
205
+ Within.new(block, broker)
182
206
  end
183
207
 
208
+ # Set additional metadata for all events published within the provided block
209
+ # {http://railseventstore.org/docs/request_metadata#passing-your-own-metadata-using-with_metadata-method Read more}
210
+ #
211
+ # @param metadata [Hash] metadata to set for events
212
+ # @param block [Proc] block of code during which the metadata will be added
213
+ # @return [Object] last value returned by the provided block
184
214
  def with_metadata(metadata, &block)
185
215
  previous_metadata = metadata()
186
- self.metadata = (previous_metadata || {}).merge(metadata)
216
+ self.metadata = previous_metadata.merge(metadata)
187
217
  block.call if block_given?
188
218
  ensure
189
219
  self.metadata = previous_metadata
190
220
  end
191
221
 
222
+ # Deserialize event which was serialized for async event handlers
223
+ # {http://railseventstore.org/docs/subscribe/#async-handlers Read more}
224
+ #
225
+ # @return [Event, Proto] deserialized event
226
+ def deserialize(event_type:, event_id:, data:, metadata:)
227
+ mapper.serialized_record_to_event(SerializedRecord.new(event_type: event_type, event_id: event_id, data: data, metadata: metadata))
228
+ end
229
+
230
+ # Read additional metadata which will be added for published events
231
+ # {http://railseventstore.org/docs/request_metadata#passing-your-own-metadata-using-with_metadata-method Read more}
232
+ #
233
+ # @return [Hash]
234
+ def metadata
235
+ @metadata.value || EMPTY_HASH
236
+ end
237
+
238
+ # Overwrite existing event(s) with the same ID.
239
+ #
240
+ # Does not notify any subscribed handlers.
241
+ # Does not enrich with additional current metadata.
242
+ # Does not allow changing which streams these events are in.
243
+ # {http://railseventstore.org/docs/migrating_messages Read more}
244
+ #
245
+ # @example Add data and metadata to existing events
246
+ #
247
+ # events = event_store.read.limit(10).to_a
248
+ # events.each do |ev|
249
+ # ev.data[:tenant_id] = 1
250
+ # ev.metadata[:server_id] = "eu-west-2"
251
+ # end
252
+ # event_store.overwrite(events)
253
+ #
254
+ # @example Change event type
255
+ #
256
+ # events = event_store.read.limit(10).each.select{|ev| OldType === ev }.map do |ev|
257
+ # NewType.new(
258
+ # event_id: ev.event_id,
259
+ # data: ev.data,
260
+ # metadata: ev.metadata,
261
+ # )
262
+ # end
263
+ # event_store.overwrite(events)
264
+ #
265
+ # @param events [Array<Event, Proto>, Event, Proto] event(s) to serialize and overwrite again
266
+ # @return [self]
267
+ def overwrite(events_or_event)
268
+ events = Array(events_or_event)
269
+ serialized_events = serialize_events(events)
270
+ repository.update_messages(serialized_events)
271
+ self
272
+ end
273
+
274
+ def inspect
275
+ "#<#{self.class}:0x#{__id__.to_s(16)}>"
276
+ end
277
+
278
+ EMPTY_HASH = {}.freeze
279
+ private_constant :EMPTY_HASH
280
+
192
281
  private
193
282
 
194
- def serialized_events(events)
283
+ def serialize_events(events)
195
284
  events.map do |ev|
196
285
  mapper.event_to_serialized_record(ev)
197
286
  end
198
287
  end
199
288
 
200
- def deserialize_event(sev)
201
- mapper.serialized_record_to_event(sev)
202
- end
203
-
204
- def normalize_to_array(events)
205
- return *events
289
+ def enrich_events_metadata(events)
290
+ events = Array(events)
291
+ events.each{|event| enrich_event_metadata(event) }
292
+ events
206
293
  end
207
294
 
208
295
  def enrich_event_metadata(event)
209
- if metadata_proc
210
- md = metadata_proc.call || {}
211
- md.each{|k,v| event.metadata[k]=(v) }
212
- end
213
- if metadata
214
- metadata.each { |key, value| event.metadata[key] = value }
215
- end
216
- event.metadata[:timestamp] ||= clock.call
296
+ metadata.each { |key, value| event.metadata[key] ||= value }
297
+ event.metadata[:timestamp] ||= clock.call
298
+ event.metadata[:correlation_id] ||= correlation_id_generator.call
217
299
  end
218
300
 
219
- attr_reader :repository, :mapper, :event_broker, :clock, :metadata_proc, :page_size
301
+ def append_to_stream_serialized_events(serialized_events, stream_name:, expected_version:)
302
+ repository.append_to_stream(serialized_events, Stream.new(stream_name), ExpectedVersion.new(expected_version))
303
+ end
220
304
 
221
305
  protected
222
306
 
223
- def metadata
224
- Thread.current["ruby_event_store_#{hash}"]
307
+ def metadata=(value)
308
+ @metadata.value = value
225
309
  end
226
310
 
227
- def metadata=(value)
228
- Thread.current["ruby_event_store_#{hash}"] = value
311
+ def default_clock
312
+ ->{ Time.now.utc }
229
313
  end
314
+
315
+ def default_correlation_id_generator
316
+ ->{ SecureRandom.uuid }
317
+ end
318
+
319
+ attr_reader :repository, :mapper, :broker, :clock, :correlation_id_generator
230
320
  end
231
321
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyEventStore
4
+ class ComposedDispatcher
5
+ def initialize(*dispatchers)
6
+ @dispatchers = dispatchers
7
+ end
8
+
9
+ def call(subscriber, event, serialized_event)
10
+ @dispatchers.each do |dispatcher|
11
+ if dispatcher.verify(subscriber)
12
+ dispatcher.call(subscriber, event, serialized_event)
13
+ break
14
+ end
15
+ end
16
+ end
17
+
18
+ def verify(subscriber)
19
+ @dispatchers.any? do |dispatcher|
20
+ dispatcher.verify(subscriber)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RubyEventStore
2
4
  GLOBAL_STREAM = Object.new
3
5
  PAGE_SIZE = 100.freeze