jouba 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -2
- data/Gemfile.lock +12 -4
- data/README.md +113 -0
- data/VERSION +1 -1
- data/jouba.gemspec +20 -13
- data/lib/jouba.rb +20 -46
- data/lib/jouba/aggregate.rb +51 -39
- data/lib/jouba/cache.rb +40 -0
- data/lib/jouba/event.rb +27 -7
- data/lib/jouba/key.rb +11 -0
- data/lib/jouba/store.rb +76 -0
- data/spec/integration/customer_spec.rb +83 -0
- data/spec/lib/jouba/aggregate_spec.rb +88 -47
- data/spec/lib/jouba/cache_spec.rb +0 -0
- data/spec/lib/jouba/event_spec.rb +74 -10
- data/spec/lib/jouba/key_spec.rb +32 -0
- data/spec/lib/jouba_spec.rb +36 -182
- data/spec/spec_helper.rb +0 -7
- metadata +28 -10
- data/README.rdoc +0 -19
- data/lib/jouba/exceptions.rb +0 -3
- data/lib/jouba/stores.rb +0 -27
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Jouba::Key do
|
4
|
+
let(:name) { 'Customer' }
|
5
|
+
let(:id) { '1' }
|
6
|
+
let(:key) { 'Customer.1' }
|
7
|
+
|
8
|
+
describe '.serialize(name, id)' do
|
9
|
+
subject { described_class.serialize(name, id) }
|
10
|
+
|
11
|
+
it 'serialize a name and id' do
|
12
|
+
expect(subject).to eq key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.deserialize(key)' do
|
17
|
+
subject { described_class.deserialize(key) }
|
18
|
+
|
19
|
+
it 'deserialize a key into name an id' do
|
20
|
+
expect(subject.name).to eq name
|
21
|
+
expect(subject.id).to eq id
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when there is more information in the key' do
|
25
|
+
let(:key) { 'Customer.1.meta.info' }
|
26
|
+
it 'parse the key properly' do
|
27
|
+
expect(subject.name).to eq name
|
28
|
+
expect(subject.id).to eq id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/spec/lib/jouba_spec.rb
CHANGED
@@ -1,213 +1,67 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Jouba do
|
4
|
+
[:Key, :Event, :Cache, :Store].each do |meth|
|
5
|
+
after { Jouba.send(meth) }
|
6
|
+
it "delegates #{meth} to config" do
|
7
|
+
expect(described_class.config).to receive(meth)
|
8
|
+
end
|
9
|
+
end
|
4
10
|
describe '.config' do
|
5
11
|
subject { described_class.config }
|
6
12
|
|
7
13
|
it { should be_a Hashie::Mash }
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
context 'when we try to access an unknown key' do
|
14
|
-
let(:unkwown_key) { :foo }
|
15
|
-
|
16
|
-
it 'fails' do
|
17
|
-
expect { subject[unkwown_key] }.to raise_error
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe '.adapters_map' do
|
23
|
-
subject { described_class.adapters_map }
|
24
|
-
let(:existing_adapter) { :sql }
|
25
|
-
let(:unknown_adapter) { :foo }
|
26
|
-
|
27
|
-
before do
|
28
|
-
Jouba.register_adapter(existing_adapter, Class)
|
29
|
-
end
|
30
|
-
|
31
|
-
after do
|
32
|
-
Jouba.adapters_map.delete(existing_adapter)
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'fail when we try to access an unknown adapter' do
|
36
|
-
expect { subject[existing_adapter] }.not_to raise_error
|
37
|
-
expect { subject[unknown_adapter] }.to raise_error
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe 'register_adapter(key, class)' do
|
42
|
-
let(:key) { :adapter }
|
43
|
-
let(:adapter_class) { double(:adapter_class) }
|
15
|
+
context 'by default' do
|
16
|
+
subject { described_class.config[key] }
|
44
17
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
after do
|
51
|
-
Jouba.adapters_map.delete(key)
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'assign the class to the key in the adapters_map' do
|
55
|
-
expect { Jouba.adapters_map[key] }.not_to raise_error
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe 'register_store(name)' do
|
60
|
-
let(:store_name) { :foo }
|
61
|
-
let(:adapter_name) { :sql }
|
62
|
-
let(:adapter_class) { double(:adapter_class) }
|
63
|
-
let(:adapter_instance) { double(:adapter_instance) }
|
64
|
-
let(:config_h) { double(:config_h) }
|
65
|
-
|
66
|
-
before do
|
67
|
-
Jouba.register_adapter(adapter_name, adapter_class)
|
68
|
-
expect { Jouba.stores[store_name] }.to raise_error
|
69
|
-
|
70
|
-
expect(described_class.config.stores).to receive(:[]).with(store_name).and_return(config_h)
|
71
|
-
config_h.stub(:adapter).and_return(adapter_name)
|
72
|
-
expect(adapter_class).to receive(:new).with(config_h).and_return(adapter_instance)
|
73
|
-
Jouba.register_store(store_name) do |c|
|
74
|
-
c.adapter = adapter_name
|
18
|
+
context 'when key is event' do
|
19
|
+
let(:key) { 'Event' }
|
20
|
+
it { expect(subject).to eq Jouba::Event }
|
75
21
|
end
|
76
|
-
end
|
77
|
-
|
78
|
-
after do
|
79
|
-
Jouba.stores.delete(store_name)
|
80
|
-
Jouba.adapters_map.delete(adapter_name)
|
81
|
-
end
|
82
|
-
|
83
|
-
subject { described_class.stores[store_name] }
|
84
|
-
|
85
|
-
it 'sets the store in the Jouba.stores' do
|
86
|
-
expect(subject).to eq adapter_instance
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
describe '.stores' do
|
91
|
-
subject { described_class.stores }
|
92
|
-
let(:existing_store) { :existing }
|
93
|
-
let(:unknown_store) { :foo }
|
94
22
|
|
95
|
-
|
96
|
-
|
97
|
-
|
23
|
+
context 'when key is key' do
|
24
|
+
let(:key) { 'Key' }
|
25
|
+
it { expect(subject).to eq Jouba::Key }
|
98
26
|
end
|
99
|
-
end
|
100
|
-
|
101
|
-
after do
|
102
|
-
Jouba.stores.delete(existing_store)
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'fail when we try to access an unknown adapter' do
|
106
|
-
expect { subject[existing_store] }.not_to raise_error
|
107
|
-
expect { subject[unknown_store] }.to raise_error
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'returns the same instance' do
|
111
|
-
expect(subject[existing_store].object_id).to eq Jouba.stores[existing_store].object_id
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
describe '.commit(aggregate, event)' do
|
116
|
-
let(:aggregate) { double(:aggregate) }
|
117
|
-
let(:event) { double(:event) }
|
118
27
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
context 'when store hasnt been able to append the events' do
|
125
|
-
before do
|
126
|
-
expect(described_class.stores[:events]).to receive(:append_events).with(aggregate, event).and_return(false)
|
28
|
+
context 'when key is cache' do
|
29
|
+
let(:key) { 'Cache' }
|
30
|
+
it { expect(subject).to be_a Jouba::Cache::Null }
|
127
31
|
end
|
128
32
|
|
129
|
-
|
130
|
-
|
33
|
+
context 'when key is key' do
|
34
|
+
let(:key) { 'Store' }
|
35
|
+
it { expect(subject).to be_a Jouba::EventStore }
|
131
36
|
end
|
132
37
|
end
|
133
38
|
end
|
134
39
|
|
135
|
-
describe '.
|
136
|
-
let(:
|
137
|
-
let(:
|
138
|
-
let(:
|
139
|
-
|
140
|
-
|
141
|
-
expect(described_class.stores[:events]).to receive(:find)
|
142
|
-
.with(aggregate_class, aggregate_id).and_return(aggregate)
|
143
|
-
expect(described_class.find(aggregate_class, aggregate_id)).to eq aggregate
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
describe '.alias_store(alias_name, target)' do
|
148
|
-
let(:alias_name) { :alias_name }
|
149
|
-
let(:target) { :target }
|
150
|
-
|
151
|
-
before do
|
152
|
-
Jouba.register_store(target) do |config|
|
153
|
-
config.adapter = :random # Check spec_helper
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
after do
|
158
|
-
Jouba.stores.delete(target)
|
159
|
-
Jouba.stores.delete(alias_name)
|
40
|
+
describe '.emit(key, name, data)' do
|
41
|
+
let(:key) { 'key' }
|
42
|
+
let(:name) { 'name' }
|
43
|
+
let(:data) { double(:data) }
|
44
|
+
let(:event_payload) do
|
45
|
+
{ key: key, name: name, data: data }
|
160
46
|
end
|
47
|
+
let(:event) { Jouba::Event.new(event_payload) }
|
161
48
|
|
162
|
-
|
163
|
-
expect { Jouba.stores[alias_name] }.to raise_error
|
164
|
-
Jouba.alias_store(alias_name, target)
|
165
|
-
expect { Jouba.stores[alias_name] }.not_to raise_error
|
166
|
-
expect(Jouba.stores[alias_name].object_id).to eq Jouba.stores[target].object_id
|
49
|
+
after { described_class.emit(key, name, data) }
|
167
50
|
|
51
|
+
it 'tracks a new event' do
|
52
|
+
expect(Jouba.Event).to receive(:new).with(event_payload).and_return(event)
|
53
|
+
expect(event).to receive(:track)
|
168
54
|
end
|
169
55
|
end
|
170
56
|
|
171
|
-
describe '.
|
57
|
+
describe '.stream(key, params)' do
|
172
58
|
let(:key) { 'key' }
|
173
|
-
|
174
|
-
expect(Jouba.stores[:lock]).to receive(:locked?).with(key)
|
175
|
-
described_class.locked?(key)
|
176
|
-
end
|
177
|
-
end
|
59
|
+
let(:params) { { foo: 'bar' } }
|
178
60
|
|
179
|
-
|
180
|
-
let(:key) { 'key' }
|
181
|
-
|
182
|
-
context 'when the key is not locked' do
|
183
|
-
before do
|
184
|
-
expect(Jouba.stores[:lock]).to receive(:lock!).with(key)
|
185
|
-
expect(Jouba.stores[:lock]).to receive(:unlock!).with(key)
|
186
|
-
expect(Jouba).to receive(:locked?).with(key).and_return(false)
|
187
|
-
end
|
61
|
+
after { described_class.stream(key, params) }
|
188
62
|
|
189
|
-
|
190
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
context 'when yield fails' do
|
194
|
-
let(:exception) { StandardError.new }
|
195
|
-
|
196
|
-
it 'make sure to release the lock' do
|
197
|
-
expect { described_class.with_lock(key) { fail(exception) } }.to raise_error
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
context 'when the key is locked' do
|
203
|
-
before do
|
204
|
-
expect(Jouba).to receive(:locked?).with(key).and_return(true)
|
205
|
-
expect(Jouba.stores[:lock]).not_to receive(:unlock!).with(key)
|
206
|
-
end
|
207
|
-
|
208
|
-
it 'fails with a LockException' do
|
209
|
-
expect { |b| described_class.with_lock(key, &b) }.to raise_error(Jouba::LockException)
|
210
|
-
end
|
63
|
+
it 'returns the stream of events' do
|
64
|
+
expect(Jouba.Event).to receive(:stream).with(key, params)
|
211
65
|
end
|
212
66
|
end
|
213
67
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -22,12 +22,5 @@ RSpec.configure do |config|
|
|
22
22
|
config.run_all_when_everything_filtered = true
|
23
23
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
24
24
|
|
25
|
-
Jouba.register_adapter(:random, Struct.new('RandomAdapter', :config))
|
26
|
-
|
27
|
-
Jouba.register_store(:events) do |store_config|
|
28
|
-
store_config.adapter = :random
|
29
|
-
end
|
30
|
-
Jouba.alias_store(:lock, :events)
|
31
|
-
|
32
25
|
config.order = 'random'
|
33
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jouba
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gregory
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-02-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hashie
|
@@ -16,28 +16,42 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.4.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.4.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: wisper
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 1.6.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 1.6.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: locality-uuid
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: pry
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,7 +157,7 @@ executables: []
|
|
143
157
|
extensions: []
|
144
158
|
extra_rdoc_files:
|
145
159
|
- LICENSE.txt
|
146
|
-
- README.
|
160
|
+
- README.md
|
147
161
|
files:
|
148
162
|
- ".document"
|
149
163
|
- ".rspec"
|
@@ -151,18 +165,22 @@ files:
|
|
151
165
|
- Gemfile
|
152
166
|
- Gemfile.lock
|
153
167
|
- LICENSE.txt
|
154
|
-
- README.
|
168
|
+
- README.md
|
155
169
|
- Rakefile
|
156
170
|
- VERSION
|
157
171
|
- jouba.gemspec
|
158
172
|
- lib/jouba.rb
|
159
173
|
- lib/jouba/aggregate.rb
|
174
|
+
- lib/jouba/cache.rb
|
160
175
|
- lib/jouba/event.rb
|
161
|
-
- lib/jouba/
|
162
|
-
- lib/jouba/
|
176
|
+
- lib/jouba/key.rb
|
177
|
+
- lib/jouba/store.rb
|
163
178
|
- lib/jouba/version.rb
|
179
|
+
- spec/integration/customer_spec.rb
|
164
180
|
- spec/lib/jouba/aggregate_spec.rb
|
181
|
+
- spec/lib/jouba/cache_spec.rb
|
165
182
|
- spec/lib/jouba/event_spec.rb
|
183
|
+
- spec/lib/jouba/key_spec.rb
|
166
184
|
- spec/lib/jouba_spec.rb
|
167
185
|
- spec/spec_helper.rb
|
168
186
|
homepage: http://github.com/gregory/jouba
|
data/README.rdoc
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
= jouba
|
2
|
-
|
3
|
-
Description goes here.
|
4
|
-
|
5
|
-
== Contributing to jouba
|
6
|
-
|
7
|
-
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
-
* Fork the project.
|
10
|
-
* Start a feature/bugfix branch.
|
11
|
-
* Commit and push until you are happy with your contribution.
|
12
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
-
|
15
|
-
== Copyright
|
16
|
-
|
17
|
-
Copyright (c) 2014 gregory. See LICENSE.txt for
|
18
|
-
further details.
|
19
|
-
|
data/lib/jouba/exceptions.rb
DELETED
data/lib/jouba/stores.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Jouba
|
2
|
-
module Stores
|
3
|
-
def self.append_events(_, _)
|
4
|
-
fail NotImplementedError
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.find(_)
|
8
|
-
fail NotImplementedError
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.count
|
12
|
-
fail NotImplementedError
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.locked?(_)
|
16
|
-
fail NotImplementedError
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.lock!(_)
|
20
|
-
fail NotImplementedError
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.unlock!(_)
|
24
|
-
fail NotImplementedError
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|