estore 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.simplecov +14 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +28 -0
- data/Gemfile +1 -10
- data/README.md +12 -11
- data/estore.gemspec +2 -1
- data/lib/estore/commands.rb +5 -3
- data/lib/estore/commands/append.rb +9 -8
- data/lib/estore/commands/base.rb +18 -18
- data/lib/estore/commands/read.rb +28 -0
- data/lib/estore/commands/{read_batch.rb → reads/batch.rb} +11 -4
- data/lib/estore/commands/{read_forward.rb → reads/forward.rb} +15 -3
- data/lib/estore/commands/subscription.rb +1 -12
- data/lib/estore/commands/{catch_up_subscription.rb → subscriptions/catch_up.rb} +12 -13
- data/lib/estore/commands/subscriptions/live.rb +15 -0
- data/lib/estore/connection.rb +2 -2
- data/lib/estore/connection/buffer.rb +2 -2
- data/lib/estore/connection_context.rb +10 -2
- data/lib/estore/message_extensions.rb +5 -3
- data/lib/estore/package.rb +8 -0
- data/lib/estore/session.rb +2 -2
- data/lib/estore/version.rb +1 -1
- data/spec/integration/session_spec.rb +18 -3
- data/spec/spec_helper.rb +3 -5
- data/spec/unit/estore/commands/append_spec.rb +35 -0
- data/spec/unit/estore/commands/reads/batch_spec.rb +34 -0
- data/spec/unit/estore/commands/reads/forward_spec.rb +34 -0
- data/spec/unit/estore/connection_context_spec.rb +53 -0
- data/spec/unit/estore/connection_mock.rb +22 -0
- data/spec/unit/estore/package_spec.rb +9 -0
- metadata +35 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bdd806cd7c12b665577e8f686f9a14225fd102ce
|
4
|
+
data.tar.gz: 519601368cfc6e8d30912510fadbd2b866eff7e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e554e45930e79aeb9c1eca4cdc5437bc8a26f3228c02b71d63c4c5e1c573cac7268e83d51bcd96bf619c7b430d1ca22eaaffbe224c44b139f6e4e031b1306dd
|
7
|
+
data.tar.gz: 1ef5c432b902a710b61886b4e0ba5eee9448c021978add9d93b033b9b747dd5a693e019c960b855ea005cc350f781b9bdea73351f85905b958c7d5e0a348a29b
|
data/.simplecov
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
SimpleCov.start do
|
2
|
+
project_name 'estore'
|
3
|
+
|
4
|
+
# Filter out the following files
|
5
|
+
add_filter '/spec/'
|
6
|
+
add_filter '/gems/'
|
7
|
+
add_filter '/config/'
|
8
|
+
|
9
|
+
require 'coveralls'
|
10
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
11
|
+
Coveralls::SimpleCov::Formatter,
|
12
|
+
SimpleCov::Formatter::HTMLFormatter
|
13
|
+
]
|
14
|
+
end
|
data/.travis.yml
CHANGED
@@ -5,7 +5,7 @@ bundler_args: --without yard guard benchmarks tools
|
|
5
5
|
env:
|
6
6
|
global:
|
7
7
|
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
8
|
-
- CODECLIMATE_REPO_TOKEN=
|
8
|
+
- CODECLIMATE_REPO_TOKEN=c571107e67aa90424c4d4256a3ad20127d1c64104b83d43f3ec23630c7aa19da
|
9
9
|
install:
|
10
10
|
- "wget -nc http://download.geteventstore.com/binaries/EventStore-OSS-Linux-v3.0.3.tar.gz"
|
11
11
|
- "tar -xvzf EventStore-OSS-Linux-v3.0.3.tar.gz"
|
data/CHANGELOG.md
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
## v0.1.2 - 2015-04-05
|
2
|
+
### Added
|
3
|
+
* Errors during commands reject promises too
|
4
|
+
* Unit testing started
|
5
|
+
|
6
|
+
[Compare v0.1.1...v0.1.2](https://github.com/rom-rb/rom-event_store/compare/v0.1.1...v0.1.2)
|
7
|
+
|
8
|
+
## v0.1.1 - 2015-04-05
|
9
|
+
### Added
|
10
|
+
* General errors during connection reject command promises
|
11
|
+
|
12
|
+
[Compare v0.1.0...v0.1.1](https://github.com/rom-rb/rom-event_store/compare/v0.1.0...v0.1.1)
|
13
|
+
|
14
|
+
## v0.1.0 - 2015-04-03
|
15
|
+
### Added
|
16
|
+
* General refactoring using the concept of `Command`
|
17
|
+
|
18
|
+
[Compare v0.0.2...v0.1.0](https://github.com/rom-rb/rom-event_store/compare/v0.0.2...v0.1.0)
|
19
|
+
|
20
|
+
## v0.0.2 - 2015-04-01
|
21
|
+
### Added
|
22
|
+
* Gem name changed to `estore`
|
23
|
+
* Minor refactoring
|
24
|
+
|
25
|
+
[Compare v0.0.1...v0.0.2](https://github.com/rom-rb/rom-event_store/compare/v0.0.1...v0.0.2)
|
26
|
+
|
27
|
+
## v0.0.1 - 2015-03-31
|
28
|
+
Forked from [eventstore-ruby](https://github.com/mathieuravaux/eventstore-ruby)
|
data/Gemfile
CHANGED
@@ -4,14 +4,5 @@ source 'https://rubygems.org'
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
group :test do
|
7
|
-
gem '
|
8
|
-
gem 'codeclimate-test-reporter', require: false
|
9
|
-
end
|
10
|
-
|
11
|
-
group :tools do
|
12
|
-
gem 'rubocop'
|
13
|
-
|
14
|
-
gem 'guard'
|
15
|
-
gem 'guard-rspec'
|
16
|
-
gem 'guard-rubocop'
|
7
|
+
gem 'coveralls', require: false
|
17
8
|
end
|
data/README.md
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/estore
|
2
|
-
[travis]: https://travis-ci.org/
|
3
|
-
[gemnasium]: https://gemnasium.com/
|
4
|
-
[codeclimate]: https://codeclimate.com/github/
|
5
|
-
[
|
6
|
-
[
|
2
|
+
[travis]: https://travis-ci.org/eventstore-rb/estore
|
3
|
+
[gemnasium]: https://gemnasium.com/eventstore-rb/estore
|
4
|
+
[codeclimate]: https://codeclimate.com/github/eventstore-rb/estore
|
5
|
+
[coveralls]: https://coveralls.io/r/eventstore-rb/estore
|
6
|
+
[inchpages]: http://inch-ci.org/github/eventstore-rb/estore
|
7
|
+
[gitter]: https://gitter.im/eventstore-rb/estore?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
7
8
|
|
8
9
|
# Estore
|
9
10
|
|
10
11
|
[![Gem Version](https://badge.fury.io/rb/estore.svg)][gem]
|
11
|
-
[![Build Status](https://travis-ci.org/
|
12
|
-
[![Dependency Status](https://gemnasium.com/
|
13
|
-
[![Code Climate](https://codeclimate.com/github/
|
14
|
-
[![
|
15
|
-
[![Inline docs](http://inch-ci.org/github/
|
16
|
-
[![Join the chat at https://gitter.im/
|
12
|
+
[![Build Status](https://travis-ci.org/eventstore-rb/estore.svg?branch=master)][travis]
|
13
|
+
[![Dependency Status](https://gemnasium.com/eventstore-rb/estore.png)][gemnasium]
|
14
|
+
[![Code Climate](https://codeclimate.com/github/eventstore-rb/estore/badges/gpa.svg)][codeclimate]
|
15
|
+
[![Coverage Status](https://coveralls.io/repos/eventstore-rb/estore/badge.svg)][coveralls]
|
16
|
+
[![Inline docs](http://inch-ci.org/github/eventstore-rb/estore.svg?branch=master)][inchpages]
|
17
|
+
[![Join the chat at https://gitter.im/eventstore-rb/estore](https://badges.gitter.im/Join%20Chat.svg)][gitter]
|
17
18
|
|
18
19
|
An [Event Store](http://geteventstore.com/) driver for Ruby
|
19
20
|
|
data/estore.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['mathieu.ravaux@gmail.com', 'hector0193@gmail.com']
|
11
11
|
spec.summary = 'An Event Store driver for Ruby'
|
12
12
|
spec.description = 'TCP driver to read and write events to Event Store'
|
13
|
-
spec.homepage = 'https://github.com/
|
13
|
+
spec.homepage = 'https://github.com/eventstore-rb/estore'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency 'bundler'
|
25
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
26
26
|
spec.add_development_dependency 'rubocop', '~> 0.28.0'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.1'
|
27
28
|
end
|
data/lib/estore/commands.rb
CHANGED
@@ -2,7 +2,9 @@ require 'estore/commands/promise'
|
|
2
2
|
require 'estore/commands/base'
|
3
3
|
require 'estore/commands/append'
|
4
4
|
require 'estore/commands/ping'
|
5
|
-
require 'estore/commands/
|
6
|
-
require 'estore/commands/
|
5
|
+
require 'estore/commands/read'
|
6
|
+
require 'estore/commands/reads/batch'
|
7
|
+
require 'estore/commands/reads/forward'
|
7
8
|
require 'estore/commands/subscription'
|
8
|
-
require 'estore/commands/
|
9
|
+
require 'estore/commands/subscriptions/live'
|
10
|
+
require 'estore/commands/subscriptions/catch_up'
|
@@ -3,9 +3,7 @@ module Estore
|
|
3
3
|
class Append
|
4
4
|
include Command
|
5
5
|
|
6
|
-
|
7
|
-
json: 1
|
8
|
-
}
|
6
|
+
handle WriteEventsCompleted => :completed
|
9
7
|
|
10
8
|
def initialize(connection, stream, events, options = {})
|
11
9
|
super(connection)
|
@@ -13,23 +11,22 @@ module Estore
|
|
13
11
|
end
|
14
12
|
|
15
13
|
def call
|
14
|
+
register!
|
15
|
+
|
16
16
|
msg = WriteEvents.new(
|
17
17
|
event_stream_id: @stream,
|
18
18
|
expected_version: @options[:expected_version] || -2,
|
19
19
|
events: Array(@events).map { |event| new_event(event) },
|
20
20
|
require_master: true
|
21
21
|
)
|
22
|
-
|
23
|
-
register!
|
24
22
|
write('WriteEvents', msg)
|
23
|
+
|
25
24
|
promise
|
26
25
|
end
|
27
26
|
|
28
|
-
def
|
27
|
+
def completed(response)
|
29
28
|
remove!
|
30
29
|
|
31
|
-
response = decode(WriteEventsCompleted, message)
|
32
|
-
|
33
30
|
if response.result == OperationResult::Success
|
34
31
|
promise.fulfill(response)
|
35
32
|
else
|
@@ -39,6 +36,10 @@ module Estore
|
|
39
36
|
|
40
37
|
private
|
41
38
|
|
39
|
+
CONTENT_TYPES = {
|
40
|
+
json: 1
|
41
|
+
}
|
42
|
+
|
42
43
|
def new_event(event)
|
43
44
|
uuid = event[:id] || SecureRandom.uuid
|
44
45
|
content_type = event.fetch(:content_type, :json)
|
data/lib/estore/commands/base.rb
CHANGED
@@ -3,6 +3,12 @@ module Estore
|
|
3
3
|
module Command
|
4
4
|
attr_reader :uuid
|
5
5
|
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
base.singleton_class.class_eval { attr_accessor :handlers }
|
9
|
+
base.handlers = {}
|
10
|
+
end
|
11
|
+
|
6
12
|
def initialize(connection)
|
7
13
|
@connection = connection
|
8
14
|
@uuid = SecureRandom.uuid
|
@@ -28,26 +34,20 @@ module Estore
|
|
28
34
|
@promise ||= Promise.new(@uuid)
|
29
35
|
end
|
30
36
|
|
31
|
-
def
|
32
|
-
|
33
|
-
rescue => error
|
34
|
-
puts "Protobuf decoding error on connection #{object_id}"
|
35
|
-
puts type: type, message: message
|
36
|
-
puts error.backtrace
|
37
|
-
raise error
|
38
|
-
end
|
37
|
+
def handle(message)
|
38
|
+
handler = self.class.handlers[message.class]
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
require_master: false
|
48
|
-
)
|
40
|
+
if handler
|
41
|
+
send(handler, message) unless handler == :ignore
|
42
|
+
else
|
43
|
+
$stderr.puts "#{message.class} arrived but not handled by "\
|
44
|
+
"command #{self.class}"
|
45
|
+
end
|
46
|
+
end
|
49
47
|
|
50
|
-
|
48
|
+
module ClassMethods
|
49
|
+
def handle(hash)
|
50
|
+
handlers.update(hash)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Estore
|
2
|
+
module Commands
|
3
|
+
module ReadStreamForward
|
4
|
+
def read(stream, from, limit)
|
5
|
+
msg = ReadStreamEvents.new(
|
6
|
+
event_stream_id: stream,
|
7
|
+
from_event_number: from,
|
8
|
+
max_count: limit,
|
9
|
+
resolve_link_tos: true,
|
10
|
+
require_master: false
|
11
|
+
)
|
12
|
+
|
13
|
+
write('ReadStreamEventsForward', msg)
|
14
|
+
end
|
15
|
+
|
16
|
+
def error(response)
|
17
|
+
case response.result
|
18
|
+
when ReadStreamEventsCompleted::ReadStreamResult::AccessDenied
|
19
|
+
:access_denied
|
20
|
+
when ReadStreamEventsCompleted::ReadStreamResult::Error
|
21
|
+
response.error ? response.error : 'No message given'
|
22
|
+
else
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -2,7 +2,9 @@ module Estore
|
|
2
2
|
module Commands
|
3
3
|
class ReadBatch
|
4
4
|
include Command
|
5
|
-
include
|
5
|
+
include ReadStreamForward
|
6
|
+
|
7
|
+
handle ReadStreamEventsForwardCompleted => :completed
|
6
8
|
|
7
9
|
def initialize(connection, stream, from, limit)
|
8
10
|
super(connection)
|
@@ -15,10 +17,15 @@ module Estore
|
|
15
17
|
promise
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
20
|
+
def completed(response)
|
19
21
|
remove!
|
20
|
-
|
21
|
-
|
22
|
+
error = error(response)
|
23
|
+
|
24
|
+
if error
|
25
|
+
promise.reject error
|
26
|
+
else
|
27
|
+
promise.fulfill(Array(response.events))
|
28
|
+
end
|
22
29
|
end
|
23
30
|
end
|
24
31
|
end
|
@@ -2,7 +2,9 @@ module Estore
|
|
2
2
|
module Commands
|
3
3
|
class ReadForward
|
4
4
|
include Command
|
5
|
-
include
|
5
|
+
include ReadStreamForward
|
6
|
+
|
7
|
+
handle ReadStreamEventsCompleted => :batch_completed
|
6
8
|
|
7
9
|
def initialize(connection, stream, from, batch_size = nil, &block)
|
8
10
|
super(connection)
|
@@ -20,8 +22,7 @@ module Estore
|
|
20
22
|
promise
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
24
|
-
response = decode(ReadStreamEventsCompleted, message)
|
25
|
+
def keep_reading(response)
|
25
26
|
events = Array(response.events)
|
26
27
|
|
27
28
|
@from += events.size
|
@@ -34,6 +35,17 @@ module Estore
|
|
34
35
|
promise.fulfill(@block ? nil : @events)
|
35
36
|
end
|
36
37
|
end
|
38
|
+
|
39
|
+
def batch_completed(response)
|
40
|
+
error = error(response)
|
41
|
+
|
42
|
+
if error
|
43
|
+
remove!
|
44
|
+
promise.reject error
|
45
|
+
else
|
46
|
+
keep_reading(response)
|
47
|
+
end
|
48
|
+
end
|
37
49
|
end
|
38
50
|
end
|
39
51
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module Estore
|
2
2
|
module Commands
|
3
|
-
|
4
|
-
include Command
|
5
|
-
|
3
|
+
module Subscription
|
6
4
|
def initialize(connection, stream, options = {})
|
7
5
|
super(connection)
|
8
6
|
@has_finished = false
|
@@ -10,10 +8,6 @@ module Estore
|
|
10
8
|
@resolve_link_tos = options.fetch(:resolve_link_tos, true)
|
11
9
|
end
|
12
10
|
|
13
|
-
def finished?
|
14
|
-
@has_finished
|
15
|
-
end
|
16
|
-
|
17
11
|
def call
|
18
12
|
start
|
19
13
|
end
|
@@ -39,11 +33,6 @@ module Estore
|
|
39
33
|
@handler = block
|
40
34
|
end
|
41
35
|
|
42
|
-
def handle(message, type)
|
43
|
-
dispatch(decode(StreamEventAppeared, message).event) if
|
44
|
-
type == 'StreamEventAppeared'
|
45
|
-
end
|
46
|
-
|
47
36
|
def dispatch(event)
|
48
37
|
@position = event.original_event_number
|
49
38
|
@handler.call(event)
|
@@ -1,11 +1,14 @@
|
|
1
1
|
module Estore
|
2
2
|
module Commands
|
3
|
-
class CatchUpSubscription
|
3
|
+
class CatchUpSubscription
|
4
4
|
include Command
|
5
|
+
include Subscription
|
6
|
+
|
7
|
+
handle StreamEventAppeared => :event_appeared,
|
8
|
+
SubscriptionConfirmation => :ignore
|
5
9
|
|
6
10
|
def initialize(connection, stream, from, options = {})
|
7
|
-
super(connection, options)
|
8
|
-
@stream = stream
|
11
|
+
super(connection, stream, options)
|
9
12
|
@from = from
|
10
13
|
@batch = options[:batch_size]
|
11
14
|
@mutex = Mutex.new
|
@@ -42,18 +45,14 @@ module Estore
|
|
42
45
|
@queue.find_all { |event| event.original_event_number > @position }
|
43
46
|
end
|
44
47
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
unless @caught_up
|
50
|
-
@mutex.synchronize do
|
51
|
-
@queue << event unless @caught_up
|
52
|
-
end
|
48
|
+
def event_appeared(response)
|
49
|
+
unless @caught_up
|
50
|
+
@mutex.synchronize do
|
51
|
+
@queue << response.event unless @caught_up
|
53
52
|
end
|
54
|
-
|
55
|
-
dispatch(event) if @caught_up
|
56
53
|
end
|
54
|
+
|
55
|
+
dispatch(response.event) if @caught_up
|
57
56
|
end
|
58
57
|
end
|
59
58
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Estore
|
2
|
+
module Commands
|
3
|
+
class LiveSubscription
|
4
|
+
include Command
|
5
|
+
include Commands::Subscription
|
6
|
+
|
7
|
+
handle StreamEventAppeared => :event_appeared,
|
8
|
+
SubscriptionConfirmation => :ignore
|
9
|
+
|
10
|
+
def event_appeared(response)
|
11
|
+
dispatch(response.event)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/estore/connection.rb
CHANGED
@@ -34,11 +34,11 @@ module Estore
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
-
def on_received_package(
|
37
|
+
def on_received_package(type, package, uuid, _flags)
|
38
38
|
if type == 'HeartbeatRequestCommand'
|
39
39
|
write(SecureRandom.uuid, 'HeartbeatResponseCommand')
|
40
40
|
else
|
41
|
-
@context.dispatch(uuid,
|
41
|
+
@context.dispatch(uuid, Package.decode(type, package))
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -42,9 +42,9 @@ module Estore
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def handle(pkg)
|
45
|
-
code, flags, uuid_bytes,
|
45
|
+
code, flags, uuid_bytes, package = parse(pkg)
|
46
46
|
command = Estore::Connection.command_name(code)
|
47
|
-
@handler.call(
|
47
|
+
@handler.call(command, package, Package.parse_uuid(uuid_bytes), flags)
|
48
48
|
end
|
49
49
|
|
50
50
|
def parse(pkg)
|
@@ -20,15 +20,23 @@ module Estore
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def dispatch(uuid, message
|
23
|
+
def dispatch(uuid, message)
|
24
24
|
command = @commands[uuid]
|
25
|
-
command.handle(message
|
25
|
+
command.handle(message) if command
|
26
|
+
rescue => error
|
27
|
+
command.reject! error
|
28
|
+
remove(command)
|
29
|
+
end
|
30
|
+
|
31
|
+
def empty?
|
32
|
+
@commands.empty?
|
26
33
|
end
|
27
34
|
|
28
35
|
def on_error(error)
|
29
36
|
# TODO: Error handling
|
30
37
|
@mutex.synchronize do
|
31
38
|
@commands.each { |_uuid, command| command.reject! error }
|
39
|
+
@commands = {}
|
32
40
|
end
|
33
41
|
end
|
34
42
|
end
|
@@ -13,7 +13,9 @@ module Estore
|
|
13
13
|
original_event.event_number
|
14
14
|
end
|
15
15
|
end
|
16
|
-
end
|
17
16
|
|
18
|
-
|
19
|
-
|
17
|
+
ResolvedEvent.send(:include, OriginalEventMixin)
|
18
|
+
ResolvedIndexedEvent.send(:include, OriginalEventMixin)
|
19
|
+
|
20
|
+
ReadStreamEventsForwardCompleted = ReadStreamEventsCompleted
|
21
|
+
end
|
data/lib/estore/package.rb
CHANGED
@@ -23,6 +23,14 @@ module Estore
|
|
23
23
|
uuid.scan(/[0-9a-f]{4}/).map { |x| x.to_i(16) }.pack('n*')
|
24
24
|
end
|
25
25
|
|
26
|
+
def self.decode(type, message)
|
27
|
+
message.empty? ? nil : Estore.const_get(type).decode(message)
|
28
|
+
rescue => error
|
29
|
+
puts 'Decoding error:'
|
30
|
+
puts type: type, message: message
|
31
|
+
raise error
|
32
|
+
end
|
33
|
+
|
26
34
|
def self.parse_uuid(bytes)
|
27
35
|
a, b, c, d, e, f, g, h =
|
28
36
|
*bytes.unpack('n*').map { |n| n.to_s(16) }.map { |n| n.rjust(4, '0') }
|
data/lib/estore/session.rb
CHANGED
@@ -26,7 +26,7 @@ module Estore
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def ping
|
29
|
-
command(Commands::Ping)
|
29
|
+
command(Commands::Ping).call
|
30
30
|
end
|
31
31
|
|
32
32
|
def read(stream, options = {})
|
@@ -56,7 +56,7 @@ module Estore
|
|
56
56
|
if options[:from]
|
57
57
|
command(Commands::CatchUpSubscription, stream, options[:from], options)
|
58
58
|
else
|
59
|
-
command(Commands::
|
59
|
+
command(Commands::LiveSubscription, stream, options)
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
data/lib/estore/version.rb
CHANGED
@@ -39,6 +39,10 @@ describe Estore::Session do
|
|
39
39
|
stream
|
40
40
|
end
|
41
41
|
|
42
|
+
it 'pings the Event Store' do
|
43
|
+
Timeout.timeout(5) { session.ping.sync }
|
44
|
+
end
|
45
|
+
|
42
46
|
it 'reads all the events from a stream' do
|
43
47
|
stream = session.read(stream_with(200)).sync
|
44
48
|
|
@@ -57,6 +61,12 @@ describe Estore::Session do
|
|
57
61
|
expect(stream).to have(15).events.starting_at(10)
|
58
62
|
end
|
59
63
|
|
64
|
+
it 'reads no events if the stream does not exist' do
|
65
|
+
stream = session.read('doesnotexist').sync
|
66
|
+
|
67
|
+
expect(stream).to have(0).events
|
68
|
+
end
|
69
|
+
|
60
70
|
it 'allows to make a live subscription' do
|
61
71
|
stream = random_stream
|
62
72
|
received = []
|
@@ -79,18 +89,23 @@ describe Estore::Session do
|
|
79
89
|
stream_with(2100, stream)
|
80
90
|
|
81
91
|
sub = session.subscription(stream, from: 30)
|
92
|
+
|
82
93
|
sub.on_event do |event|
|
94
|
+
# Events received during processing should be
|
95
|
+
# received later too
|
96
|
+
sleep 1 if received.size < 1
|
83
97
|
received << event
|
84
98
|
puts "Receiving... #{received.size}"
|
85
99
|
end
|
86
|
-
sub.start
|
87
100
|
|
88
101
|
Thread.new do
|
89
|
-
|
102
|
+
30.times do
|
90
103
|
stream_with(2, stream)
|
91
104
|
end
|
92
105
|
end
|
93
106
|
|
94
|
-
|
107
|
+
sub.start
|
108
|
+
|
109
|
+
expect(received).to have(2130).events.starting_at(30).before(20.seconds)
|
95
110
|
end
|
96
111
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'codeclimate-test-reporter'
|
4
|
-
CodeClimate::TestReporter.start
|
5
|
-
end
|
6
|
-
|
2
|
+
require 'simplecov'
|
7
3
|
require 'estore'
|
8
4
|
|
9
5
|
trap 'TTIN' do
|
@@ -39,6 +35,8 @@ RSpec::Matchers.define :have do |expectation|
|
|
39
35
|
expect(wrapper.event.event_number).to be(index + @start)
|
40
36
|
end
|
41
37
|
end
|
38
|
+
|
39
|
+
true
|
42
40
|
end
|
43
41
|
|
44
42
|
chain(:events) {}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'unit/estore/connection_mock'
|
3
|
+
|
4
|
+
describe Estore::Commands::Append do
|
5
|
+
subject(:append) do
|
6
|
+
Estore::Commands::Append.new(connection, 'test-stream', events)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:events) do
|
10
|
+
[{ type: 'something', data: 'something else' }, { type: 'stuff' }]
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:connection) { Estore::ConnectionMock.new }
|
14
|
+
let(:promise) { append.call }
|
15
|
+
|
16
|
+
before do
|
17
|
+
promise
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'writes a message with the events to the connection buffer' do
|
21
|
+
_uuid, _type, msg = connection.buffer.first
|
22
|
+
|
23
|
+
expect(msg.events.size).to be(events.size)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'rejects the promise on errors' do
|
27
|
+
append.handle(
|
28
|
+
Estore::WriteEventsCompleted.new(
|
29
|
+
result: Estore::OperationResult::AccessDenied
|
30
|
+
)
|
31
|
+
)
|
32
|
+
|
33
|
+
expect { promise.sync }.to raise_exception
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'unit/estore/connection_mock'
|
3
|
+
|
4
|
+
describe Estore::Commands::ReadBatch do
|
5
|
+
subject(:read) do
|
6
|
+
Estore::Commands::ReadBatch.new(connection, 'test-stream', 5, 10)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:message) { Estore::ReadStreamEventsCompleted }
|
10
|
+
let(:connection) { Estore::ConnectionMock.new }
|
11
|
+
let(:promise) { read.call }
|
12
|
+
|
13
|
+
def result(type)
|
14
|
+
message.new(
|
15
|
+
result: message::ReadStreamResult.const_get(type)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
before do
|
20
|
+
promise
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'rejects the promise on errors' do
|
24
|
+
read.handle(result(:Error))
|
25
|
+
|
26
|
+
expect { promise.sync }.to raise_exception
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'rejects the promise when access is denied' do
|
30
|
+
read.handle(result(:AccessDenied))
|
31
|
+
|
32
|
+
expect { promise.sync }.to raise_exception
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'unit/estore/connection_mock'
|
3
|
+
|
4
|
+
describe Estore::Commands::ReadForward do
|
5
|
+
subject(:read) do
|
6
|
+
Estore::Commands::ReadForward.new(connection, 'test-stream', 5, 10)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:message) { Estore::ReadStreamEventsCompleted }
|
10
|
+
let(:connection) { Estore::ConnectionMock.new }
|
11
|
+
let(:promise) { read.call }
|
12
|
+
|
13
|
+
def result(type)
|
14
|
+
message.new(
|
15
|
+
result: message::ReadStreamResult.const_get(type)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
before do
|
20
|
+
promise
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'rejects the promise on errors' do
|
24
|
+
read.handle(result(:Error))
|
25
|
+
|
26
|
+
expect { promise.sync }.to raise_exception
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'rejects the promise when access is denied' do
|
30
|
+
read.handle(result(:AccessDenied))
|
31
|
+
|
32
|
+
expect { promise.sync }.to raise_exception
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Estore::ConnectionContext do
|
4
|
+
subject(:context) { Estore::ConnectionContext.new }
|
5
|
+
|
6
|
+
let(:bad_command) do
|
7
|
+
BadCommand = Class.new do
|
8
|
+
attr_reader :error
|
9
|
+
|
10
|
+
def uuid
|
11
|
+
'test'
|
12
|
+
end
|
13
|
+
|
14
|
+
def reject!(error)
|
15
|
+
@error = error
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle(_msg)
|
19
|
+
boom
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
BadCommand.new
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
context.register(bad_command)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'rejects a command when exceptions happen inside the command' do
|
31
|
+
context.dispatch('test', 'something')
|
32
|
+
|
33
|
+
expect(bad_command.error).to be_instance_of(NameError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'removes all commands when exceptions happen inside the command' do
|
37
|
+
context.dispatch('test', 'something')
|
38
|
+
|
39
|
+
expect(context.empty?).to be_truthy
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'rejects a command when exceptions happen in the connection' do
|
43
|
+
context.on_error(StandardError.new('this is an error'))
|
44
|
+
|
45
|
+
expect(bad_command.error).to be_instance_of(StandardError)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'removes all commands when exceptions happen in the connection' do
|
49
|
+
context.on_error('this is an error')
|
50
|
+
|
51
|
+
expect(context.empty?).to be_truthy
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Estore
|
2
|
+
class ConnectionMock
|
3
|
+
attr_reader :buffer, :registry
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@buffer = []
|
7
|
+
@registry = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def write(uuid, type, msg)
|
11
|
+
@buffer << [uuid, type, msg]
|
12
|
+
end
|
13
|
+
|
14
|
+
def register(command)
|
15
|
+
@registry[command.uuid] = command
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove(command)
|
19
|
+
@registry.delete(command.uuid)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: estore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mathieu Ravaux
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-04-
|
12
|
+
date: 2015-04-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: beefcake
|
@@ -81,6 +81,20 @@ dependencies:
|
|
81
81
|
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: 0.28.0
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '3.1'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '3.1'
|
84
98
|
description: TCP driver to read and write events to Event Store
|
85
99
|
email:
|
86
100
|
- mathieu.ravaux@gmail.com
|
@@ -91,6 +105,7 @@ extra_rdoc_files: []
|
|
91
105
|
files:
|
92
106
|
- ".gitignore"
|
93
107
|
- ".rubocop.yml"
|
108
|
+
- ".simplecov"
|
94
109
|
- ".travis.yml"
|
95
110
|
- ".yardopts"
|
96
111
|
- CHANGELOG.md
|
@@ -103,12 +118,14 @@ files:
|
|
103
118
|
- lib/estore/commands.rb
|
104
119
|
- lib/estore/commands/append.rb
|
105
120
|
- lib/estore/commands/base.rb
|
106
|
-
- lib/estore/commands/catch_up_subscription.rb
|
107
121
|
- lib/estore/commands/ping.rb
|
108
122
|
- lib/estore/commands/promise.rb
|
109
|
-
- lib/estore/commands/
|
110
|
-
- lib/estore/commands/
|
123
|
+
- lib/estore/commands/read.rb
|
124
|
+
- lib/estore/commands/reads/batch.rb
|
125
|
+
- lib/estore/commands/reads/forward.rb
|
111
126
|
- lib/estore/commands/subscription.rb
|
127
|
+
- lib/estore/commands/subscriptions/catch_up.rb
|
128
|
+
- lib/estore/commands/subscriptions/live.rb
|
112
129
|
- lib/estore/connection.rb
|
113
130
|
- lib/estore/connection/buffer.rb
|
114
131
|
- lib/estore/connection/protocol.rb
|
@@ -121,8 +138,14 @@ files:
|
|
121
138
|
- lib/estore/version.rb
|
122
139
|
- spec/integration/session_spec.rb
|
123
140
|
- spec/spec_helper.rb
|
141
|
+
- spec/unit/estore/commands/append_spec.rb
|
142
|
+
- spec/unit/estore/commands/reads/batch_spec.rb
|
143
|
+
- spec/unit/estore/commands/reads/forward_spec.rb
|
144
|
+
- spec/unit/estore/connection_context_spec.rb
|
145
|
+
- spec/unit/estore/connection_mock.rb
|
146
|
+
- spec/unit/estore/package_spec.rb
|
124
147
|
- vendor/proto/ClientMessageDtos.proto
|
125
|
-
homepage: https://github.com/
|
148
|
+
homepage: https://github.com/eventstore-rb/estore
|
126
149
|
licenses:
|
127
150
|
- MIT
|
128
151
|
metadata: {}
|
@@ -149,3 +172,9 @@ summary: An Event Store driver for Ruby
|
|
149
172
|
test_files:
|
150
173
|
- spec/integration/session_spec.rb
|
151
174
|
- spec/spec_helper.rb
|
175
|
+
- spec/unit/estore/commands/append_spec.rb
|
176
|
+
- spec/unit/estore/commands/reads/batch_spec.rb
|
177
|
+
- spec/unit/estore/commands/reads/forward_spec.rb
|
178
|
+
- spec/unit/estore/connection_context_spec.rb
|
179
|
+
- spec/unit/estore/connection_mock.rb
|
180
|
+
- spec/unit/estore/package_spec.rb
|