replay 0.0.1 → 0.1.0

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/.rvmrc +1 -1
  3. data/Gemfile +7 -3
  4. data/Guardfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +153 -0
  7. data/Rakefile +15 -0
  8. data/lib/replay.rb +39 -10
  9. data/lib/replay/backends.rb +50 -0
  10. data/lib/replay/event_declarations.rb +36 -0
  11. data/lib/replay/event_decorator.rb +13 -0
  12. data/lib/replay/events.rb +24 -0
  13. data/lib/replay/inflector.rb +55 -0
  14. data/lib/replay/observer.rb +18 -0
  15. data/lib/replay/publisher.rb +72 -0
  16. data/lib/replay/repository.rb +61 -0
  17. data/lib/replay/repository/configuration.rb +30 -0
  18. data/lib/replay/repository/identity_map.rb +25 -0
  19. data/lib/replay/router.rb +5 -0
  20. data/lib/replay/router/default_router.rb +21 -0
  21. data/lib/replay/rspec.rb +50 -0
  22. data/lib/replay/subscription_manager.rb +28 -0
  23. data/lib/replay/test.rb +64 -0
  24. data/lib/replay/test/test_event_stream.rb +19 -0
  25. data/lib/replay/version.rb +1 -1
  26. data/proofs/all.rb +7 -0
  27. data/proofs/proofs_init.rb +10 -0
  28. data/proofs/replay/inflector_proof.rb +32 -0
  29. data/proofs/replay/publisher_proof.rb +170 -0
  30. data/proofs/replay/repository_configuration_proof.rb +67 -0
  31. data/proofs/replay/repository_proof.rb +46 -0
  32. data/proofs/replay/subscriber_manager_proof.rb +39 -0
  33. data/proofs/replay/test_proof.rb +28 -0
  34. data/replay.gemspec +5 -4
  35. data/test/replay/observer_spec.rb +37 -0
  36. data/test/replay/router/default_router_spec.rb +43 -0
  37. data/test/test_helper.rb +10 -0
  38. metadata +65 -48
  39. data/README +0 -27
  40. data/lib/replay/active_record_event_store.rb +0 -32
  41. data/lib/replay/domain.rb +0 -33
  42. data/lib/replay/event.rb +0 -27
  43. data/lib/replay/event_store.rb +0 -55
  44. data/lib/replay/projector.rb +0 -19
  45. data/lib/replay/test_storage.rb +0 -8
  46. data/lib/replay/unknown_event_error.rb +0 -2
  47. data/test/spec_helper.rb +0 -6
  48. data/test/test_events.sqlite3 +0 -0
  49. data/test/unit/active_record_event_store_spec.rb +0 -24
  50. data/test/unit/domain_spec.rb +0 -53
  51. data/test/unit/event_spec.rb +0 -13
  52. data/test/unit/event_store_spec.rb +0 -28
  53. 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 "activerecord"
23
- s.add_development_dependency "sqlite3-ruby"
24
- # s.add_runtime_dependency "rest-client"
25
- s.add_runtime_dependency "activesupport"
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
+