jouba 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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