|
2 | 2 |
|
3 | 3 | module RubyEventStore
|
4 | 4 | class Projection
|
5 |
| - private_class_method :new |
| 5 | + ANONYMOUS_CLASS = "#<Class:".freeze |
6 | 6 |
|
7 |
| - def self.from_stream(stream_or_streams) |
8 |
| - streams = Array(stream_or_streams) |
9 |
| - raise(ArgumentError, "At least one stream must be given") if streams.empty? |
10 |
| - new(streams: streams) |
11 |
| - end |
12 |
| - |
13 |
| - def self.from_all_streams |
14 |
| - new |
15 |
| - end |
16 |
| - |
17 |
| - def initialize(streams: []) |
18 |
| - @streams = streams |
| 7 | + def initialize(initial_state = nil) |
19 | 8 | @handlers = {}
|
20 |
| - @init = -> { {} } |
| 9 | + @init = -> { initial_state } |
21 | 10 | end
|
22 | 11 |
|
23 |
| - attr_reader :streams, :handlers |
24 |
| - |
25 |
| - def init(handler) |
26 |
| - @init = handler |
27 |
| - self |
28 |
| - end |
| 12 | + def on(*event_klasses, &block) |
| 13 | + raise(ArgumentError, 'No handler block given') unless block_given? |
29 | 14 |
|
30 |
| - def when(events, handler) |
31 |
| - Array(events).each { |event| handlers[event.to_s] = handler } |
| 15 | + event_klasses.each do |event_klass| |
| 16 | + name = event_klass.to_s |
| 17 | + raise(ArgumentError, 'Anonymous class is missing name') if name.start_with? ANONYMOUS_CLASS |
32 | 18 |
|
| 19 | + @handlers[name] = ->(state, event) { block.call(state, event) } |
| 20 | + end |
33 | 21 | self
|
34 | 22 | end
|
35 | 23 |
|
36 |
| - def initial_state |
37 |
| - @init.call |
38 |
| - end |
39 |
| - |
40 |
| - def current_state |
41 |
| - @current_state ||= initial_state |
42 |
| - end |
43 |
| - |
44 |
| - def call(event) |
45 |
| - handlers.fetch(event.event_type).(current_state, event) |
46 |
| - end |
47 |
| - |
48 |
| - def handled_events |
49 |
| - handlers.keys |
50 |
| - end |
51 |
| - |
52 |
| - def run(event_store, start: nil, count: PAGE_SIZE) |
| 24 | + def call(*scopes) |
53 | 25 | return initial_state if handled_events.empty?
|
54 |
| - streams.any? ? reduce_from_streams(event_store, start, count) : reduce_from_all_streams(event_store, start, count) |
55 |
| - end |
56 |
| - |
57 |
| - private |
58 | 26 |
|
59 |
| - def valid_starting_point?(start) |
60 |
| - return true unless start |
61 |
| - streams.any? ? (start.instance_of?(Array) && start.size === streams.size) : start.instance_of?(String) |
| 27 | + scopes.reduce(initial_state) do |state, scope| |
| 28 | + scope.of_types(handled_events).reduce(state, &method(:transition)) |
| 29 | + end |
62 | 30 | end
|
63 | 31 |
|
64 |
| - def reduce_from_streams(event_store, start, count) |
65 |
| - raise ArgumentError.new("Start must be an array with event ids") unless valid_starting_point?(start) |
66 |
| - streams |
67 |
| - .zip(start_events(start)) |
68 |
| - .reduce(initial_state) do |state, (stream_name, start_event_id)| |
69 |
| - read_scope(event_store, stream_name, count, start_event_id).reduce(state, &method(:transition)) |
70 |
| - end |
71 |
| - end |
72 |
| - |
73 |
| - def reduce_from_all_streams(event_store, start, count) |
74 |
| - raise ArgumentError.new("Start must be valid event id") unless valid_starting_point?(start) |
75 |
| - read_scope(event_store, nil, count, start).reduce(initial_state, &method(:transition)) |
76 |
| - end |
| 32 | + private |
77 | 33 |
|
78 |
| - def read_scope(event_store, stream, count, start) |
79 |
| - scope = event_store.read.in_batches(count) |
80 |
| - scope = scope.of_type(handled_events) |
81 |
| - scope = scope.stream(stream) if stream |
82 |
| - scope = scope.from(start) if start |
83 |
| - scope |
| 34 | + def initial_state |
| 35 | + @init.call |
84 | 36 | end
|
85 | 37 |
|
86 |
| - def start_events(start) |
87 |
| - start ? start : Array.new |
| 38 | + def handled_events |
| 39 | + @handlers.keys |
88 | 40 | end
|
89 | 41 |
|
90 | 42 | def transition(state, event)
|
91 |
| - handlers.fetch(event.event_type).call(state, event) |
92 |
| - state |
| 43 | + @handlers.fetch(event.event_type).call(state, event) |
93 | 44 | end
|
94 | 45 | end
|
95 | 46 | end
|
0 commit comments