replay 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 084d0ba8e80a2b93566f0ba8e780feedccf92d28
4
- data.tar.gz: 7cf7c4e15f76ee0659cb0be60cac9a21d0fc2d6b
3
+ metadata.gz: d2ba86d3b1d0e597548c8fb5a807f0a5e9f98313
4
+ data.tar.gz: 940e276d8721b831afd454f55c2dd7d574d4ab5a
5
5
  SHA512:
6
- metadata.gz: e1561eba9d3b6469a38243a840e22dd850500a057f3b272f8ea67964680436867f40c57a892e07cf31aa44cdd519660d3f273f4bd657e0e304635120bec65d64
7
- data.tar.gz: ebd32484a7c18fa8c7dbbd77c49b3564bfaede2ee2c785c839e0a1af00d1dac5ee876ea57cc1f6659c6438f28013e1353aecd2b9abf80afe422389a7a9ab43eb
6
+ metadata.gz: 3a8d3363c8b40dc87669b5d99934a1321eaa789cce0b18520cf1f2bf9e8228368256a8842ff0f1dad0093b416dee0af2f067a80fb3c096e72373f5be81a4a2cf
7
+ data.tar.gz: 854dd7ae22f150b007853c8b3d39e2c697dbece4d2c7a421a1c93767243e983ed4bf2591217a4b5662c7d987b7e1ca38141bdf19d20e20c2aa4e82cb5f8a1783
@@ -32,6 +32,7 @@ require 'replay/inflector'
32
32
  require 'replay/events'
33
33
  require 'replay/event_decorator'
34
34
  require 'replay/event_declarations'
35
+ require 'replay/event_envelope'
35
36
  require 'replay/publisher'
36
37
  require 'replay/subscription_manager'
37
38
  require 'replay/subscriptions'
@@ -15,8 +15,8 @@ module Replay
15
15
  def initialize
16
16
  @store = {}
17
17
  end
18
- def self.published(stream_id, event)
19
- instance.published(stream_id, event)
18
+ def self.published(envelope)
19
+ instance.published(envelope)
20
20
  end
21
21
 
22
22
  def self.clear
@@ -27,9 +27,9 @@ module Replay
27
27
  @store = {}
28
28
  end
29
29
 
30
- def published(stream_id, event)
30
+ def published(envelope)
31
31
  @store[stream_id] ||= []
32
- @store[stream_id] << event
32
+ @store[stream_id] << envelope
33
33
  end
34
34
 
35
35
  def event_stream(stream_id)
@@ -1,10 +1,16 @@
1
1
  module Replay
2
2
  class Configuration
3
3
  attr_accessor :storage
4
+ attr_writer :reject_load_on_empty_stream
4
5
 
5
6
  def storage=(stores)
6
7
  stores = [stores] unless stores.is_a?(Array)
7
8
  @storage = stores
8
9
  end
10
+
11
+ def reject_load_on_empty_stream?
12
+ @reject_load_on_empty_stream ||= true
13
+ @reject_load_on_empty_stream
14
+ end
9
15
  end
10
- end
16
+ end
@@ -15,6 +15,7 @@ module Replay
15
15
  end
16
16
  end
17
17
  end
18
+
18
19
  def method_missing(name, *args)
19
20
  declare_event(self, name, args.first)
20
21
  end
@@ -22,13 +23,11 @@ module Replay
22
23
  def declare_event(base, name, props)
23
24
  klass = Class.new do
24
25
  include Replay::EventDecorator
25
- attribute :published_at, Time, default: lambda{|p,a| Time.now}
26
26
  values do
27
27
  props.keys.each do |prop|
28
28
  attribute prop, props[prop]
29
29
  end
30
30
  end
31
- include Virtus::Equalizer.new("#{name.to_s} equalizer", (self.attribute_set.map(&:name) - [:published_at]).map(&:to_s))
32
31
  end
33
32
  base.const_set name, klass
34
33
  end
@@ -4,8 +4,12 @@ module Replay
4
4
  def self.included(base)
5
5
  base.class_eval do
6
6
  include Virtus.value_object
7
+ attr_accessor :metadata
7
8
  def inspect
8
- "#{self.class.to_s}: #{self.attributes.map{|k, v| "#{k.to_s} = #{v.to_s}"}.join(", ")}"
9
+ "#{self.type}: #{self.attributes.map{|k, v| "#{k.to_s} = #{v.to_s}"}.join(", ")}"
10
+ end
11
+ def type
12
+ self.class.to_s
9
13
  end
10
14
  end
11
15
  end
@@ -0,0 +1,19 @@
1
+ module Replay
2
+ class EventEnvelope
3
+ attr_reader :stream_id, :event, :metadata
4
+ def initialize(stream_id, event, metadata = {})
5
+ @metadata = metadata
6
+ @event = event
7
+ @stream_id = stream_id
8
+ end
9
+
10
+ def type
11
+ @event.type
12
+ end
13
+
14
+ def method_missing(method, *args)
15
+ return @event.send(method, args) if @event.respond_to?(method)
16
+ super
17
+ end
18
+ end
19
+ end
@@ -21,7 +21,7 @@ module Replay
21
21
  end
22
22
 
23
23
  def published(stream_id, event)
24
- blk = @observer_blocks[Replay::Inflector.underscore(event.class.to_s)]
24
+ blk = @observer_blocks[Replay::Inflector.underscore(event.type)]
25
25
  blk.call(stream_id, event, binding) if blk
26
26
  end
27
27
  end
@@ -13,28 +13,36 @@ module Replay
13
13
  return apply([events], raise_unhandled) unless events.is_a?(Array)
14
14
 
15
15
  events.each do |event|
16
- blk = block_for(event.class)
17
- raise UnhandledEventError.new "event #{event.class.name} is not handled by #{self.class.name}" if (blk.nil? && raise_unhandled)
18
- self.instance_exec(event, &blk)
16
+ apply_method = apply_method_for(event.class)
17
+ raise UnhandledEventError.new "event #{event.type} is not handled by #{self.class.name}" if (!respond_to?(apply_method) && raise_unhandled)
18
+ self.send(apply_method, event)
19
19
  end
20
20
  return self
21
21
  end
22
22
 
23
- def block_for(event_type)
24
- self.class.block_for(event_type)
23
+ def apply_method_for(klass)
24
+ self.class.apply_method_for(klass)
25
25
  end
26
- protected :block_for
27
26
 
28
- def publish(event)
29
- apply(event)
30
- subscription_manager.notify_subscribers(to_stream_id, event)
27
+ private :apply_method_for
28
+
29
+ def publish(event, metadata={})
30
+ return publish([event]) unless event.is_a?(Array)
31
+ event.each do |evt|
32
+ metadata = ({:published_at => Time.now}.merge!(metadata))
33
+ apply(evt)
34
+ subscription_manager.notify_subscribers(to_stream_id, evt, metadata)
35
+ end
31
36
  return self
32
37
  end
33
38
 
34
-
35
39
  def to_stream_id
36
- raise Replay::UndefinedKeyError.new("No key attribute defined for #{self.class.to_s}") unless self.class.key_attr
37
- self.send(self.class.key_attr).to_s
40
+ raise Replay::UndefinedKeyError.new("No key attribute defined for #{self.type}") unless self.key_attr
41
+ self.send(self.key_attr).to_s
42
+ end
43
+
44
+ def key_attr
45
+ self.class.key_attr
38
46
  end
39
47
 
40
48
  module ClassMethods
@@ -47,17 +55,17 @@ module Replay
47
55
  end
48
56
 
49
57
  def apply(event_type, &block)
