replay 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rvmrc +1 -1
- data/Gemfile +7 -3
- data/Guardfile +10 -0
- data/LICENSE +21 -0
- data/README.md +153 -0
- data/Rakefile +15 -0
- data/lib/replay.rb +39 -10
- data/lib/replay/backends.rb +50 -0
- data/lib/replay/event_declarations.rb +36 -0
- data/lib/replay/event_decorator.rb +13 -0
- data/lib/replay/events.rb +24 -0
- data/lib/replay/inflector.rb +55 -0
- data/lib/replay/observer.rb +18 -0
- data/lib/replay/publisher.rb +72 -0
- data/lib/replay/repository.rb +61 -0
- data/lib/replay/repository/configuration.rb +30 -0
- data/lib/replay/repository/identity_map.rb +25 -0
- data/lib/replay/router.rb +5 -0
- data/lib/replay/router/default_router.rb +21 -0
- data/lib/replay/rspec.rb +50 -0
- data/lib/replay/subscription_manager.rb +28 -0
- data/lib/replay/test.rb +64 -0
- data/lib/replay/test/test_event_stream.rb +19 -0
- data/lib/replay/version.rb +1 -1
- data/proofs/all.rb +7 -0
- data/proofs/proofs_init.rb +10 -0
- data/proofs/replay/inflector_proof.rb +32 -0
- data/proofs/replay/publisher_proof.rb +170 -0
- data/proofs/replay/repository_configuration_proof.rb +67 -0
- data/proofs/replay/repository_proof.rb +46 -0
- data/proofs/replay/subscriber_manager_proof.rb +39 -0
- data/proofs/replay/test_proof.rb +28 -0
- data/replay.gemspec +5 -4
- data/test/replay/observer_spec.rb +37 -0
- data/test/replay/router/default_router_spec.rb +43 -0
- data/test/test_helper.rb +10 -0
- metadata +65 -48
- data/README +0 -27
- data/lib/replay/active_record_event_store.rb +0 -32
- data/lib/replay/domain.rb +0 -33
- data/lib/replay/event.rb +0 -27
- data/lib/replay/event_store.rb +0 -55
- data/lib/replay/projector.rb +0 -19
- data/lib/replay/test_storage.rb +0 -8
- data/lib/replay/unknown_event_error.rb +0 -2
- data/test/spec_helper.rb +0 -6
- data/test/test_events.sqlite3 +0 -0
- data/test/unit/active_record_event_store_spec.rb +0 -24
- data/test/unit/domain_spec.rb +0 -53
- data/test/unit/event_spec.rb +0 -13
- data/test/unit/event_store_spec.rb +0 -28
- data/test/unit/projector_spec.rb +0 -19
@@ -0,0 +1,170 @@
|
|
1
|
+
require_relative "../proofs_init.rb"
|
2
|
+
require 'replay/test'
|
3
|
+
|
4
|
+
class ReplayTest
|
5
|
+
include Replay::Publisher
|
6
|
+
include Replay::EventExaminer
|
7
|
+
|
8
|
+
key :pkey
|
9
|
+
|
10
|
+
def initialize(pkey = 1)
|
11
|
+
@pkey = pkey
|
12
|
+
end
|
13
|
+
|
14
|
+
events do
|
15
|
+
SomeEvent(pid: Integer)
|
16
|
+
UnhandledEvent(pid: Integer)
|
17
|
+
end
|
18
|
+
|
19
|
+
apply SomeEvent do |event|
|
20
|
+
@event_count ||= 0
|
21
|
+
@event_applied = event.pid
|
22
|
+
@event_count += 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def pkey
|
26
|
+
@pkey
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module ReplayTest::Proof
|
31
|
+
def sets_publish_time
|
32
|
+
publish SomeEvent(pid: 123)
|
33
|
+
events.last.published_at != nil && (Time.now - events.last.published_at) < 20
|
34
|
+
end
|
35
|
+
|
36
|
+
def published_at_not_considered_in_equality
|
37
|
+
event = SomeEvent(pid: 123)
|
38
|
+
event == event.with(:published_at => Time.now-100)
|
39
|
+
end
|
40
|
+
|
41
|
+
def defines_events?
|
42
|
+
self.class.const_defined?(:SomeEvent) && self.class.const_get(:SomeEvent).is_a?(Class)
|
43
|
+
end
|
44
|
+
def adds_convenience_method?
|
45
|
+
respond_to? :SomeEvent
|
46
|
+
end
|
47
|
+
def applies_event?(event)
|
48
|
+
apply(event)
|
49
|
+
@event_applied == event.pid
|
50
|
+
end
|
51
|
+
|
52
|
+
def applies_events?(events)
|
53
|
+
apply(events)
|
54
|
+
@event_count == events.count
|
55
|
+
end
|
56
|
+
|
57
|
+
def throws_unhandled_event?(raise_it = true)
|
58
|
+
begin
|
59
|
+
apply(UnhandledEvent(pid: 123), raise_it)
|
60
|
+
rescue Replay::UnhandledEventError => e
|
61
|
+
true && raise_it
|
62
|
+
rescue Exception
|
63
|
+
!raise_it || false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def can_publish_events?
|
68
|
+
event = SomeEvent(pid: 123)
|
69
|
+
publish(event)
|
70
|
+
events.detect{|e| e==event}
|
71
|
+
end
|
72
|
+
|
73
|
+
def subscribers_receive_events
|
74
|
+
sub = Class.new do
|
75
|
+
def published(stream, event)
|
76
|
+
@published = true
|
77
|
+
end
|
78
|
+
def published?; @published; end
|
79
|
+
end.new
|
80
|
+
add_subscriber(sub)
|
81
|
+
publish(ReplayTest::SomeEvent.new(pid: 123))
|
82
|
+
sub.published?
|
83
|
+
end
|
84
|
+
def subscribes()
|
85
|
+
sub = Class.new do
|
86
|
+
def published(stream, event)
|
87
|
+
@published = true
|
88
|
+
end
|
89
|
+
def published?; @published; end
|
90
|
+
end.new
|
91
|
+
add_subscriber(sub)
|
92
|
+
has_subscriber?(sub)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
title "Publisher"
|
97
|
+
|
98
|
+
proof "Defines events given in the events block" do
|
99
|
+
r = ReplayTest.new
|
100
|
+
r.prove{ defines_events? }
|
101
|
+
end
|
102
|
+
|
103
|
+
proof "Adds a convenience method for an event constructor to the class" do
|
104
|
+
r = ReplayTest.new
|
105
|
+
r.prove{ adds_convenience_method? }
|
106
|
+
end
|
107
|
+
|
108
|
+
proof "Applies events singly" do
|
109
|
+
r = ReplayTest.new
|
110
|
+
r.prove{ applies_event? ReplayTest::SomeEvent.new(:pid => 123)}
|
111
|
+
end
|
112
|
+
|
113
|
+
proof "Applies events in ordered batches" do
|
114
|
+
r = ReplayTest.new
|
115
|
+
r.prove do applies_events?([
|
116
|
+
ReplayTest::SomeEvent.new(:pid => 123),
|
117
|
+
ReplayTest::SomeEvent.new(:pid => 234),
|
118
|
+
ReplayTest::SomeEvent.new(:pid => 456)
|
119
|
+
])
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
proof "Throws an UnhandledEventError for unhandled events" do
|
124
|
+
r = ReplayTest.new
|
125
|
+
r.prove{ throws_unhandled_event? }
|
126
|
+
end
|
127
|
+
|
128
|
+
proof "Ignores unhandled events if requested" do
|
129
|
+
r = ReplayTest.new
|
130
|
+
r.prove{ throws_unhandled_event? false}
|
131
|
+
end
|
132
|
+
|
133
|
+
proof "Can publish events to the indicated stream" do
|
134
|
+
r = ReplayTest.new
|
135
|
+
r.prove { can_publish_events? }
|
136
|
+
end
|
137
|
+
|
138
|
+
proof "Subscriber can subscribe to events from publisher" do
|
139
|
+
r = ReplayTest.new
|
140
|
+
r.prove{ subscribes }
|
141
|
+
end
|
142
|
+
|
143
|
+
proof "Subscriber receives published events" do
|
144
|
+
r = ReplayTest.new
|
145
|
+
r.prove{ subscribers_receive_events }
|
146
|
+
end
|
147
|
+
|
148
|
+
proof "Returns self from apply" do
|
149
|
+
r = ReplayTest.new
|
150
|
+
r.prove{ apply([]) == self}
|
151
|
+
end
|
152
|
+
proof "Returns self from publish" do
|
153
|
+
r = ReplayTest.new
|
154
|
+
r.prove{ publish([]) == self}
|
155
|
+
end
|
156
|
+
|
157
|
+
proof "sets the publish time on events" do
|
158
|
+
r = ReplayTest.new
|
159
|
+
r.prove{ sets_publish_time }
|
160
|
+
end
|
161
|
+
|
162
|
+
proof "publish time is not part of equality" do
|
163
|
+
r = ReplayTest.new
|
164
|
+
r.prove{ published_at_not_considered_in_equality}
|
165
|
+
end
|
166
|
+
|
167
|
+
proof "Can implement initializer with arguments" do
|
168
|
+
r = ReplayTest.new(:foo)
|
169
|
+
r.prove { pkey == :foo }
|
170
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative "../proofs_init.rb"
|
2
|
+
require 'replay/test'
|
3
|
+
|
4
|
+
class Subscriber
|
5
|
+
def published(stream_id, event); end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Replay::Repository::Configuration::Proof
|
9
|
+
def can_configure_store?
|
10
|
+
self.store = :memory
|
11
|
+
self.store == Replay::Backends::MemoryStore
|
12
|
+
end
|
13
|
+
|
14
|
+
def can_add_default_subscriber?
|
15
|
+
sub = Subscriber.new
|
16
|
+
self.add_default_subscriber sub
|
17
|
+
subscribers.include? sub
|
18
|
+
end
|
19
|
+
|
20
|
+
def subscribers_include_store
|
21
|
+
self.store = :memory
|
22
|
+
self.subscribers.include? Replay::Backends::MemoryStore
|
23
|
+
end
|
24
|
+
|
25
|
+
def requires_store_to_act_like_subscriber
|
26
|
+
begin
|
27
|
+
store = Class.new do
|
28
|
+
def self.event_stream(stream); end
|
29
|
+
end
|
30
|
+
self.store = store
|
31
|
+
rescue Replay::InvalidSubscriberError => e
|
32
|
+
return true
|
33
|
+
rescue Exception => e
|
34
|
+
end
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def requires_store_to_load_events
|
39
|
+
begin
|
40
|
+
self.store = Subscriber.new
|
41
|
+
rescue Replay::InvalidStorageError
|
42
|
+
return true
|
43
|
+
rescue Exception => e
|
44
|
+
end
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
proof "can configure a store" do
|
50
|
+
Replay::Repository::Configuration.new.prove {can_configure_store?}
|
51
|
+
end
|
52
|
+
|
53
|
+
proof 'adding a store adds it as a subscriber' do
|
54
|
+
Replay::Repository::Configuration.new.prove { subscribers_include_store }
|
55
|
+
end
|
56
|
+
|
57
|
+
proof "can configure a default_subscriber" do
|
58
|
+
Replay::Repository::Configuration.new.prove {can_add_default_subscriber?}
|
59
|
+
end
|
60
|
+
|
61
|
+
proof "raises error if store won't load events" do
|
62
|
+
Replay::Repository::Configuration.new.prove {requires_store_to_load_events }
|
63
|
+
end
|
64
|
+
|
65
|
+
proof "raises error if store won't act like a subscriber" do
|
66
|
+
Replay::Repository::Configuration.new.prove {requires_store_to_act_like_subscriber }
|
67
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "../proofs_init.rb"
|
2
|
+
require 'replay/test'
|
3
|
+
|
4
|
+
class Subscriber
|
5
|
+
def published(stream_id, event); end
|
6
|
+
end
|
7
|
+
|
8
|
+
class RepositoryTest
|
9
|
+
include Replay::Repository
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :proven
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.can_be_configured
|
17
|
+
self.configure do |config|
|
18
|
+
self.proven = true
|
19
|
+
end
|
20
|
+
|
21
|
+
self.proven
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
title "Repository interface"
|
26
|
+
|
27
|
+
proof "repository can be configured" do
|
28
|
+
RepositoryTest.prove {can_be_configured }
|
29
|
+
end
|
30
|
+
|
31
|
+
Proof::Output.class_exec do
|
32
|
+
writer :pending, :level => :info do |text|
|
33
|
+
prefix :pending, "PENDING: #{text}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def pending(text = "pending test")
|
38
|
+
Proof::Output.pending "pending test"
|
39
|
+
end
|
40
|
+
proof "loads an instance of supplied class" do; pending; end
|
41
|
+
proof "load raises an error if event stream isn't found" do; pending; end
|
42
|
+
proof "load returns uncreated instance when :create is pending" do; false; end
|
43
|
+
proof "load returns a created instance when :create is true" do; pending; end
|
44
|
+
|
45
|
+
proof "reload returns the supplied instance in its current state" do; pending; end
|
46
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative "../proofs_init.rb"
|
2
|
+
require 'replay/test'
|
3
|
+
|
4
|
+
module Replay::SubscriptionManager::Proof
|
5
|
+
def only_adds_legit_subs
|
6
|
+
begin
|
7
|
+
add_subscriber(Object.new)
|
8
|
+
rescue Replay::InvalidSubscriberError => e
|
9
|
+
return true
|
10
|
+
end
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def sub_gets_notified
|
15
|
+
sub = Class.new do
|
16
|
+
attr_accessor :stream, :event
|
17
|
+
def published(stream, event)
|
18
|
+
self.stream = stream
|
19
|
+
self.event = event
|
20
|
+
end
|
21
|
+
end.new
|
22
|
+
add_subscriber(sub)
|
23
|
+
notify_subscribers "123", "456"
|
24
|
+
sub.stream == '123' && sub.event == '456'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
title "Subscription Manager"
|
29
|
+
|
30
|
+
proof "raises InvalidSubscriberError when subscriber fails to implement #published" do
|
31
|
+
sm = Replay::SubscriptionManager.new
|
32
|
+
sm.prove { only_adds_legit_subs }
|
33
|
+
end
|
34
|
+
|
35
|
+
proof "notifies proper subscriber when informed" do
|
36
|
+
sm = Replay::SubscriptionManager.new
|
37
|
+
sm.prove { sub_gets_notified }
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative "../proofs_init.rb"
|
2
|
+
require 'replay/test'
|
3
|
+
|
4
|
+
desc "proof for the test support provided by replay"
|
5
|
+
|
6
|
+
title "Test support"
|
7
|
+
|
8
|
+
proof "fuzzy matching of events" do
|
9
|
+
class TestEvent
|
10
|
+
include Replay::EventDecorator
|
11
|
+
|
12
|
+
values do
|
13
|
+
attribute :one, String
|
14
|
+
attribute :two, String
|
15
|
+
end
|
16
|
+
|
17
|
+
def matches_fuzzy(event)
|
18
|
+
self.kind_of_matches?(event)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
e1 = TestEvent.new(one: '1')
|
23
|
+
e2 = TestEvent.new(one: '1', two: '2')
|
24
|
+
|
25
|
+
e1.prove{ matches_fuzzy(e2)}
|
26
|
+
e2.prove{ !matches_fuzzy(e1)}
|
27
|
+
|
28
|
+
end
|
data/replay.gemspec
CHANGED
@@ -19,8 +19,9 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
# specify any dependencies here; for example:
|
22
|
-
s.add_development_dependency "
|
23
|
-
s.add_development_dependency "
|
24
|
-
|
25
|
-
s.add_runtime_dependency "
|
22
|
+
s.add_development_dependency "bundler", "~>1.3"
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "minitest"
|
25
|
+
#s.add_runtime_dependency "rest-client"
|
26
|
+
s.add_runtime_dependency "virtus", "~>1.0.0"
|
26
27
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ObservedEvent
|
4
|
+
end
|
5
|
+
|
6
|
+
class UnobservedEvent
|
7
|
+
end
|
8
|
+
|
9
|
+
class ObserverTest
|
10
|
+
include Replay::Observer
|
11
|
+
observe ObservedEvent do |stream, event|
|
12
|
+
@observed = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.observed?
|
16
|
+
@observed
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.reset
|
20
|
+
@observed=false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Replay::Observer do
|
25
|
+
before do
|
26
|
+
ObserverTest.reset
|
27
|
+
end
|
28
|
+
it "calls the observer block for observed events" do
|
29
|
+
ObserverTest.published('123', ObservedEvent.new)
|
30
|
+
ObserverTest.must_be :observed?
|
31
|
+
end
|
32
|
+
|
33
|
+
it "does not notify of unobserved events" do
|
34
|
+
ObserverTest.published('123', UnobservedEvent.new)
|
35
|
+
ObserverTest.wont_be :observed?
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Router
|
4
|
+
module Test
|
5
|
+
def has_observer?(object)
|
6
|
+
@subscription_manager.has_subscriber?(object)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
class TypedEvent
|
11
|
+
|
12
|
+
end
|
13
|
+
class Observer
|
14
|
+
def self.published(stream, event)
|
15
|
+
@typed=true
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.typed_received?
|
19
|
+
@typed
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Replay::Router::DefaultRouter.send(:include, Router::Test)
|
24
|
+
|
25
|
+
describe Replay::Router::DefaultRouter do
|
26
|
+
before do
|
27
|
+
@router = Replay::Router::DefaultRouter.instance
|
28
|
+
end
|
29
|
+
describe "adding observers" do
|
30
|
+
it "tracks the observing object" do
|
31
|
+
@router.add_observer(Observer)
|
32
|
+
assert(@router.has_observer?(Observer), "router does not track observer")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
describe "event publishing" do
|
36
|
+
it "tells the observing object about events being published" do
|
37
|
+
@router.add_observer(Observer)
|
38
|
+
@router.published("123", TypedEvent.new)
|
39
|
+
assert Observer.typed_received?, "Did not receive notification of event"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|