jouba 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2c7a94f5f635563e588fb7cde10b683a38ec995
4
- data.tar.gz: dd81e20474b69a36966f5faee97e1f9276505c73
3
+ metadata.gz: 94a0e68b0a8935c5a7b34943bae5e8fa8c978f3d
4
+ data.tar.gz: b09485abe09d0965c75fb8f5dfdc33ac05a23b87
5
5
  SHA512:
6
- metadata.gz: 7e712caf41bee379cd54a34519b260693341631da4115bfd2f0c19d673a3a777a0e2f0560a69e441416413259b46265fa2d47d035e85afa3acc9d87b53e3e958
7
- data.tar.gz: a334785038fca5d65d4b0274e92af8cbf8555e4b56f71287e7e1f1d07cfa5c3c14a9905f2daee0044f3154c8f74fc0759bdace392be6a467728a3efabff01ba7
6
+ metadata.gz: 9a6681a756a9d6d08d6087891c6d93a53102656e3d9ca61c393e089702dfd89e7229ecc167371e8a58de067874cd518f33e46ed29cd1b58a849f25b0f1c10c5d
7
+ data.tar.gz: 7909d8679c12b9ffaee9778a96b59fa61116d2708501ed2659bff4f3f34e6d6c9b5e1b6f50090ff36c849f936b307b3ea4932d0ce61971a586464bb7335a7459
data/.rspec CHANGED
@@ -1 +1,2 @@
1
1
  --color
2
+ --format=documentation
data/Gemfile CHANGED
@@ -1,14 +1,14 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gem 'hashie', '~> 3.3.2'
4
- gem 'wisper', '~>1.3.0'
3
+ gem 'hashie', '~> 3.3'
4
+ gem 'wisper', '~>1.3'
5
5
 
6
6
  group :development do
7
- gem 'pry'
8
- gem "rspec", "~> 2.14.0"
7
+ gem 'pry', '~> 0'
8
+ gem "rspec", "~> 2.14"
9
9
  gem "rdoc", "~> 3.12"
10
10
  gem "bundler", "~> 1.0"
11
- gem "jeweler"
12
- gem "simplecov", ">= 0"
13
- gem 'rubocop', '~> 0.23.0'
11
+ gem "jeweler", ' ~> 2.0'
12
+ gem "simplecov", "~> 0"
13
+ gem 'rubocop', '~> 0.23'
14
14
  end
@@ -3,7 +3,7 @@ GEM
3
3
  specs:
4
4
  addressable (2.3.6)
5
5
  ast (2.0.0)
6
- builder (3.1.4)
6
+ builder (3.2.2)
7
7
  coderay (1.1.0)
8
8
  descendants_tracker (0.0.4)
9
9
  thread_safe (~> 0.3, >= 0.3.1)
@@ -11,14 +11,14 @@ GEM
11
11
  docile (1.1.3)
12
12
  faraday (0.9.0)
13
13
  multipart-post (>= 1.2, < 3)
14
- git (1.2.6)
15
- github_api (0.11.3)
14
+ git (1.2.8)
15
+ github_api (0.12.2)
16
16
  addressable (~> 2.3)
17
- descendants_tracker (~> 0.0.1)
17
+ descendants_tracker (~> 0.0.4)
18
18
  faraday (~> 0.8, < 0.10)
19
- hashie (>= 1.2)
19
+ hashie (>= 3.3)
20
20
  multi_json (>= 1.7.5, < 2.0)
21
- nokogiri (~> 1.6.0)
21
+ nokogiri (~> 1.6.3)
22
22
  oauth2
23
23
  hashie (3.3.2)
24
24
  highline (1.6.21)
@@ -32,15 +32,15 @@ GEM
32
32
  rake
33
33
  rdoc
34
34
  json (1.8.1)
35
- jwt (1.0.0)
35
+ jwt (1.2.0)
36
36
  method_source (0.8.2)
37
- mini_portile (0.6.0)
37
+ mini_portile (0.6.1)
38
38
  multi_json (1.10.1)
39
39
  multi_xml (0.5.5)
40
40
  multipart-post (2.0.0)
41
- nokogiri (1.6.2.1)
42
- mini_portile (= 0.6.0)
43
- oauth2 (0.9.4)
41
+ nokogiri (1.6.5)
42
+ mini_portile (~> 0.6.0)
43
+ oauth2 (1.0.0)
44
44
  faraday (>= 0.8, < 0.10)
45
45
  jwt (~> 1.0)
46
46
  multi_json (~> 1.3)
@@ -54,7 +54,7 @@ GEM
54
54
  coderay (~> 1.0)
55
55
  method_source (~> 0.8)
56
56
  slop (~> 3.4)
57
- rack (1.5.2)
57
+ rack (1.6.0)
58
58
  rainbow (2.0.0)
59
59
  rake (10.4.2)
60
60
  rdoc (3.12.2)
@@ -88,11 +88,11 @@ PLATFORMS
88
88
 
89
89
  DEPENDENCIES
90
90
  bundler (~> 1.0)
91
- hashie (~> 3.3.2)
92
- jeweler
93
- pry
91
+ hashie (~> 3.3)
92
+ jeweler (~> 2.0)
93
+ pry (~> 0)
94
94
  rdoc (~> 3.12)
95
- rspec (~> 2.14.0)
96
- rubocop (~> 0.23.0)
97
- simplecov
98
- wisper (~> 1.3.0)
95
+ rspec (~> 2.14)
96
+ rubocop (~> 0.23)
97
+ simplecov (~> 0)
98
+ wisper (~> 1.3)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.1.0
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: jouba 0.0.1 ruby lib
5
+ # stub: jouba 0.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "jouba"
9
- s.version = "0.0.1"
9
+ s.version = "0.1.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["gregory"]
14
- s.date = "2014-12-30"
14
+ s.date = "2015-01-02"
15
15
  s.description = "Jouba is a tool set for event sourcing: aggregate root, entities and storage"
16
16
  s.email = "greg2502@gmail.com"
17
17
  s.extra_rdoc_files = [
@@ -32,9 +32,11 @@ Gem::Specification.new do |s|
32
32
  "lib/jouba.rb",
33
33
  "lib/jouba/aggregate.rb",
34
34
  "lib/jouba/event.rb",
35
+ "lib/jouba/exceptions.rb",
35
36
  "lib/jouba/stores.rb",
36
37
  "lib/jouba/version.rb",
37
38
  "spec/lib/jouba/aggregate_spec.rb",
39
+ "spec/lib/jouba/event_spec.rb",
38
40
  "spec/lib/jouba_spec.rb",
39
41
  "spec/spec_helper.rb"
40
42
  ]
@@ -47,36 +49,36 @@ Gem::Specification.new do |s|
47
49
  s.specification_version = 4
48
50
 
49
51
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
- s.add_runtime_dependency(%q<hashie>, ["~> 3.3.2"])
51
- s.add_runtime_dependency(%q<wisper>, ["~> 1.3.0"])
52
- s.add_development_dependency(%q<pry>, [">= 0"])
53
- s.add_development_dependency(%q<rspec>, ["~> 2.14.0"])
52
+ s.add_runtime_dependency(%q<hashie>, ["~> 3.3"])
53
+ s.add_runtime_dependency(%q<wisper>, ["~> 1.3"])
54
+ s.add_development_dependency(%q<pry>, ["~> 0"])
55
+ s.add_development_dependency(%q<rspec>, ["~> 2.14"])
54
56
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
55
57
  s.add_development_dependency(%q<bundler>, ["~> 1.0"])
56
- s.add_development_dependency(%q<jeweler>, [">= 0"])
57
- s.add_development_dependency(%q<simplecov>, [">= 0"])
58
- s.add_development_dependency(%q<rubocop>, ["~> 0.23.0"])
58
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0"])
59
+ s.add_development_dependency(%q<simplecov>, ["~> 0"])
60
+ s.add_development_dependency(%q<rubocop>, ["~> 0.23"])
59
61
  else
60
- s.add_dependency(%q<hashie>, ["~> 3.3.2"])
61
- s.add_dependency(%q<wisper>, ["~> 1.3.0"])
62
- s.add_dependency(%q<pry>, [">= 0"])
63
- s.add_dependency(%q<rspec>, ["~> 2.14.0"])
62
+ s.add_dependency(%q<hashie>, ["~> 3.3"])
63
+ s.add_dependency(%q<wisper>, ["~> 1.3"])
64
+ s.add_dependency(%q<pry>, ["~> 0"])
65
+ s.add_dependency(%q<rspec>, ["~> 2.14"])
64
66
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
65
67
  s.add_dependency(%q<bundler>, ["~> 1.0"])
66
- s.add_dependency(%q<jeweler>, [">= 0"])
67
- s.add_dependency(%q<simplecov>, [">= 0"])
68
- s.add_dependency(%q<rubocop>, ["~> 0.23.0"])
68
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
69
+ s.add_dependency(%q<simplecov>, ["~> 0"])
70
+ s.add_dependency(%q<rubocop>, ["~> 0.23"])
69
71
  end
70
72
  else
71
- s.add_dependency(%q<hashie>, ["~> 3.3.2"])
72
- s.add_dependency(%q<wisper>, ["~> 1.3.0"])
73
- s.add_dependency(%q<pry>, [">= 0"])
74
- s.add_dependency(%q<rspec>, ["~> 2.14.0"])
73
+ s.add_dependency(%q<hashie>, ["~> 3.3"])
74
+ s.add_dependency(%q<wisper>, ["~> 1.3"])
75
+ s.add_dependency(%q<pry>, ["~> 0"])
76
+ s.add_dependency(%q<rspec>, ["~> 2.14"])
75
77
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
76
78
  s.add_dependency(%q<bundler>, ["~> 1.0"])
77
- s.add_dependency(%q<jeweler>, [">= 0"])
78
- s.add_dependency(%q<simplecov>, [">= 0"])
79
- s.add_dependency(%q<rubocop>, ["~> 0.23.0"])
79
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
80
+ s.add_dependency(%q<simplecov>, ["~> 0"])
81
+ s.add_dependency(%q<rubocop>, ["~> 0.23"])
80
82
  end
81
83
  end
82
84
 
@@ -2,6 +2,7 @@ require 'hashie'
2
2
  require 'wisper'
3
3
 
4
4
  require 'jouba/version'
5
+ require 'jouba/exceptions'
5
6
  require 'jouba/event'
6
7
  require 'jouba/aggregate'
7
8
  require 'jouba/stores'
@@ -9,46 +10,55 @@ require 'jouba/stores'
9
10
  module Jouba
10
11
  module_function
11
12
 
12
- class Configuration < Hashie::Mash
13
- def store
14
- @store ||= Hashie::Mash.new
13
+ def adapters_map
14
+ @adapters_map ||= Hashie::Mash.new do |_, k|
15
+ fail("Unknown adapter #{k}, valids are: #{@adapters_map.keys.join(' ')}")
15
16
  end
16
17
  end
17
18
 
18
- class<<self
19
- attr_accessor :adapter
20
- attr_reader :adapters_map
19
+ def alias_store(alias_name, target)
20
+ stores[alias_name] = stores[target]
21
21
  end
22
22
 
23
- def adapter
24
- @adapter ||= config.store.adapter
23
+ def commit(aggregate, event)
24
+ yield if stores[:events].append_events(aggregate, event)
25
25
  end
26
26
 
27
27
  def config
28
- @config ||= Configuration.new
28
+ @config ||= Hashie::Mash.new { |_, k| fail("Unknown key #{k}, please use configure to set it up") }
29
29
  end
30
30
 
31
- def configure
32
- config.tap { |configuration| yield(configuration) }
31
+ def find(aggregate_class, aggregate_id)
32
+ stores[:events].find(aggregate_class, aggregate_id)
33
33
  end
34
34
 
35
- def commit(aggregate, event)
36
- store.append_events(aggregate, event)
35
+ def locked?(key)
36
+ stores[:lock].locked?(key)
37
37
  end
38
38
 
39
- def find(aggregate_class, aggregate_id)
40
- store.find(aggregate_class, aggregate_id)
39
+ def register_adapter(key, klass)
40
+ adapters_map[key] = klass
41
41
  end
42
42
 
43
- def adapters_map
44
- @adapters_map ||= {}
43
+ def register_store(name)
44
+ yield(config.stores!.send("#{name}!"))
45
+ store_config = config.stores[name]
46
+ adapter = adapters_map[store_config.adapter]
47
+ stores[name] = adapter.new(store_config)
45
48
  end
46
49
 
47
- def register_adapter(key, klass)
48
- adapters_map.merge!(key => klass.new)
50
+ def stores
51
+ @stores ||= Hashie::Mash.new { |_, k| fail("Unknown store #{k}, valids are: #{@stores.keys.join(' ')}") }
49
52
  end
50
53
 
51
- def store
52
- adapters_map[adapter] || fail("unknown adapter - valids are #{adapters_map.keys.join(', ')}")
54
+ def with_lock(key)
55
+ fail(LockException, "#{key} has been locked") if Jouba.locked?(key)
56
+
57
+ begin
58
+ stores[:lock].lock!(key)
59
+ yield
60
+ ensure
61
+ stores[:lock].unlock!(key)
62
+ end
53
63
  end
54
64
  end
@@ -13,8 +13,25 @@ module Jouba
13
13
  Jouba.find(self, id)
14
14
  end
15
15
 
16
- def build_from_events(uuid, events)
17
- new { |aggregate| aggregate[:uuid] = uuid }.apply_events(events)
16
+ def build_from_events(uuid, events = [])
17
+ new.tap do |aggregate|
18
+ aggregate[:uuid] = uuid
19
+ aggregate.apply_events(events)
20
+
21
+ after_initialize_blocks.each do |block|
22
+ block.call(aggregate)
23
+ end
24
+ end
25
+ end
26
+
27
+ def after_initialize(&block)
28
+ after_initialize_blocks.push(block)
29
+ end
30
+
31
+ private
32
+
33
+ def after_initialize_blocks
34
+ @after_initialize_blocks ||= []
18
35
  end
19
36
  end
20
37
 
@@ -26,8 +43,15 @@ module Jouba
26
43
  event = Event.build(event_name, args)
27
44
 
28
45
  apply_events(event)
29
- Jouba.commit(self, event)
30
- publish(event_name, args)
46
+ Jouba.commit(self, event) do
47
+ publish(event_name, args)
48
+ end
49
+ end
50
+
51
+ def commit_with_lock(event_name, args, lock_key)
52
+ Jouba.with_lock(lock_key) do
53
+ commit(event_name, args)
54
+ end
31
55
  end
32
56
 
33
57
  def apply_events(events)
@@ -0,0 +1,3 @@
1
+ module Jouba
2
+ class LockException < StandardError; end
3
+ end
@@ -1,4 +1,27 @@
1
1
  module Jouba
2
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
3
26
  end
4
27
  end
@@ -2,11 +2,7 @@ require 'ostruct'
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Jouba::Aggregate do
5
- let(:aggregate_class) do
6
- Class.new do
7
- include Jouba::Aggregate
8
- end
9
- end
5
+ let(:aggregate_class) { Class.new { include Jouba::Aggregate } }
10
6
 
11
7
  subject { aggregate_class }
12
8
 
@@ -19,6 +15,41 @@ describe Jouba::Aggregate do
19
15
  end
20
16
  end
21
17
 
18
+ describe '.build_from_events(uuid, events)' do
19
+ let(:aggregate) { aggregate_class.new }
20
+ let(:uuid) { '123' }
21
+ let(:events) { [double(:event)] }
22
+
23
+ it 'build the aggregate by applying the events' do
24
+ expect(aggregate_class).to receive(:new).and_return(aggregate)
25
+ expect(aggregate).to receive(:[]=).with(:uuid, uuid)
26
+ expect(aggregate).to receive(:apply_events).with(events)
27
+ aggregate_class.build_from_events(uuid, events)
28
+ end
29
+
30
+ context 'when after_initialize_blocks is not empty' do
31
+ let(:observer) { double(:observer) }
32
+
33
+ before do
34
+ aggregate_class.after_initialize do |aggregate|
35
+ aggregate.subscribe(observer)
36
+ end
37
+ end
38
+
39
+ it 'apply the blocks once initialized' do
40
+ expect(aggregate_class).to receive(:new).and_return(aggregate)
41
+ expect(aggregate).to receive(:apply_events).with(events)
42
+ expect(aggregate).to receive(:[]=).with(:uuid, uuid)
43
+ expect(aggregate).to receive(:subscribe).with(observer)
44
+ aggregate_class.build_from_events(uuid, events)
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ describe '.after_initialize(&block)' do
51
+ end
52
+
22
53
  describe '#uuid' do
23
54
  let(:uuids) { (1..3).map { aggregate_class.new.uuid } }
24
55
 
@@ -34,7 +65,7 @@ describe Jouba::Aggregate do
34
65
  let(:event) { Jouba::Event.new(name: event_name, data: data) }
35
66
 
36
67
  it 'append the event to the store' do
37
- expect(Jouba.store).to receive(:append_events).with(aggregate, event)
68
+ expect(Jouba.stores[:events]).to receive(:append_events).with(aggregate, event)
38
69
  expect(Jouba::Event).to receive(:build).with(event_name, data).and_return(event)
39
70
  expect(aggregate).to receive(event_name).with(data)
40
71
  aggregate.commit(event_name, data)
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Jouba::Event do
4
+ let(:event_name) { 'event_name' }
5
+ let(:data) { [:foo, 1, 'bar', { foo: 'bar' }, [1, 2]] }
6
+ let(:occured_at) { double(:time) }
7
+
8
+ describe '.build(event_name, data)' do
9
+ before do
10
+ Time.stub_chain(:now, :utc).and_return(occured_at)
11
+ end
12
+
13
+ subject { described_class.build(event_name, data) }
14
+
15
+ it 'build an event' do
16
+ expect(subject.name).to eq event_name
17
+ expect(subject.data).to eq data
18
+ expect(subject.occured_at).to eq occured_at
19
+ end
20
+ end
21
+ end
@@ -4,49 +4,112 @@ describe Jouba do
4
4
  describe '.config' do
5
5
  subject { described_class.config }
6
6
 
7
- it { should be_a Jouba::Configuration }
7
+ it { should be_a Hashie::Mash }
8
8
 
9
- it 'has a store reader' do
10
- expect(subject.store).to be_a Hashie::Mash
9
+ it 'has a stores reader' do
10
+ expect(subject.stores).to be_a Hashie::Mash
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
11
19
  end
12
20
  end
13
21
 
14
- describe '.connection' do
15
- let(:store_adapter) { Class }
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
16
30
 
17
- subject { described_class.store }
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) }
18
44
 
19
45
  before do
20
- Jouba.register_adapter(:sql, store_adapter)
46
+ expect { Jouba.adapters_map[key] }.to raise_error
47
+ Jouba.register_adapter(key, Class)
21
48
  end
22
49
 
23
50
  after do
24
- Jouba.adapter = :random
51
+ Jouba.adapters_map.delete(key)
25
52
  end
26
53
 
27
- context 'when adapter has been registered' do
28
- before do
29
- Jouba.adapter = :sql
30
- end
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
31
58
 
32
- it 'return a new instance' do
33
- expect(subject).to be_a(store_adapter)
34
- end
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) }
35
65
 
36
- it 'return the same instance' do
37
- expect(subject.object_id).to eq Jouba.store.object_id
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
38
75
  end
76
+ end
39
77
 
40
- context 'when unknown adapter' do
41
- before do
42
- Jouba.adapter = :foo
43
- end
78
+ after do
79
+ Jouba.stores.delete(store_name)
80
+ Jouba.adapters_map.delete(adapter_name)
81
+ end
44
82
 
45
- it 'raise an error' do
46
- expect { subject }.to raise_error
47
- end
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
+
95
+ before do
96
+ Jouba.register_store(existing_store) do |config|
97
+ config.adapter = :random # Check spec_helper
48
98
  end
49
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
50
113
  end
51
114
 
52
115
  describe '.commit(aggregate, event)' do
@@ -54,8 +117,18 @@ describe Jouba do
54
117
  let(:event) { double(:event) }
55
118
 
56
119
  it 'commit the event to the store' do
57
- expect(described_class.store).to receive(:append_events).with(aggregate, event)
58
- described_class.commit(aggregate, event)
120
+ expect(described_class.stores[:events]).to receive(:append_events).with(aggregate, event).and_return(true)
121
+ expect { |b| described_class.commit(aggregate, event, &b) }.to yield_with_no_args
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)
127
+ end
128
+
129
+ it 'doesnt yield' do
130
+ expect { |b| described_class.commit(aggregate, event, &b) }.not_to yield_with_no_args
131
+ end
59
132
  end
60
133
  end
61
134
 
@@ -65,8 +138,76 @@ describe Jouba do
65
138
  let(:aggregate) { double(:aggregate) }
66
139
 
67
140
  it 'retrieve an aggregate from the store' do
68
- expect(described_class.store).to receive(:find).with(aggregate_class, aggregate_id).and_return(aggregate)
141
+ expect(described_class.stores[:events]).to receive(:find)
142
+ .with(aggregate_class, aggregate_id).and_return(aggregate)
69
143
  expect(described_class.find(aggregate_class, aggregate_id)).to eq aggregate
70
144
  end
71
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)
160
+ end
161
+
162
+ it 'alias the two stores' do
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
167
+
168
+ end
169
+ end
170
+
171
+ describe '.locked?(key)' do
172
+ let(:key) { 'key' }
173
+ it 'asks the lock store if a key is locked' do
174
+ expect(Jouba.stores[:lock]).to receive(:locked?).with(key)
175
+ described_class.locked?(key)
176
+ end
177
+ end
178
+
179
+ describe '.with_lock(key)' do
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
188
+
189
+ it 'yield with lock' do
190
+ expect { |b| described_class.with_lock(key, &b) }.to yield_with_no_args
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
211
+ end
212
+ end
72
213
  end
@@ -22,11 +22,12 @@ 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'))
25
+ Jouba.register_adapter(:random, Struct.new('RandomAdapter', :config))
26
26
 
27
- Jouba.configure do |jouba_config|
28
- jouba_config.store.adapter = :random
27
+ Jouba.register_store(:events) do |store_config|
28
+ store_config.adapter = :random
29
29
  end
30
+ Jouba.alias_store(:lock, :events)
30
31
 
31
32
  config.order = 'random'
32
33
  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: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gregory
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-30 00:00:00.000000000 Z
11
+ date: 2015-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -16,40 +16,40 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 3.3.2
19
+ version: '3.3'
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: 3.3.2
26
+ version: '3.3'
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: 1.3.0
33
+ version: '1.3'
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: 1.3.0
40
+ version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 2.14.0
61
+ version: '2.14'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 2.14.0
68
+ version: '2.14'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rdoc
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -98,28 +98,28 @@ dependencies:
98
98
  name: jeweler
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '2.0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '2.0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: simplecov
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ">="
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
117
  version: '0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">="
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 0.23.0
131
+ version: '0.23'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 0.23.0
138
+ version: '0.23'
139
139
  description: 'Jouba is a tool set for event sourcing: aggregate root, entities and
140
140
  storage'
141
141
  email: greg2502@gmail.com
@@ -158,9 +158,11 @@ files:
158
158
  - lib/jouba.rb
159
159
  - lib/jouba/aggregate.rb
160
160
  - lib/jouba/event.rb
161
+ - lib/jouba/exceptions.rb
161
162
  - lib/jouba/stores.rb
162
163
  - lib/jouba/version.rb
163
164
  - spec/lib/jouba/aggregate_spec.rb
165
+ - spec/lib/jouba/event_spec.rb
164
166
  - spec/lib/jouba_spec.rb
165
167
  - spec/spec_helper.rb
166
168
  homepage: http://github.com/gregory/jouba