50
- @application_blocks[stringify_class(event_type)] = block
51
- end
52
-
53
- def block_for(event_type)
54
- blk = @application_blocks[stringify_class(event_type)]
55
- return blk
58
+ method_name = apply_method_for(event_type)
59
+ define_method method_name, block
56
60
  end
57
61
 
58
62
  def stringify_class(klass)
59
63
  Replay::Inflector.underscore(klass.to_s.dup)
60
64
  end
65
+
66
+ def apply_method_for(klass)
67
+ "handle_#{stringify_class(klass).gsub(".", "_")}"
68
+ end
61
69
  end
62
70
  end
63
71
  end
@@ -19,15 +19,15 @@ module Replay
19
19
  repository_load(klass, stream_id, options)
20
20
  end
21
21
 
22
- def repository_load(klass, stream_id, options={})
23
- events = store.event_stream(stream_id)
24
- if events.empty?
22
+ def repository_load(klass_or_instance, stream_id, options={})
23
+ stream = store.event_stream(stream_id)
24
+ if stream.empty? && configuration.reject_load_on_empty_stream?
25
25
  raise Errors::EventStreamNotFoundError.new("Could not find any events for stream identifier #{stream_id}") if options[:create].nil?
26
26
  end
27
27
 
28
- obj = prepare(klass.new)
29
- obj.create(stream_id) if options[:create] && events.empty?
30
- obj.apply(events)
28
+ obj = klass_or_instance.is_a?(Class) ? prepare(klass.new, options[:metadata]) : klass_or_instance
29
+ obj.create(stream_id) if options[:create] && stream.empty?
30
+ obj.apply(stream.map(&:event))
31
31
 
32
32
  obj
33
33
  end
@@ -40,7 +40,8 @@ module Replay
40
40
  new_obj
41
41
  end
42
42
 
43
- def prepare(obj)
43
+ def prepare(obj, metadata={})
44
+ obj.subscription_manager = SubscriptionManager.new(configuration.logger, metadata)
44
45
  @configuration.subscribers.each do |subscriber|
45
46
  obj.add_subscriber(subscriber)
46
47
  end
@@ -48,7 +49,7 @@ module Replay
48
49
  end
49
50
 
50
51
  def configure
51
- @configuration ||= Configuration.new
52
+ @configuration ||= Configuration.default
52
53
  yield @configuration
53
54
  @configuration
54
55
  end
@@ -1,8 +1,14 @@
1
1
  module Replay
2
2
  module Repository
3
3
  class Configuration
4
- def initialize
4
+ attr_accessor :logger
5
+ def initialize(logger = nil)
5
6
  @default_subscribers =[]
7
+ @logger = logger
8
+ end
9
+
10
+ def self.default
11
+ self.new(Replay.logger)
6
12
  end
7
13
 
8
14
  def add_default_subscriber(subscriber)
@@ -43,7 +43,7 @@ RSpec::Matchers.define :publish do |expected_event|
43
43
  end
44
44
 
45
45
  def event_interpretation(event)
46
- "#{event.class.to_s} [#{event.attributes.reject{|k,v| v.nil?}.keys.map{|k| "#{k.to_s} = #{event.attributes[k]}"}.join(", ")}]"
46
+ "#{event.type} [#{event.attributes.reject{|k,v| v.nil?}.keys.map{|k| "#{k.to_s} = #{event.attributes[k]}"}.join(", ")}]"
47
47
  end
48
48
 
49
49
  end
@@ -1,26 +1,32 @@
1
1
  module Replay
2
2
  class SubscriptionManager
3
-
4
- def initialize(logger = nil)
3
+ def initialize(logger = nil, session_metadata = {})
5
4
  @subscribers = []
6
5
  @logger = logger
6
+ @session_metadata = session_metadata
7
7
  end
8
8
 
9
9
  def add_subscriber(subscriber)
10
10
  if subscriber.respond_to?(:published)
11
- @subscribers << subscriber
11
+ @subscribers << subscriber
12
12
  else
13
13
  raise Replay::InvalidSubscriberError.new(subscriber)
14
14
  end
15
15
  end
16
16
 
17
- def notify_subscribers(stream_id, event)
17
+ def notify_subscribers(stream_id, event, metadata = {})
18
18
  @subscribers.each do |sub|
19
19
  begin
20
- sub.published(stream_id, event)
20
+ meta = metadata.merge(@session_metadata)
21
+ sub.published(EventEnvelope.new(stream_id, event, meta))
22
+ #sub.published(stream_id, event, metadata)
21
23
  rescue Exception => e
22
24
  #hmmmm
23
- @logger.error "exception in event subscriber #{sub.class.to_s} while handling event stream #{stream_id} #{event.inspect}: #{e.message}\n#{e.backtrace.join("\n")}" if @logger
25
+ if @logger
26
+ @logger.error "exception in event subscriber #{sub.class.to_s} while handling event stream #{stream_id} #{event.inspect}: #{e.message}\n#{e.backtrace.join("\n")}"
27
+ else
28
+ raise e
29
+ end
24
30
  end
25
31
  end
26
32
  end
@@ -4,12 +4,16 @@ module Replay
4
4
  @subscription_manager ||= Replay::SubscriptionManager.new(Replay.logger)
5
5
  end
6
6
 
7
+ def subscription_manager=(sm)
8
+ @subscription_manager = sm
9
+ end
10
+
7
11
  def add_subscriber(subscriber)
8
12
  subscription_manager.add_subscriber(subscriber)
9
13
  end
10
14
 
11
- def published(stream_id, event)
12
- @subscription_manager.notify_subscribers(stream_id, event)
15
+ def published(stream_id, event, metadata)
16
+ @subscription_manager.notify_subscribers(stream_id, event, metadata)
13
17
  end
14
18
  end
15
19
  end
@@ -61,8 +61,10 @@ Replay::EventDecorator.module_exec do
61
61
  relevant_attrs_match = event.attributes.reject{|k,v| v.nil?}
62
62
  relevant_attrs_self = self.attributes.reject{|k,v| v.nil?}
63
63
 
64
+ keys_self = relevant_attrs_self.keys
64
65
  if (relevant_attrs_self.keys - relevant_attrs_match.keys).empty?
65
- if relevant_attrs_self.reject{|k, v| event[k] == v}.any?
66
+ #publication time is not considered part of the event data
67
+ if keys_self.reject{|k| event[k] == self[k]}.any?
66
68
  return false
67
69
  else
68
70
  return true
@@ -1,19 +1,22 @@
1
+ require 'replay/test'
1
2
  module Replay
2
3
  class TestEventStream
4
+ include EventExaminer
5
+ attr_accessor :events
3
6
 
4
7
  def initialize
5
8
  @events = []
6
9
  end
7
- def publish(stream_id, event)
8
- @events << {stream: stream_id, event: event}
10
+ def published(event_envelope)
11
+ @events << event_envelope
9
12
  end
10
13
 
11
14
  def published_event?(event)
12
- @events.detect{|e| e[:event]==event}
15
+ @events.detect{|e| e.event==event}
13
16
  end
14
17
 
15
18
  def published?(stream_id, event)
16
- @events.detect{|e| e == {stream: stream_id, event: event}}
19
+ @events.detect{|e| e.stream_id == stream_id && e.event == event}
17
20
  end
18
21
  end
19
22
  end
@@ -1,3 +1,3 @@
1
1
  module Replay
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,5 +1,6 @@
1
1
  require_relative "../proofs_init.rb"
2
2
  require 'replay/test'
3
+ require 'replay/test/test_event_stream'
3
4
 
4
5
  class ReplayTest
5
6
  include Replay::Publisher
@@ -29,8 +30,10 @@ end
29
30
 
30
31
  module ReplayTest::Proof
31
32
  def sets_publish_time
33
+ ts=Replay::TestEventStream.new
34
+ add_subscriber(ts)
32
35
  publish SomeEvent(pid: 123)
33
- events.last.published_at != nil && (Time.now - events.last.published_at) < 20
36
+ ts.events.last.metadata[:published_at] != nil && (Time.now - ts.events.last.metadata[:published_at]) < 1
34
37
  end
35
38
 
36
39
  def published_at_not_considered_in_equality
@@ -72,7 +75,7 @@ module ReplayTest::Proof
72
75
 
73
76
  def subscribers_receive_events
74
77
  sub = Class.new do
75
- def published(stream, event)
78
+ def published(envelope)
76
79
  @published = true
77
80
  end
78
81
  def published?; @published; end
@@ -154,7 +157,7 @@ proof "Returns self from publish" do
154
157
  r.prove{ publish([]) == self}
155
158
  end
156
159
 
157
- proof "sets the publish time on events" do
160
+ proof "adds the publish time to event metadata" do
158
161
  r = ReplayTest.new
159
162
  r.prove{ sets_publish_time }
160
163
  end
@@ -2,9 +2,11 @@ require_relative "../proofs_init.rb"
2
2
  require 'replay/test'
3
3
 
4
4
  class Subscriber
5
- def published(stream_id, event); end
5
+ def published(envelope); end
6
6
  end
7
7
 
8
+ title "Repository configuration"
9
+
8
10
  module Replay::Repository::Configuration::Proof
9
11
  def can_configure_store?
10
12
  self.store = :memory
@@ -14,9 +14,9 @@ module Replay::SubscriptionManager::Proof
14
14
  def sub_gets_notified
15
15
  sub = Class.new do
16
16
  attr_accessor :stream, :event
17
- def published(stream, event)
18
- self.stream = stream
19
- self.event = event
17
+ def published(envelope)
18
+ self.stream = envelope.stream_id
19
+ self.event = envelope.event
20
20
  end
21
21
  end.new
22
22
  add_subscriber(sub)
@@ -21,8 +21,12 @@ proof "fuzzy matching of events" do
21
21
 
22
22
  e1 = TestEvent.new(one: '1')
23
23
  e2 = TestEvent.new(one: '1', two: '2')
24
+ e3 = TestEvent.new(one: '1', two: '2')
25
+ e4 = TestEvent.new(one: '1', two: '4')
24
26
 
25
27
  e1.prove{ matches_fuzzy(e2)}
26
28
  e2.prove{ !matches_fuzzy(e1)}
27
-
29
+ e2.prove{ matches_fuzzy(e2)}
30
+ e2.prove{ matches_fuzzy(e3)}
31
+ e2.prove{ !matches_fuzzy(e4)}
28
32
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Replay::VERSION
8
8
  s.authors = ["karmajunkie"]
9
9
  s.email = ["keith.gaddis@gmail.com"]
10
- s.homepage = ""
10
+ s.homepage = "https://github.com/karmajunkie/replay"
11
11
  s.summary = %q{Replay supports event-sourced data models.}
12
12
  s.description = %q{Replay supports event-sourced data models.}
13
13
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: replay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - karmajunkie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-15 00:00:00.000000000 Z
11
+ date: 2014-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -85,6 +85,7 @@ files:
85
85
  - lib/replay/configuration.rb
86
86
  - lib/replay/event_declarations.rb
87
87
  - lib/replay/event_decorator.rb
88
+ - lib/replay/event_envelope.rb
88
89
  - lib/replay/events.rb
89
90
  - lib/replay/inflector.rb
90
91
  - lib/replay/observer.rb
@@ -112,7 +113,7 @@ files:
112
113
  - test/replay/observer_spec.rb
113
114
  - test/replay/router/default_router_spec.rb
114
115
  - test/test_helper.rb
115
- homepage: ''
116
+ homepage: https://github.com/karmajunkie/replay
116
117
  licenses: []
117
118
  metadata: {}
118
119
  post_install_message: