sandthorn_driver_event_store 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 72f4778deae2b0d24a481a8665fcdbb3ee98b55a
4
+ data.tar.gz: b6474c0758bf60c41f95b4189229dd364dcc4bed
5
+ SHA512:
6
+ metadata.gz: a693d6007d3b25e7d685c97458cf7ef58e02a4b66e376adb27238c6a88d706fc0203c01dcc21e555d8dc77edd914a448d52d880a78207ca23342c520c5251686
7
+ data.tar.gz: 374ab395ffefc0f14d3d55922656db88ff2eaba211aafb2616785629af92d0c07e18fdfdf6aaed2f765512075ea45c21d30e2dffb3acaa484e08ab46fa938cf9
data/.autotest ADDED
@@ -0,0 +1,3 @@
1
+ Autotest.add_hook :initialize do |at|
2
+ %w{.git spec/db coverage}.each {|exception| at.add_exception(exception)}
3
+ end
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.sqlite3
19
+ .idea
20
+ spec/db_file/test_events.csv
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format d
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ sandthorn_driver_event_store
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'http_eventstore', git: 'git://github.com/hallgren/http_eventstore.git', branch: 'event_store_version_3' #path: '../http_eventstore'
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ interactor :simple
2
+ guard :rspec, cmd: 'bundle exec rspec' do
3
+ watch(%r{^spec/.+_spec\.rb$})
4
+ watch(%r{^lib/sandthorn_driver_event_store/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
7
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Morgan Hallgren
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # Sandthorn Event Store driver
2
+
3
+ A [Event Store](geteventstore.com) driver for [Sandthorn](https://github.com/Sandthorn/sandthorn).
4
+
5
+ This driver is a write upon [http_eventstore](https://github.com/arkency/http_eventstore) from [Arkency](http://arkency.com)
6
+
7
+ Currently only a subset of functionallity is implemented contra the [sandthorn_sequel_driver](https://github.com/Sandthorn/sandthorn_sequel_driver)
8
+
9
+ * save_events
10
+ * find
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'sandthorn_driver_event_store'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install sandthorn_driver_event_store
25
+
26
+ ## Usage
27
+
28
+ SandthornDriverEventStore.driver host: "localhost", port: 2113
29
+
30
+ ## Todo
31
+
32
+ * All - Get all events based on Aggregate type (Class)
33
+ * Get events - Functionallity for [Sandthorn Sequel Projection](https://github.com/Sandthorn/sandthorn_sequel_projection)
34
+ * Implement snapshoting, now all event of an aggregate has to be fetched to build the aggregate.
35
+
36
+ ## Contributing
37
+
38
+ 1. Fork it
39
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
40
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
41
+ 4. Push to the branch (`git push origin my-new-feature`)
42
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task default: :spec
7
+
8
+ task :benchmark do
9
+ sh "ulimit -n 8192 && rspec --tag benchmark"
10
+ end
@@ -0,0 +1,57 @@
1
+ module SandthornDriverEventStore
2
+ class EventAccess < Access::Base
3
+ # = EventAccess
4
+ # Reads and writes events.
5
+
6
+ def store_events(events = [])
7
+ events = Utilities.array_wrap(events)
8
+ timestamp = Time.now.utc
9
+ stream_name = events.first[:aggregate_id]
10
+
11
+ event_store_events = events.map do |event|
12
+ build_event_data(timestamp, event)
13
+ end
14
+
15
+ if event_store_events.any?
16
+ expected_version = event_store_events.first[:position] > 0 ? event_store_events.first[:position]-1 : nil
17
+ storage.append_to_stream(stream_name, event_store_events, expected_version)
18
+ end
19
+ end
20
+
21
+ def find_events_by_aggregate_id(aggregate_id)
22
+ return storage.read_all_events_forward(aggregate_id).map { |event|
23
+ {
24
+ event_args: JSON.parse(event.data.to_json, symbolize_names: true),
25
+ aggregate_id: event.stream_name,
26
+ aggregate_version: event.position+1,
27
+ event_name: event.type
28
+ }
29
+ }
30
+ end
31
+
32
+ def get_events(*args)
33
+ query_builder = EventQuery.new(storage)
34
+ query_builder.build(*args)
35
+ wrap(query_builder.events)
36
+ end
37
+
38
+ private
39
+
40
+ def wrap(arg)
41
+ events = Utilities.array_wrap(arg)
42
+ events.map { |e| EventWrapper.new(e.values) }
43
+ end
44
+
45
+ def build_event_data(timestamp, event)
46
+ {
47
+ event_type: event[:event_name].to_s,
48
+ data: event[:event_args],
49
+ event_id: SecureRandom.uuid,
50
+ id: event[:aggregate_id],
51
+ position: event[:aggregate_version]-1,
52
+ created_time: timestamp
53
+ }
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ module SandthornDriverEventStore
2
+ module Access
3
+ class Base
4
+ # = Access::Base
5
+ # Inheriting classes use +storage+ to provide access to a
6
+ # particular database model/table.
7
+ def initialize(storage)
8
+ @storage = storage
9
+ end
10
+
11
+ private
12
+
13
+ attr_reader :storage
14
+ end
15
+ end
16
+ end
17
+
18
+ require "sandthorn_driver_event_store/access/event_access"
@@ -0,0 +1,49 @@
1
+ module SandthornDriverEventStore::Errors
2
+ Error = Class.new(StandardError)
3
+ InternalError = Class.new(Error)
4
+ NoAggregateError = Class.new(Error)
5
+ EventFormatError = Class.new(Error)
6
+
7
+ class ConcurrencyError < Error
8
+ attr_reader :event, :aggregate
9
+ def initialize(event, aggregate)
10
+ @event = event
11
+ @aggregate = aggregate
12
+ super(create_message)
13
+ end
14
+
15
+ def create_message
16
+ "#{aggregate.aggregate_type} with id #{aggregate.aggregate_id}: " +
17
+ "expected event with version #{aggregate.aggregate_version}, but got #{event.aggregate_version}"
18
+ end
19
+ end
20
+
21
+ class WrongAggregateVersionError < Error;
22
+ def initialize(aggregate, version)
23
+ @aggregate = aggregate
24
+ @version = version
25
+ super(create_message)
26
+ end
27
+
28
+ def create_message
29
+ "#{@aggregate[:aggregate_type]} with id #{@aggregate[:aggregate_id]}" +
30
+ " should be at version #{@version}" +
31
+ " but was #{@aggregate[:aggregate_version]} in the event store."
32
+ end
33
+ end
34
+
35
+ class WrongSnapshotVersionError < Error
36
+ attr_reader :aggregate, :version
37
+ def initialize(aggregate, version)
38
+ @aggregate = aggregate
39
+ @version = version
40
+ super(create_message)
41
+ end
42
+
43
+ def create_message
44
+ "#{aggregate[:aggregate_type]} with id #{aggregate[:aggregate_id]}: tried to save snapshot with version "+
45
+ "#{version}, but current version is at #{aggregate[:aggregate_version]}"
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,65 @@
1
+ module SandthornDriverEventStore
2
+ class EventQuery
3
+ def initialize(storage)
4
+ @storage = storage
5
+ end
6
+
7
+ def build(
8
+ stream_name:,
9
+ take: 0,
10
+ after_sequence_number: 0)
11
+
12
+ query = storage.events
13
+ query = add_sequence_number(query, after_sequence_number)
14
+ query = add_select(query)
15
+ query = add_limit(query, take)
16
+ @query = query.order(:sequence_number)
17
+ end
18
+
19
+ def events
20
+ @query.all
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :storage
26
+
27
+ def add_limit(query, take)
28
+ if take > 0
29
+ query.limit(take)
30
+ else
31
+ query
32
+ end
33
+ end
34
+
35
+ def add_select(query)
36
+ query.select(*select_columns)
37
+ end
38
+
39
+ def select_columns
40
+ aggregate_version = Sequel.qualify(storage.events_table_name, :aggregate_version)
41
+ aggregate_id = Sequel.qualify(storage.events_table_name, :aggregate_id)
42
+ [
43
+ :aggregate_type,
44
+ aggregate_version,
45
+ aggregate_id,
46
+ :sequence_number,
47
+ :event_name,
48
+ :event_data,
49
+ :timestamp
50
+ ]
51
+ end
52
+
53
+ def add_sequence_number(query, after_sequence_number)
54
+ query.where { sequence_number > after_sequence_number }
55
+ end
56
+
57
+ def add_aggregate_types(query, aggregate_types)
58
+ if aggregate_types.any?
59
+ query = query.where( :aggregate_type => aggregate_types )
60
+ end
61
+ query
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,48 @@
1
+ module SandthornDriverEventStore
2
+ class EventStore
3
+
4
+ attr_reader :driver
5
+
6
+ def initialize event_store_driver:
7
+ @driver = event_store_driver
8
+ driver.execute do |db|
9
+ @storage = db
10
+ end
11
+ end
12
+
13
+ def save_events events, aggregate_id, class_name
14
+ driver.execute do |db|
15
+ event_access = get_event_access(db)
16
+ events = events.map { |event| event[:aggregate_type] = class_name; event[:aggregate_id] = aggregate_id; event;}
17
+ event_access.store_events(events)
18
+ end
19
+ end
20
+
21
+ def find aggregate_id
22
+ driver.execute do |db|
23
+ event_access = get_event_access(db)
24
+ event_access.find_events_by_aggregate_id(aggregate_id)
25
+ end
26
+ end
27
+
28
+ def all aggregate_type
29
+ raise :NotImplemented
30
+ end
31
+
32
+ def get_events(*args)
33
+ raise :NotImplemented
34
+ end
35
+
36
+
37
+ private
38
+
39
+ def get_event_access(db)
40
+ EventAccess.new(storage(db))
41
+ end
42
+
43
+ def storage(db)
44
+ @storage
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ require 'http_eventstore'
2
+
3
+ module SandthornDriverEventStore
4
+ class EventStoreDriver
5
+
6
+ def initialize host:, port:, page_size:
7
+
8
+ @connection = HttpEventstore::Connection.new do |config|
9
+ #default value is '127.0.0.1'
10
+ config.endpoint = host
11
+ #default value is 2113
12
+ config.port = port
13
+ #default value is 20 entries per page
14
+ config.page_size = page_size
15
+ end
16
+ end
17
+
18
+ def execute
19
+ yield @connection
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module SandthornDriverEventStore
2
+ module Utilities
3
+ def self.array_wrap(object)
4
+ if object.nil?
5
+ []
6
+ elsif object.respond_to?(:to_ary)
7
+ object.to_ary || [object]
8
+ else
9
+ [object]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ require "sandthorn_driver_event_store/utilities/array"
@@ -0,0 +1,3 @@
1
+ module SandthornDriverEventStore
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,12 @@
1
+ require 'delegate'
2
+ module SandthornDriverEventStore
3
+ class EventWrapper < SimpleDelegator
4
+
5
+ [:aggregate_version, :event_name, :event_data, :timestamp, :aggregate_id, :aggregate_type].each do |attribute|
6
+ define_method(attribute) do
7
+ fetch(attribute)
8
+ end
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1 @@
1
+ require "sandthorn_driver_event_store/wrappers/event_wrapper"
@@ -0,0 +1,18 @@
1
+ require "sandthorn_driver_event_store/version"
2
+ require "sandthorn_driver_event_store/utilities"
3
+ require "sandthorn_driver_event_store/wrappers"
4
+ require "sandthorn_driver_event_store/event_query"
5
+ require "sandthorn_driver_event_store/access"
6
+ require 'sandthorn_driver_event_store/event_store'
7
+ require 'sandthorn_driver_event_store/event_store_driver'
8
+ require 'sandthorn_driver_event_store/errors'
9
+
10
+ module SandthornDriverEventStore
11
+ class << self
12
+ def driver host:, port:, page_size: 20
13
+ driver = SandthornDriverEventStore::EventStoreDriver.new host: host, port: port, page_size: page_size
14
+ return SandthornDriverEventStore::EventStore.new event_store_driver: driver
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sandthorn_driver_event_store/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sandthorn_driver_event_store"
8
+ spec.version = SandthornDriverEventStore::VERSION
9
+ spec.authors = ["Morgan Hallgren"]
10
+ spec.email = ["morgan.hallgren@gmail.com"]
11
+ spec.description = %q{Event Store driver for Sandthorn}
12
+ spec.summary = %q{Event Store driver for Sandthorn}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.required_ruby_version = ">= 2.0"
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "gem-release"
28
+ spec.add_development_dependency "pry"
29
+ spec.add_development_dependency "pry-doc"
30
+ spec.add_development_dependency "awesome_print"
31
+ spec.add_development_dependency "autotest-standalone"
32
+ spec.add_development_dependency "uuidtools"
33
+ spec.add_development_dependency "ruby-beautify"
34
+ spec.add_development_dependency "msgpack"
35
+ spec.add_development_dependency "snappy"
36
+ spec.add_development_dependency "guard-rspec"
37
+
38
+ #spec.add_runtime_dependency "http_eventstore"
39
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module SandthornDriverEventStore
4
+ describe EventStore do
5
+ before(:each) { prepare_for_test }
6
+ context "interface structure" do
7
+ let(:subject) { event_store }
8
+ methods = [
9
+ :save_events,
10
+ :find,
11
+ :all,
12
+ :get_events,
13
+ :driver
14
+ ]
15
+
16
+ methods.each do |method|
17
+ it "responds to #{method}" do
18
+ expect(subject).to respond_to(method)
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+
3
+ module SandthornDriverEventStore
4
+ describe EventAccess do
5
+
6
+ before do
7
+ prepare_for_test
8
+ end
9
+
10
+ let(:db) { Sequel.connect(event_store_url)}
11
+ let(:aggregate_id) { SecureRandom.uuid }
12
+ let(:aggregate) do
13
+ aggregate_access.register_aggregate(aggregate_id, "foo")
14
+ end
15
+ let(:storage) { return event_store_driver.execute { |db| return db; } }
16
+ let(:access) { EventAccess.new(storage) }
17
+
18
+ let(:events) do
19
+ [
20
+ {
21
+ aggregate_version: 1,
22
+ aggregate_id: aggregate_id,
23
+ aggregate_type: "Foo",
24
+ event_name: "new",
25
+ event_args: {test: "new_data"}
26
+ },{
27
+ aggregate_version: 2,
28
+ aggregate_id: aggregate_id,
29
+ aggregate_type: "Foo",
30
+ event_name: "foo",
31
+ event_args: {test: "foo_data"}
32
+ }
33
+ ]
34
+ end
35
+
36
+ describe "#store_events" do
37
+
38
+ it "handles both arrays and single events" do
39
+ access.store_events(events[0])
40
+ events = access.find_events_by_aggregate_id(aggregate_id)
41
+ expect(events.length).to eq(1)
42
+ end
43
+
44
+ context "when the aggregate version of an event is incorrect" do
45
+ it "throws an error" do
46
+ event = { aggregate_version: 100, aggregate_id: aggregate_id, aggregate_type: "Foo", event_name: "new", event_data: "noop" }
47
+ expect { access.store_events([event])}.to raise_error
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "#find_events_by_aggregate_id" do
53
+ context "when there are events" do
54
+ it "returns correct events" do
55
+ access.store_events(events)
56
+
57
+ stored_events = access.find_events_by_aggregate_id(aggregate_id)
58
+ expect(stored_events.size).to eq(events.size)
59
+ expect(stored_events).to all(respond_to(:merge))
60
+ stored_events.each { |event|
61
+ expect(event[:aggregate_id]).to eql aggregate_id
62
+ }
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,129 @@
1
+ # require 'spec_helper'
2
+ # require 'securerandom'
3
+
4
+ # module SandthornDriverEventStore
5
+ # describe EventStore do
6
+ # before(:each) { prepare_for_test }
7
+ # let(:test_events_a) do
8
+ # e = []
9
+ # e << {aggregate_version: 1, event_name: "new", event_data: "---\n:method_name: new\n:method_args: []\n:attribute_deltas:\n- :attribute_name: :@aggregate_id\n :old_value: \n :new_value: 0a74e545-be84-4506-8b0a-73e947856327\n"}
10
+ # e << {aggregate_version: 2, event_name: "foo", event_data: "A2"}
11
+ # e << {aggregate_version: 3, event_name: "bard", event_data: "A3"}
12
+ # end
13
+ # let(:aggregate_id_a) { SecureRandom.uuid }
14
+ # let(:test_events_b) do
15
+ # e = []
16
+ # e << {aggregate_version: 1, event_name: "new", event_data: "B1" }
17
+ # e << {aggregate_version: 2, event_name: "foo", event_data: "B2"}
18
+ # e << {aggregate_version: 3, event_name: "bar", event_data: "B3"}
19
+ # end
20
+ # let(:aggregate_id_b) { SecureRandom.uuid }
21
+ # let(:test_events_c) do
22
+ # e = []
23
+ # e << {aggregate_version: 1, event_name: "new", event_data: "C1" }
24
+ # end
25
+ # let(:test_events_c_2) do
26
+ # e = []
27
+ # e << {aggregate_version: 2, event_name: "flubber", event_data: "C2" }
28
+ # end
29
+ # let(:aggregate_id_c) { SecureRandom.uuid }
30
+ # before(:each) do
31
+ # event_store.save_events test_events_a, aggregate_id_a, SandthornDriverEventStore::EventStore
32
+ # event_store.save_events test_events_c, aggregate_id_c, String
33
+ # event_store.save_events test_events_b, aggregate_id_b, SandthornDriverEventStore::EventStore
34
+ # event_store.save_events test_events_c_2, aggregate_id_c, String
35
+ # end
36
+
37
+ # let(:event) { event_store.find(aggregate_id_c).first }
38
+
39
+ # it "returns events that can be merged" do
40
+ # expect(event).to respond_to(:merge)
41
+ # end
42
+
43
+ # context "when using get_events" do
44
+ # context "and using take" do
45
+ # let(:events) {event_store.get_events stream_name: "test", after_sequence_number: nil, take: 2}
46
+ # it "should find 2 events" do
47
+ # expect(events.length).to eql 2
48
+ # end
49
+ # end
50
+ # context "and combining args" do
51
+ # let(:events) do
52
+ # all = event_store.get_events after_sequence_number: 0
53
+ # first_seq_number = all[0][:sequence_number]
54
+ # event_store.get_events after_sequence_number: first_seq_number, take: 100
55
+ # end
56
+ # it "should find 7 events" do
57
+ # expect(events.length).to eql 7
58
+ # end
59
+
60
+ # end
61
+ # context "and getting events for SandthornDriverEventStore::EventStore, and String after 0" do
62
+ # let(:events) {event_store.get_events after_sequence_number: 0, aggregate_types: [SandthornDriverEventStore::EventStore, String]}
63
+ # it "should find 5 events" do
64
+ # expect(events.length).to eql 5
65
+ # end
66
+ # it "should be in sequence_number order" do
67
+ # check = 0
68
+ # events.each { |e| expect(e[:sequence_number]).to be > check; check = e[:sequence_number] }
69
+ # end
70
+ # it "should contain only events for aggregate_id_a and aggregate_id_c" do
71
+ # events.each { |e| expect([aggregate_id_a, aggregate_id_c].include?(e[:aggregate_id])).to be_truthy }
72
+ # end
73
+ # end
74
+ # context "and getting events for SandthornDriverEventStore::EventStore after 0" do
75
+ # let(:events) {event_store.get_events after_sequence_number: 0, aggregate_types: [SandthornDriverEventStore::EventStore]}
76
+ # it "should find 3 events" do
77
+ # expect(events.length).to eql 3
78
+ # end
79
+ # it "should be in sequence_number order" do
80
+ # check = 0
81
+ # events.each { |e| expect(e[:sequence_number]).to be > check; check = e[:sequence_number] }
82
+ # end
83
+ # it "should contain only events for aggregate_id_a" do
84
+ # events.each { |e| expect(e[:aggregate_id]).to eql aggregate_id_a }
85
+ # end
86
+ # end
87
+ # end
88
+ # context "when using :get_new_events_after_event_id_matching_classname to get events" do
89
+ # context "and getting events for SandthornDriverEventStore::EventStore after 0" do
90
+ # let(:events) {event_store.get_new_events_after_event_id_matching_classname 0, SandthornDriverEventStore::EventStore}
91
+ # it "should find 3 events" do
92
+ # expect(events.length).to eql 3
93
+ # end
94
+ # it "should be in sequence_number order" do
95
+ # check = 0
96
+ # events.each { |e| expect(e[:sequence_number]).to be > check; check = e[:sequence_number] }
97
+ # end
98
+ # it "should contain only events for aggregate_id_a" do
99
+ # events.each { |e| expect(e[:aggregate_id]).to eql aggregate_id_a }
100
+ # end
101
+ # it "should be able to get events after a sequence number" do
102
+ # new_from = events[1][:sequence_number]
103
+ # ev = event_store.get_new_events_after_event_id_matching_classname new_from, SandthornDriverEventStore::EventStore
104
+ # expect(ev.last[:aggregate_version]).to eql 3
105
+ # expect(ev.length).to eql 1
106
+ # end
107
+ # it "should be able to limit the number of results" do
108
+ # ev = event_store.get_new_events_after_event_id_matching_classname 0, SandthornDriverEventStore::EventStore, take: 2
109
+ # expect(ev.length).to eql 2
110
+ # expect(ev.last[:aggregate_version]).to eql 2
111
+ # end
112
+ # end
113
+ # context "and getting events for String after 0" do
114
+ # let(:events) {event_store.get_new_events_after_event_id_matching_classname 0, "String"}
115
+ # it "should find 3 events" do
116
+ # expect(events.length).to eql 2
117
+ # end
118
+ # it "should be in sequence_number order" do
119
+ # check = 0
120
+ # events.each { |e| expect(e[:sequence_number]).to be > check; check = e[:sequence_number] }
121
+ # end
122
+ # it "should contain only events for aggregate_id_c" do
123
+ # events.each { |e| expect(e[:aggregate_id]).to eql aggregate_id_c }
124
+ # end
125
+ # end
126
+ # end
127
+
128
+ # end
129
+ # end
@@ -0,0 +1,95 @@
1
+ #Copied from 'http_eventstore' spec
2
+
3
+ require 'json'
4
+
5
+ module HttpEventstore
6
+ class InMemoryEs
7
+
8
+ def initialize(endpoint, port, page_size)
9
+ @endpoint = Endpoint.new(endpoint, port)
10
+ @page_size = page_size
11
+ @event_store = {}
12
+ end
13
+ attr_reader :endpoint, :page_size, :event_store
14
+
15
+ def append_to_stream(stream_name, event_data, expected_version = nil)
16
+ unless event_store.key?(stream_name)
17
+ event_store[stream_name] = []
18
+ end
19
+
20
+ id = event_store[stream_name].length
21
+ event_store[stream_name].unshift({'eventId' => event_data.event_id, 'data' => event_data.data.to_json, 'eventType' => event_data.type, 'positionEventNumber' => id})
22
+
23
+ end
24
+
25
+ def append_events_to_stream(stream_name, events_data=[], expected_version = nil)
26
+ unless event_store.key?(stream_name)
27
+ event_store[stream_name] = []
28
+ end
29
+ events_data.each do |event_data|
30
+ id = event_store[stream_name].length
31
+ event_store[stream_name].unshift({'eventId' => event_data.event_id, 'data' => event_data.data.to_json, 'eventType' => event_data.type, 'positionEventNumber' => id})
32
+ end
33
+ end
34
+
35
+ def delete_stream(stream_name, hard_delete)
36
+ event_store.delete(stream_name)
37
+ end
38
+
39
+ def read_stream_page(uri)
40
+ params = uri.scan(/\/(\w+)\/(\d+)/)
41
+ stream_name = params[0][0]
42
+ last_index = params[0][1].to_i
43
+ direction = params[1][0]
44
+ count = params[1][1].to_i
45
+ if direction == 'next'
46
+ read_stream_backward(stream_name, last_index, count)
47
+ else
48
+ read_stream_forward(stream_name, last_index, count)
49
+ end
50
+ end
51
+
52
+ def read_stream_backward(stream_name, start, count)
53
+ if event_store.key?(stream_name)
54
+ start_index = start == :head ? event_store[stream_name].length - 1 : start
55
+ last_index = start_index - count
56
+ entries = event_store[stream_name].select do |event|
57
+ event['positionEventNumber'] > last_index && event['positionEventNumber'] <= start_index
58
+ end
59
+ { 'entries' => entries, 'links' => links(last_index, stream_name, 'next', entries, count)}
60
+ end
61
+ end
62
+
63
+ def read_stream_forward(stream_name, start_index, count, long_pool = 0)
64
+ if event_store.key?(stream_name)
65
+ last_index = start_index + count
66
+ entries = event_store[stream_name].reverse.select do |event|
67
+ event['positionEventNumber'] < last_index && event['positionEventNumber'] >= start_index
68
+ end
69
+ { 'entries' => entries.reverse!, 'links' => links(last_index, stream_name, 'previous', entries, count)}
70
+ end
71
+ end
72
+
73
+ def reset!
74
+ @event_store = {}
75
+ end
76
+
77
+ def endpoint
78
+ Endpoint.new('127.0.0.1', 2113)
79
+ end
80
+
81
+ private
82
+
83
+ def links(batch_size, stream_name, direction, entries, count)
84
+ if entries.empty? || batch_size < 0
85
+ []
86
+ else
87
+ [{
88
+ 'uri' => "http://#{endpoint.url}/streams/#{stream_name}/#{batch_size}/#{direction}/#{count}",
89
+ 'relation' => direction
90
+ }]
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,16 @@
1
+ require 'http_eventstore'
2
+ require 'in_memory_es'
3
+
4
+ module SandthornDriverEventStore
5
+ class InMemoryEventStoreDriver
6
+
7
+ def initialize
8
+ @connection = HttpEventstore::Connection.new { |config| config.client = HttpEventstore::InMemoryEs.new("localhost", 2113, 20) }
9
+ end
10
+
11
+ def execute
12
+ yield @connection
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ module SandthornDriverEventStore
5
+ describe EventStore do
6
+ context "when saving a prefectly sane event stream" do
7
+ let(:test_events) do
8
+ e = []
9
+ e << {aggregate_version: 1, event_name: "new", event_args: {:method_name=>"new", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"aggregate_id", :old_value=>nil, :new_value=>"e147e4bb-e98d-4008-ae9a-0bccce314d7b"}]}}
10
+ e << {aggregate_version: 2, event_name: "foo", event_args: {:method_name=>"foo", :method_args=>[], :attribute_deltas=>[{:attribute_name=>"aggregate_id", :old_value=>nil, :new_value=>"e147e4bb-e98d-4008-ae9a-0bccce314d7b"}]}}
11
+ e << {aggregate_version: 3, event_name: "flubber", event_args: {:method_name=>"flubber", :method_args=>["bar"], :attribute_deltas=>[{:attribute_name=>"aggregate_id", :old_value=>nil, :new_value=>"e147e4bb-e98d-4008-ae9a-0bccce314d7b"}]}}
12
+ end
13
+
14
+ let(:aggregate_id) { SecureRandom.uuid }
15
+
16
+ it "should be able to save and retrieve events on the aggregate" do
17
+ event_store.save_events test_events, aggregate_id, String
18
+ events = event_store.find aggregate_id
19
+ expect(events.length).to eql test_events.length
20
+ end
21
+
22
+ it "should have correct keys when asking for events" do
23
+ event_store.save_events test_events, aggregate_id, String
24
+ events = event_store.find aggregate_id
25
+ event = events.first
26
+ expect(event[:event_args]).to eql(test_events.first[:event_args])
27
+ expect(event[:event_name]).to eql("new")
28
+ expect(event[:aggregate_id]).to eql aggregate_id
29
+ expect(event[:aggregate_version]).to eql 1
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ require 'sandthorn_driver_event_store'
2
+ require 'ap'
3
+ require 'uuidtools'
4
+ require 'in_memory_event_store_driver'
5
+
6
+ # This file was generated by the `rspec --init` command. Conventionally, all
7
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
8
+ # Require this file using `require "spec_helper"` to ensure that it is only
9
+ # loaded once.
10
+ #
11
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
12
+ RSpec.configure do |config|
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+ config.filter_run_excluding benchmark: true
16
+ config.order = 'random'
17
+ end
18
+ def prepare_for_test context: :test
19
+ # migrator.send(:clear_for_test)
20
+ end
21
+
22
+ def event_store_driver_memory
23
+ SandthornDriverEventStore::InMemoryEventStoreDriver.new
24
+ end
25
+
26
+ def event_store_driver
27
+ event_store.driver
28
+ end
29
+
30
+ def event_store
31
+ @event_store ||= SandthornDriverEventStore.driver host: "localhost", port: 2113, page_size: 20
32
+ return @event_store
33
+ end
metadata ADDED
@@ -0,0 +1,265 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sandthorn_driver_event_store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Morgan Hallgren
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: gem-release
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-doc
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: awesome_print
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: autotest-standalone
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: uuidtools
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: ruby-beautify
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: msgpack
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: snappy
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: guard-rspec
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ description: Event Store driver for Sandthorn
196
+ email:
197
+ - morgan.hallgren@gmail.com
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files: []
201
+ files:
202
+ - ".autotest"
203
+ - ".gitignore"
204
+ - ".rspec"
205
+ - ".ruby-gemset"
206
+ - ".ruby-version"
207
+ - ".travis.yml"
208
+ - Gemfile
209
+ - Guardfile
210
+ - LICENSE.txt
211
+ - README.md
212
+ - Rakefile
213
+ - lib/sandthorn_driver_event_store.rb
214
+ - lib/sandthorn_driver_event_store/access.rb
215
+ - lib/sandthorn_driver_event_store/access/event_access.rb
216
+ - lib/sandthorn_driver_event_store/errors.rb
217
+ - lib/sandthorn_driver_event_store/event_query.rb
218
+ - lib/sandthorn_driver_event_store/event_store.rb
219
+ - lib/sandthorn_driver_event_store/event_store_driver.rb
220
+ - lib/sandthorn_driver_event_store/utilities.rb
221
+ - lib/sandthorn_driver_event_store/utilities/array.rb
222
+ - lib/sandthorn_driver_event_store/version.rb
223
+ - lib/sandthorn_driver_event_store/wrappers.rb
224
+ - lib/sandthorn_driver_event_store/wrappers/event_wrapper.rb
225
+ - sandthorn_driver_event_store.gemspec
226
+ - spec/driver_interface_spec.rb
227
+ - spec/event_access_spec.rb
228
+ - spec/get_events_spec.rb
229
+ - spec/in_memory_es.rb
230
+ - spec/in_memory_event_store_driver.rb
231
+ - spec/saving_events_spec.rb
232
+ - spec/spec_helper.rb
233
+ homepage: ''
234
+ licenses:
235
+ - MIT
236
+ metadata: {}
237
+ post_install_message:
238
+ rdoc_options: []
239
+ require_paths:
240
+ - lib
241
+ required_ruby_version: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - ">="
244
+ - !ruby/object:Gem::Version
245
+ version: '2.0'
246
+ required_rubygems_version: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
251
+ requirements: []
252
+ rubyforge_project:
253
+ rubygems_version: 2.4.3
254
+ signing_key:
255
+ specification_version: 4
256
+ summary: Event Store driver for Sandthorn
257
+ test_files:
258
+ - spec/driver_interface_spec.rb
259
+ - spec/event_access_spec.rb
260
+ - spec/get_events_spec.rb
261
+ - spec/in_memory_es.rb
262
+ - spec/in_memory_event_store_driver.rb
263
+ - spec/saving_events_spec.rb
264
+ - spec/spec_helper.rb
265
+ has_rdoc: