sandthorn_driver_event_store 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: