euston-eventstore 1.0.2-java

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 (32) hide show
  1. data/Rakefile +126 -0
  2. data/euston-eventstore.gemspec +68 -0
  3. data/lib/euston-eventstore/commit.rb +77 -0
  4. data/lib/euston-eventstore/constants.rb +5 -0
  5. data/lib/euston-eventstore/dispatcher/asynchronous_dispatcher.rb +37 -0
  6. data/lib/euston-eventstore/dispatcher/null_dispatcher.rb +11 -0
  7. data/lib/euston-eventstore/dispatcher/synchronous_dispatcher.rb +21 -0
  8. data/lib/euston-eventstore/errors.rb +21 -0
  9. data/lib/euston-eventstore/event_message.rb +26 -0
  10. data/lib/euston-eventstore/optimistic_event_store.rb +68 -0
  11. data/lib/euston-eventstore/optimistic_event_stream.rb +106 -0
  12. data/lib/euston-eventstore/persistence/mongodb/mongo_commit.rb +82 -0
  13. data/lib/euston-eventstore/persistence/mongodb/mongo_commit_id.rb +16 -0
  14. data/lib/euston-eventstore/persistence/mongodb/mongo_config.rb +28 -0
  15. data/lib/euston-eventstore/persistence/mongodb/mongo_event_message.rb +31 -0
  16. data/lib/euston-eventstore/persistence/mongodb/mongo_persistence_engine.rb +167 -0
  17. data/lib/euston-eventstore/persistence/mongodb/mongo_persistence_factory.rb +31 -0
  18. data/lib/euston-eventstore/persistence/mongodb/mongo_snapshot.rb +32 -0
  19. data/lib/euston-eventstore/persistence/mongodb/mongo_stream_head.rb +29 -0
  20. data/lib/euston-eventstore/persistence/stream_head.rb +23 -0
  21. data/lib/euston-eventstore/snapshot.rb +21 -0
  22. data/lib/euston-eventstore/version.rb +5 -0
  23. data/lib/euston-eventstore.rb +7 -0
  24. data/spec/event_store/dispatcher/asynchronous_dispatcher_spec.rb +75 -0
  25. data/spec/event_store/dispatcher/synchronous_dispatcher_spec.rb +39 -0
  26. data/spec/event_store/optimistic_event_store_spec.rb +292 -0
  27. data/spec/event_store/optimistic_event_stream_spec.rb +318 -0
  28. data/spec/event_store/persistence/mongodb_spec.rb +301 -0
  29. data/spec/event_store/serialization/simple_message.rb +12 -0
  30. data/spec/spec_helper.rb +39 -0
  31. data/spec/support/array_enumeration_counter.rb +20 -0
  32. metadata +178 -0
data/Rakefile ADDED
@@ -0,0 +1,126 @@
1
+ require 'date'
2
+ require 'rspec/core/rake_task'
3
+
4
+ #############################################################################
5
+ #
6
+ # Helper functions
7
+ #
8
+ #############################################################################
9
+
10
+ def name
11
+ @name ||= Dir['*.gemspec'].first.split('.').first
12
+ end
13
+
14
+ def version
15
+ line = File.read("lib/#{name}/version.rb")[/^\s*VERSION\s*=\s*.*/]
16
+ line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
17
+ end
18
+
19
+ def date
20
+ Date.today.to_s
21
+ end
22
+
23
+ def rubyforge_project
24
+ name
25
+ end
26
+
27
+ def gemspec_file
28
+ "#{name}.gemspec"
29
+ end
30
+
31
+ def gem_file
32
+ "#{name}-#{version}.gem"
33
+ end
34
+
35
+ def replace_header(head, header_name)
36
+ head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
37
+ end
38
+
39
+ def platform
40
+ RUBY_PLATFORM.to_s == 'java' ? '-java' : ''
41
+ end
42
+
43
+ #############################################################################
44
+ #
45
+ # Custom tasks
46
+ #
47
+ #############################################################################
48
+
49
+ default_rspec_opts = %w[--colour --format Fuubar]
50
+
51
+ desc "Run all examples"
52
+ RSpec::Core::RakeTask.new(:spec) do |t|
53
+ t.rspec_opts = default_rspec_opts
54
+ end
55
+
56
+ #############################################################################
57
+ #
58
+ # Packaging tasks
59
+ #
60
+ #############################################################################
61
+
62
+ def built_gem
63
+ Dir["#{name}*.gem"].first
64
+ end
65
+
66
+ desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
67
+ task :release => :build do
68
+ unless `git branch` =~ /^\* master$/
69
+ puts "You must be on the master branch to release!"
70
+ exit!
71
+ end
72
+ sh "git commit --allow-empty -a -m 'Release #{version}'"
73
+ sh "git tag v#{version}"
74
+ sh "git push origin master"
75
+ sh "git push origin v#{version}"
76
+ sh "gem push pkg/#{built_gem}"
77
+ end
78
+
79
+ desc "Build #{gem_file} into the pkg directory"
80
+ task :build => :gemspec do
81
+ sh "mkdir -p pkg"
82
+ sh "gem build #{gemspec_file}"
83
+ sh "mv #{built_gem} pkg"
84
+ end
85
+
86
+ desc "Generate #{gemspec_file}"
87
+ task :gemspec => :validate do
88
+ # read spec file and split out manifest section
89
+ spec = File.read(gemspec_file)
90
+ head, manifest, tail = spec.split(" # = MANIFEST =\n")
91
+
92
+ # replace name version and date
93
+ replace_header(head, :name)
94
+ replace_header(head, :version)
95
+ replace_header(head, :date)
96
+ #comment this out if your rubyforge_project has a different name
97
+ #replace_header(head, :rubyforge_project)
98
+
99
+ # determine file list from git ls-files
100
+ files = `git ls-files`.
101
+ split("\n").
102
+ sort.
103
+ reject { |file| file =~ /^\./ }.
104
+ reject { |file| file =~ /^(rdoc|pkg)/ }.
105
+ map { |file| " #{file}" }.
106
+ join("\n")
107
+
108
+ # piece file back together and write
109
+ manifest = " s.files = %w[\n#{files}\n ]\n"
110
+ spec = [head, manifest, tail].join(" # = MANIFEST =\n")
111
+ File.open(gemspec_file, 'w') { |io| io.write(spec) }
112
+ puts "Updated #{gemspec_file}"
113
+ end
114
+
115
+ desc "Validate #{gemspec_file}"
116
+ task :validate do
117
+ libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
118
+ unless libfiles.empty?
119
+ puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
120
+ exit!
121
+ end
122
+ unless Dir['VERSION*'].empty?
123
+ puts "A `VERSION` file at root level violates Gem best practices."
124
+ exit!
125
+ end
126
+ end
@@ -0,0 +1,68 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'euston-eventstore'
3
+ s.version = '1.0.2'
4
+ s.date = '2011-09-15'
5
+ # s.platform = Gem::Platform::CURRENT
6
+ s.platform = RUBY_PLATFORM.to_s == 'java' ? 'java' : Gem::Platform::RUBY
7
+ s.authors = ['Lee Henson', 'Guy Boertje']
8
+ s.email = ['lee.m.henson@gmail.com', 'guyboertje@gmail.com']
9
+ s.summary = %q{Event store for use with Euston.}
10
+ s.description = "Ruby port for Jonathan Oliver's EventStore. See https://github.com/joliver/EventStore for details."
11
+ s.homepage = 'http://github.com/leemhenson/euston-eventstore'
12
+ # = MANIFEST =
13
+ s.files = %w[
14
+ Rakefile
15
+ euston-eventstore.gemspec
16
+ lib/euston-eventstore.rb
17
+ lib/euston-eventstore/commit.rb
18
+ lib/euston-eventstore/constants.rb
19
+ lib/euston-eventstore/dispatcher/asynchronous_dispatcher.rb
20
+ lib/euston-eventstore/dispatcher/null_dispatcher.rb
21
+ lib/euston-eventstore/dispatcher/synchronous_dispatcher.rb
22
+ lib/euston-eventstore/errors.rb
23
+ lib/euston-eventstore/event_message.rb
24
+ lib/euston-eventstore/optimistic_event_store.rb
25
+ lib/euston-eventstore/optimistic_event_stream.rb
26
+ lib/euston-eventstore/persistence/mongodb/mongo_commit.rb
27
+ lib/euston-eventstore/persistence/mongodb/mongo_commit_id.rb
28
+ lib/euston-eventstore/persistence/mongodb/mongo_config.rb
29
+ lib/euston-eventstore/persistence/mongodb/mongo_event_message.rb
30
+ lib/euston-eventstore/persistence/mongodb/mongo_persistence_engine.rb
31
+ lib/euston-eventstore/persistence/mongodb/mongo_persistence_factory.rb
32
+ lib/euston-eventstore/persistence/mongodb/mongo_snapshot.rb
33
+ lib/euston-eventstore/persistence/mongodb/mongo_stream_head.rb
34
+ lib/euston-eventstore/persistence/stream_head.rb
35
+ lib/euston-eventstore/snapshot.rb
36
+ lib/euston-eventstore/version.rb
37
+ spec/event_store/dispatcher/asynchronous_dispatcher_spec.rb
38
+ spec/event_store/dispatcher/synchronous_dispatcher_spec.rb
39
+ spec/event_store/optimistic_event_store_spec.rb
40
+ spec/event_store/optimistic_event_stream_spec.rb
41
+ spec/event_store/persistence/mongodb_spec.rb
42
+ spec/event_store/serialization/simple_message.rb
43
+ spec/spec_helper.rb
44
+ spec/support/array_enumeration_counter.rb
45
+ ]
46
+ # = MANIFEST =
47
+
48
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
49
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
50
+
51
+ s.add_dependency 'activesupport', '~> 3.0.9'
52
+ s.add_dependency 'hash-keys', '~> 1.0.0'
53
+ s.add_dependency 'require_all', '~> 1.2.0'
54
+ s.add_dependency 'uuid', '~> 2.3.0'
55
+
56
+ if RUBY_PLATFORM.to_s == 'java'
57
+ s.add_dependency 'json-jruby', '~> 1.5.0'
58
+ s.add_dependency 'jmongo', '~> 1.0.0'
59
+ else
60
+ s.add_dependency 'bson_ext', '~> 1.1.0'
61
+ s.add_dependency 'json', '~> 1.5.0'
62
+ s.add_dependency 'mongo', '~> 1.3.1'
63
+ end
64
+
65
+ s.add_development_dependency 'awesome_print', '~> 0.4.0'
66
+ s.add_development_dependency 'fuubar', '~> 0.0.0'
67
+ s.add_development_dependency 'rspec', '~> 2.6.0'
68
+ end
@@ -0,0 +1,77 @@
1
+ module Euston
2
+ module EventStore
3
+
4
+ # Represents a series of events which have been fully committed as a single unit and which apply to the stream indicated.
5
+ class Commit
6
+ def initialize(hash)
7
+ defaults = {
8
+ :stream_id => nil,
9
+ :stream_revision => 1,
10
+ :commit_id => nil,
11
+ :commit_sequence => 1,
12
+ :commit_timestamp => Time.now.utc,
13
+ :headers => OpenStruct.new,
14
+ :events => []
15
+ }
16
+ values = defaults.merge hash
17
+ defaults.keys.each { |key| instance_variable_set "@#{key}", values[key] }
18
+ end
19
+
20
+ def to_hash
21
+ {
22
+ :stream_id => stream_id,
23
+ :stream_revision => stream_revision,
24
+ :commit_id => commit_id,
25
+ :commit_sequence => commit_sequence,
26
+ :commit_timestamp => commit_timestamp,
27
+ :headers => headers.is_a?(OpenStruct) ? headers.instance_variable_get(:@table) : headers,
28
+ :events => events
29
+ }
30
+ end
31
+ # Gets the value which uniquely identifies the stream to which the commit belongs.
32
+ attr_reader :stream_id
33
+
34
+ # Gets the value which indicates the revision of the most recent event in the stream to which this commit applies.
35
+ attr_reader :stream_revision
36
+
37
+ # Gets the value which uniquely identifies the commit within the stream.
38
+ attr_reader :commit_id
39
+
40
+ # Gets the value which indicates the sequence (or position) in the stream to which this commit applies.
41
+ attr_reader :commit_sequence
42
+
43
+ # Gets the point in time at which the commit was persisted.
44
+ attr_reader :commit_timestamp
45
+
46
+ # Gets the metadata which provides additional, unstructured information about this commit.
47
+ attr_reader :headers
48
+
49
+ # Gets the collection of event messages to be committed as a single unit.
50
+ attr_reader :events
51
+
52
+ def ==(other)
53
+ (other.is_a? Commit) && (@stream_id == other.stream_id) && (@commit_id == other.commit_id)
54
+ end
55
+
56
+ class << self
57
+ def empty?(attempt)
58
+ attempt.nil? || attempt.events.empty?
59
+ end
60
+
61
+ def has_identifier?(attempt)
62
+ !(attempt.stream_id.nil? || attempt.commit_id.nil?)
63
+ end
64
+
65
+ def valid?(attempt)
66
+ raise ArgumentError.new('The commit must not be nil.') if attempt.nil?
67
+ raise ArgumentError.new('The commit must be uniquely identified.') unless Commit.has_identifier? attempt
68
+ raise ArgumentError.new('The commit sequence must be a positive number.') unless attempt.commit_sequence > 0
69
+ raise ArgumentError.new('The stream revision must be a positive number.') unless attempt.stream_revision > 0
70
+ raise ArgumentError.new('The stream revision must always be greater than or equal to the commit sequence.') if (attempt.stream_revision < attempt.commit_sequence)
71
+
72
+ true
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,5 @@
1
+ module Euston
2
+ module EventStore
3
+ FIXNUM_MAX = (2**(0.size * 8 -2) -1)
4
+ end
5
+ end
@@ -0,0 +1,37 @@
1
+ module Euston
2
+ module EventStore
3
+ module Dispatcher
4
+ class AsynchronousDispatcher
5
+ def initialize(bus, persistence, &block)
6
+ @bus = bus
7
+ @persistence = persistence
8
+ @handle_exception = block_given? ? block : Proc.new {}
9
+
10
+ start
11
+ end
12
+
13
+ def dispatch(commit)
14
+ Thread.fork(commit) { |c| begin_dispatch c }
15
+ end
16
+
17
+ protected
18
+
19
+ def begin_dispatch(commit)
20
+ begin
21
+ @bus.publish commit
22
+ @persistence.mark_commit_as_dispatched commit
23
+ rescue Exception => e
24
+ @handle_exception.call commit, e
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def start
31
+ @persistence.init
32
+ @persistence.get_undispatched_commits.each { |commit| dispatch commit }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ module Euston
2
+ module EventStore
3
+ module Dispatcher
4
+ class NullDispatcher
5
+ def dispatch(commit)
6
+ # no-op
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ module Euston
2
+ module EventStore
3
+ module Dispatcher
4
+ class SynchronousDispatcher
5
+ def initialize persistence, &block
6
+ @persistence = persistence
7
+ @dispatch = block
8
+ end
9
+
10
+ def dispatch commit
11
+ @dispatch.call commit
12
+ @persistence.mark_commit_as_dispatched commit
13
+ end
14
+
15
+ def lookup
16
+ @persistence.get_undispatched_commits.each { |commit| dispatch commit }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Euston
2
+ module EventStore
3
+ # Represents an optimistic concurrency conflict between multiple writers.
4
+ class ConcurrencyError < RuntimeError; end
5
+
6
+ # Represents an attempt to commit the same information more than once.
7
+ class DuplicateCommitError < RuntimeError; end
8
+
9
+ # Represents a loss of communications with the storage
10
+ class StorageUnavailableError < RuntimeError; end
11
+
12
+ # Represents a general failure of the storage engine or persistence infrastructure.
13
+ class StorageError < RuntimeError; end
14
+
15
+ # Represents an attempt to commit the same information more than once.
16
+ class StreamNotFoundError < RuntimeError; end
17
+
18
+ # Represents an error when the proxy returns a non 200 code that does not map to any of the above errors.
19
+ class ProxyCallError < RuntimeError; end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ module Euston
2
+ module EventStore
3
+ # Represents a single element in a stream of events.
4
+ class EventMessage
5
+
6
+ def initialize(arg = nil)
7
+ if arg.is_a?(Hash) && (arg.keys & ['body','headers']).size == 2
8
+ @body, @headers = arg.values_at('body','headers')
9
+ else
10
+ @headers = {}
11
+ @body = arg
12
+ end
13
+ end
14
+
15
+ def to_hash
16
+ {:headers=>@headers,:body=>@body}
17
+ end
18
+
19
+ # Gets the metadata which provides additional, unstructured information about this message.
20
+ attr_reader :headers
21
+
22
+ # Gets or sets the actual event message body.
23
+ attr_reader :body
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,68 @@
1
+ module Euston
2
+ module EventStore
3
+ class OptimisticEventStore
4
+ def initialize(persistence)
5
+ @persistence = persistence
6
+ end
7
+
8
+ def instrumentation
9
+ return nil unless @persistence.respond_to?(:instrumentation)
10
+ @persistence.instrumentation
11
+ end
12
+
13
+ def add_snapshot(snapshot)
14
+ @persistence.add_snapshot snapshot
15
+ end
16
+
17
+ def commit(attempt)
18
+ return unless Commit.valid?(attempt) && !Commit.empty?(attempt)
19
+
20
+ @persistence.commit attempt
21
+ end
22
+
23
+ def create_stream(stream_id)
24
+ OptimisticEventStream.new(:stream_id => stream_id,
25
+ :persistence => self)
26
+ end
27
+
28
+ def get_from(stream_id, min_revision, max_revision)
29
+ @persistence.get_from(:stream_id => stream_id,
30
+ :min_revision => min_revision,
31
+ :max_revision => max_revision).to_enum
32
+ end
33
+
34
+ def get_snapshot(stream_id, max_revision)
35
+ @persistence.get_snapshot stream_id, validate_max_revision(max_revision)
36
+ end
37
+
38
+ def get_streams_to_snapshot(max_threshold)
39
+ @persistence.get_streams_to_snapshot max_threshold
40
+ end
41
+
42
+ def open_stream(options)
43
+ options = { :stream_id => nil,
44
+ :min_revision => 0,
45
+ :max_revision => 0,
46
+ :snapshot => nil }.merge(options)
47
+
48
+ options = options.merge(:max_revision => validate_max_revision(options[:max_revision]),
49
+ :persistence => self)
50
+
51
+ if options[:snapshot].nil?
52
+ options.delete :snapshot
53
+ else
54
+ options.delete :stream_id
55
+ options.delete :min_revision
56
+ end
57
+
58
+ OptimisticEventStream.new options
59
+ end
60
+
61
+ private
62
+
63
+ def validate_max_revision(max_revision)
64
+ max_revision <= 0 ? FIXNUM_MAX : max_revision
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,106 @@
1
+ module Euston
2
+ module EventStore
3
+ class OptimisticEventStream
4
+ def initialize(options)
5
+ @persistence = options[:persistence]
6
+ @committed_events = []
7
+ @uncommitted_events = []
8
+ @uncommitted_headers = {}
9
+ @commit_sequence = 0
10
+ @identifiers = []
11
+
12
+ if options.has_key? :snapshot
13
+ snapshot = options[:snapshot]
14
+ @stream_id = snapshot.stream_id
15
+ commits = @persistence.get_from @stream_id, snapshot.stream_revision, options[:max_revision]
16
+ populate_stream snapshot.stream_revision + 1, options[:max_revision], commits
17
+ @stream_revision = snapshot.stream_revision + committed_events.length
18
+ else
19
+ @stream_id = options[:stream_id]
20
+ @stream_revision = 0
21
+ min_revision = options[:min_revision] ||= nil
22
+ max_revision = options[:max_revision] ||= nil
23
+
24
+ unless min_revision.nil? || max_revision.nil?
25
+ commits = @persistence.get_from @stream_id, min_revision, max_revision
26
+ populate_stream min_revision, max_revision, commits
27
+
28
+ raise StreamNotFoundError if (min_revision > 0 && committed_events.empty?)
29
+ end
30
+ end
31
+ end
32
+
33
+ attr_reader :stream_id, :stream_revision, :commit_sequence, :committed_events, :uncommitted_events, :uncommitted_headers
34
+
35
+ def <<(event)
36
+ @uncommitted_events << event unless event.nil? || event.body.nil?
37
+ end
38
+
39
+ def clear_changes
40
+ @uncommitted_events = []
41
+ @uncommitted_headers = {}
42
+ end
43
+
44
+ def commit_changes(commit_id)
45
+ raise Euston::EventStore::DuplicateCommitError if @identifiers.include? commit_id
46
+
47
+ return unless has_changes
48
+
49
+ begin
50
+ persist_changes commit_id
51
+ rescue ConcurrencyError => e
52
+ commits = @persistence.get_from stream_id, stream_revision + 1, FIXNUM_MAX
53
+ populate_stream stream_revision + 1, FIXNUM_MAX, commits
54
+
55
+ raise e
56
+ end
57
+ end
58
+
59
+ protected
60
+
61
+ def copy_values_to_new_commit(commit_id)
62
+ Euston::EventStore::Commit.new :stream_id => stream_id,
63
+ :stream_revision => stream_revision + uncommitted_events.length,
64
+ :commit_id => commit_id,
65
+ :commit_sequence => commit_sequence + 1,
66
+ :commit_timestamp => Time.now.utc,
67
+ :headers => uncommitted_headers,
68
+ :events => uncommitted_events
69
+ end
70
+
71
+ def has_changes
72
+ !uncommitted_events.empty?
73
+ end
74
+
75
+ def persist_changes(commit_id)
76
+ commit = copy_values_to_new_commit commit_id
77
+ @persistence.commit commit
78
+
79
+ populate_stream stream_revision + 1, commit.stream_revision, [ commit ]
80
+ clear_changes
81
+ end
82
+
83
+ def populate_stream(min_revision, max_revision, commits = [])
84
+ commits.each do |commit|
85
+ @identifiers << commit.commit_id
86
+ @commit_sequence = commit.commit_sequence
87
+
88
+ current_revision = commit.stream_revision - commit.events.length + 1
89
+
90
+ return if current_revision > max_revision
91
+
92
+ commit.events.each do |event|
93
+ break if current_revision > max_revision
94
+
95
+ unless current_revision < min_revision
96
+ @committed_events << event
97
+ @stream_revision = current_revision
98
+ end
99
+
100
+ current_revision += 1
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,82 @@
1
+ module Euston
2
+ module EventStore
3
+ module Persistence
4
+ module Mongodb
5
+ module MongoCommit
6
+ extend ::ActiveSupport::Concern
7
+
8
+ included do
9
+ alias_method :original_initialize, :initialize
10
+ alias_method :initialize, :mongo_initialize
11
+ end
12
+
13
+ class << self
14
+ def from_hash(hash)
15
+ return nil if hash.nil?
16
+
17
+ id = hash['_id']
18
+ events = hash['events'].sort_by { |e| e["stream_revision"] }.to_a
19
+ stream_revision = events.last['stream_revision']
20
+ events = events.map { |e| Euston::EventStore::Persistence::Mongodb::MongoEventMessage.from_hash e['payload'] }
21
+
22
+ Euston::EventStore::Commit.new :stream_id => id['stream_id'],
23
+ :stream_revision => stream_revision,
24
+ :commit_id => hash['commit_id'],
25
+ :commit_sequence => id['commit_sequence'],
26
+ :commit_timestamp => hash['commit_timestamp'],
27
+ :headers => hash['headers'].recursive_symbolize_keys!,
28
+ :events => events
29
+ end
30
+ end
31
+
32
+ def mongo_initialize(hash)
33
+ original_initialize(hash)
34
+ @dispatched = hash[:dispatched]
35
+ end
36
+
37
+ attr_reader :dispatched
38
+
39
+ def to_hash
40
+ {
41
+ :_id => { :stream_id => stream_id, :commit_sequence => commit_sequence },
42
+ :commit_id => commit_id,
43
+ :commit_timestamp => commit_timestamp.to_f,
44
+ :dispatched => dispatched || false,
45
+ :events => events.map { |e| e.to_hash },
46
+ :headers => headers
47
+ }
48
+ end
49
+
50
+ def to_mongo_commit
51
+ mongo_stream_revision = stream_revision - (events.length - 1)
52
+ mongo_events = events.map do |e|
53
+ hash = { :stream_revision => mongo_stream_revision, :payload => e.to_hash }
54
+ mongo_stream_revision += 1
55
+ hash
56
+ end
57
+
58
+ {
59
+ :_id => { :stream_id => stream_id, :commit_sequence => commit_sequence },
60
+ :commit_id => commit_id,
61
+ :commit_timestamp => commit_timestamp.to_f,
62
+ :headers => headers,
63
+ :events => mongo_events,
64
+ :dispatched => false
65
+ }
66
+ end
67
+
68
+ def to_id_query
69
+ {
70
+ '_id.commit_sequence' => commit_sequence,
71
+ '_id.stream_id' => stream_id
72
+ }
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ class Commit
79
+ include Persistence::Mongodb::MongoCommit
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,16 @@
1
+ module Euston
2
+ module EventStore
3
+ module Persistence
4
+ module Mongodb
5
+ module MongoCommitId
6
+ def initialize(stream_id, commit_sequence)
7
+ @stream_id = stream_id
8
+ @commit_sequence = commit_sequence
9
+ end
10
+
11
+ attr_reader :stream_id, :commit_sequence
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end