entity_store 0.0.2 → 0.0.3
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.
- data/lib/entity_store/event.rb +8 -8
- data/lib/entity_store/event_bus.rb +6 -4
- data/lib/entity_store/store.rb +12 -11
- data/lib/entity_store/version.rb +1 -1
- data/spec/entity_store/event_spec.rb +17 -13
- data/spec/entity_store/store_spec.rb +24 -17
- metadata +17 -7
data/lib/entity_store/event.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module EntityStore
|
2
2
|
module Event
|
3
|
-
attr_accessor :entity_id
|
4
|
-
|
3
|
+
attr_accessor :entity_id, :entity_version
|
4
|
+
|
5
5
|
def initialize(attrs={})
|
6
|
-
attrs.each_pair do |key, value|
|
6
|
+
attrs.each_pair do |key, value|
|
7
7
|
send("#{key}=", value) if respond_to?("#{key}=")
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def receiver_name
|
12
12
|
elements = self.class.name.split('::')
|
13
13
|
elements[elements.count - 1].
|
@@ -16,20 +16,20 @@ module EntityStore
|
|
16
16
|
tr("-", "_").
|
17
17
|
downcase
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def attributes
|
21
21
|
Hash[*public_methods.select {|m| m =~ /\w\=$/}.collect do |m|
|
22
22
|
attribute_name = m.to_s.chop.to_sym
|
23
23
|
[attribute_name, send(attribute_name).respond_to?(:attributes) ? send(attribute_name).attributes : send(attribute_name)]
|
24
24
|
end.flatten]
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def self.included(klass)
|
28
28
|
klass.class_eval do
|
29
29
|
extend ClassMethods
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
module ClassMethods
|
34
34
|
def time_attribute(*names)
|
35
35
|
class_eval do
|
@@ -44,7 +44,7 @@ module EntityStore
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def entity_value_attribute(name, klass)
|
49
49
|
define_method(name) { instance_variable_get("@#{name}") }
|
50
50
|
define_method("#{name}=") do |value|
|
@@ -3,7 +3,9 @@ module EntityStore
|
|
3
3
|
class << self
|
4
4
|
def publish(entity_type, event)
|
5
5
|
publish_externally entity_type, event
|
6
|
-
|
6
|
+
|
7
|
+
EntityStore.logger.debug { "publishing #{event.inspect}" }
|
8
|
+
|
7
9
|
subscribers_to(event.receiver_name).each do |s|
|
8
10
|
begin
|
9
11
|
s.new.send(event.receiver_name, event)
|
@@ -17,15 +19,15 @@ module EntityStore
|
|
17
19
|
def subscribers_to(event_name)
|
18
20
|
subscribers.select { |s| s.instance_methods.include?(event_name.to_sym) }
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
def subscribers
|
22
24
|
EntityStore.event_subscribers
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
def publish_externally(entity_type, event)
|
26
28
|
external_store.add_event(entity_type, event)
|
27
29
|
end
|
28
|
-
|
30
|
+
|
29
31
|
def external_store
|
30
32
|
@_external_store ||= ExternalStore.new
|
31
33
|
end
|
data/lib/entity_store/store.rb
CHANGED
@@ -3,16 +3,16 @@ module EntityStore
|
|
3
3
|
def storage_client
|
4
4
|
@storage_client || MongoEntityStore.new
|
5
5
|
end
|
6
|
-
|
7
|
-
def add(entity)
|
8
|
-
entity.id = storage_client.add_entity(entity)
|
6
|
+
|
7
|
+
def add(entity)
|
8
|
+
entity.id = storage_client.add_entity(entity)
|
9
9
|
add_events(entity)
|
10
10
|
return entity
|
11
11
|
rescue => e
|
12
12
|
EntityStore.logger.error { "Store#add error: #{e.inspect} - #{entity.inspect}" }
|
13
13
|
raise e
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def save(entity)
|
17
17
|
# need to look at concurrency if we start storing version on client
|
18
18
|
entity.version += 1
|
@@ -23,10 +23,11 @@ module EntityStore
|
|
23
23
|
EntityStore.logger.error { "Store#save error: #{e.inspect} - #{entity.inspect}" }
|
24
24
|
raise e
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def add_events(entity)
|
28
28
|
entity.pending_events.each do |e|
|
29
29
|
e.entity_id = entity.id.to_s
|
30
|
+
e.entity_version = entity.version
|
30
31
|
storage_client.add_event(e)
|
31
32
|
end
|
32
33
|
entity.pending_events.each {|e| EventBus.publish(entity.type, e) }
|
@@ -35,22 +36,22 @@ module EntityStore
|
|
35
36
|
def get!(id)
|
36
37
|
get(id, true)
|
37
38
|
end
|
38
|
-
|
39
|
+
|
39
40
|
def get(id, raise_exception=false)
|
40
41
|
if entity = storage_client.get_entity(id, raise_exception)
|
41
|
-
storage_client.get_events(id).each { |e| e.apply(entity) }
|
42
|
-
end
|
42
|
+
storage_client.get_events(id).each { |e| e.apply(entity) }
|
43
|
+
end
|
43
44
|
return entity
|
44
45
|
end
|
45
|
-
|
46
|
+
|
46
47
|
# Public : USE AT YOUR PERIL this clears the ENTIRE data store
|
47
|
-
#
|
48
|
+
#
|
48
49
|
# Returns nothing
|
49
50
|
def clear_all
|
50
51
|
storage_client.entities.drop
|
51
52
|
storage_client.events.drop
|
52
53
|
@storage_client = nil
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
end
|
56
57
|
end
|
data/lib/entity_store/version.rb
CHANGED
@@ -16,18 +16,22 @@ end
|
|
16
16
|
describe Event do
|
17
17
|
before(:each) do
|
18
18
|
@id = random_integer
|
19
|
+
@version = random_integer
|
19
20
|
@name = random_string
|
20
21
|
@time = random_time
|
21
22
|
@town = random_string
|
22
23
|
@county = random_string
|
23
24
|
end
|
24
25
|
describe "#initialize" do
|
25
|
-
|
26
|
-
subject { DummyEvent.new({:entity_id => @id, :name => @name, :updated_at => @time, :sent_at => nil, :address => {:town => @town, :county => @county}})}
|
27
|
-
|
26
|
+
|
27
|
+
subject { DummyEvent.new({:entity_id => @id, :entity_version => @version, :name => @name, :updated_at => @time, :sent_at => nil, :address => {:town => @town, :county => @county}})}
|
28
|
+
|
28
29
|
it "should set entity_id" do
|
29
30
|
subject.entity_id.should eq(@id)
|
30
31
|
end
|
32
|
+
it "should set entity_version" do
|
33
|
+
subject.entity_version.should eq(@version)
|
34
|
+
end
|
31
35
|
it "should set name" do
|
32
36
|
subject.name.should eq(@name)
|
33
37
|
end
|
@@ -41,27 +45,27 @@ describe Event do
|
|
41
45
|
subject.address.county.should eq(@county)
|
42
46
|
end
|
43
47
|
end
|
44
|
-
|
48
|
+
|
45
49
|
describe "#attributes" do
|
46
50
|
before(:each) do
|
47
|
-
@event = DummyEvent.new(:entity_id => @id, :name => @name, :updated_at => @time, :address => DummyValue.new(:town => @town, :county => @county))
|
51
|
+
@event = DummyEvent.new(:entity_id => @id, :entity_version => @version, :name => @name, :updated_at => @time, :address => DummyValue.new(:town => @town, :county => @county))
|
48
52
|
end
|
49
|
-
|
53
|
+
|
50
54
|
subject { @event.attributes }
|
51
|
-
|
55
|
+
|
52
56
|
it "returns a hash of the attributes" do
|
53
|
-
subject.should eq({:entity_id => @id, :name => @name, :updated_at => @time, :sent_at => nil, :address => {:town => @town, :county => @county}})
|
57
|
+
subject.should eq({:entity_id => @id, :entity_version => @version, :name => @name, :updated_at => @time, :sent_at => nil, :address => {:town => @town, :county => @county}})
|
54
58
|
end
|
55
59
|
end
|
56
|
-
|
60
|
+
|
57
61
|
describe ".time_attribute" do
|
58
62
|
before(:each) do
|
59
63
|
@event = DummyEvent.new
|
60
64
|
@time = random_time
|
61
65
|
end
|
62
|
-
context "updated_at" do
|
66
|
+
context "updated_at" do
|
63
67
|
subject { @event.updated_at = @time.to_s }
|
64
|
-
|
68
|
+
|
65
69
|
it "parses the time field when added as a string" do
|
66
70
|
subject
|
67
71
|
@event.updated_at.to_i.should eq(@time.to_i)
|
@@ -69,14 +73,14 @@ describe Event do
|
|
69
73
|
end
|
70
74
|
context "sent_at" do
|
71
75
|
subject { @event.updated_at = @time.to_s }
|
72
|
-
|
76
|
+
|
73
77
|
it "parses the time field when added as a string" do
|
74
78
|
subject
|
75
79
|
@event.updated_at.to_i.should eq(@time.to_i)
|
76
80
|
end
|
77
81
|
end
|
78
82
|
end
|
79
|
-
|
83
|
+
|
80
84
|
describe ".value_attribute" do
|
81
85
|
before(:each) do
|
82
86
|
@event = DummyEvent.new
|
@@ -2,9 +2,9 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
class DummyEntity
|
4
4
|
include Entity
|
5
|
-
|
5
|
+
|
6
6
|
attr_accessor :name
|
7
|
-
|
7
|
+
|
8
8
|
def initialize(name)
|
9
9
|
@name = name
|
10
10
|
end
|
@@ -20,9 +20,9 @@ describe Store do
|
|
20
20
|
@store.stub(:add_events)
|
21
21
|
@store.stub(:storage_client) { @storage_client }
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
subject { @store.add(@entity) }
|
25
|
-
|
25
|
+
|
26
26
|
it "adds the new entity to the store" do
|
27
27
|
@storage_client.should_receive(:add_entity).with(@entity)
|
28
28
|
subject
|
@@ -33,23 +33,24 @@ describe Store do
|
|
33
33
|
end
|
34
34
|
it "returns a reference to the ride" do
|
35
35
|
subject.should eq(@entity)
|
36
|
-
end
|
36
|
+
end
|
37
37
|
end
|
38
38
|
|
39
39
|
describe "#add_events" do
|
40
40
|
before(:each) do
|
41
41
|
@entity = DummyEntity.new(random_string)
|
42
42
|
@entity.id = random_string
|
43
|
-
@entity.
|
44
|
-
@entity.pending_events << mock(Event, :entity_id= => true)
|
43
|
+
@entity.version = random_integer
|
44
|
+
@entity.pending_events << mock(Event, :entity_id= => true, :entity_version= => true)
|
45
|
+
@entity.pending_events << mock(Event, :entity_id= => true, :entity_version= => true)
|
45
46
|
@storage_client = mock("StorageClient", :add_event => true)
|
46
47
|
@store = Store.new
|
47
48
|
@store.stub(:storage_client) { @storage_client }
|
48
49
|
EventBus.stub(:publish)
|
49
50
|
end
|
50
|
-
|
51
|
+
|
51
52
|
subject { @store.add_events(@entity) }
|
52
|
-
|
53
|
+
|
53
54
|
it "adds each of the events" do
|
54
55
|
@entity.pending_events.each do |e|
|
55
56
|
@storage_client.should_receive(:add_event).with(e)
|
@@ -62,15 +63,21 @@ describe Store do
|
|
62
63
|
end
|
63
64
|
subject
|
64
65
|
end
|
66
|
+
it "should assign the current entity version to each event" do
|
67
|
+
@entity.pending_events.each do |e|
|
68
|
+
e.should_receive(:entity_version=).with(@entity.version)
|
69
|
+
end
|
70
|
+
subject
|
71
|
+
end
|
65
72
|
it "publishes each event to the EventBus" do
|
66
73
|
@entity.pending_events.each do |e|
|
67
74
|
EventBus.should_receive(:publish).with(@entity.type, e)
|
68
75
|
end
|
69
76
|
subject
|
70
77
|
end
|
71
|
-
|
78
|
+
|
72
79
|
end
|
73
|
-
|
80
|
+
|
74
81
|
describe "#save" do
|
75
82
|
before(:each) do
|
76
83
|
@new_id = random_string
|
@@ -80,9 +87,9 @@ describe Store do
|
|
80
87
|
@store.stub(:add_events)
|
81
88
|
@store.stub(:storage_client) { @storage_client }
|
82
89
|
end
|
83
|
-
|
90
|
+
|
84
91
|
subject { @store.save(@entity) }
|
85
|
-
|
92
|
+
|
86
93
|
it "increments the entity version number" do
|
87
94
|
@entity.should_receive(:version=).with(@entity.version + 1)
|
88
95
|
subject
|
@@ -99,21 +106,21 @@ describe Store do
|
|
99
106
|
subject.should eq(@entity)
|
100
107
|
end
|
101
108
|
end
|
102
|
-
|
109
|
+
|
103
110
|
describe "#get" do
|
104
111
|
before(:each) do
|
105
112
|
@id = random_integer
|
106
113
|
@entity = DummyEntity.new(random_string)
|
107
114
|
DummyEntity.stub(:new).and_return(@ride)
|
108
115
|
@events = [mock("Event", :apply => true), mock("Event", :apply => true)]
|
109
|
-
|
116
|
+
|
110
117
|
@storage_client = mock("StorageClient", :get_entity => @entity, :get_events => @events)
|
111
118
|
@store = Store.new
|
112
119
|
@store.stub(:storage_client) { @storage_client }
|
113
120
|
end
|
114
|
-
|
121
|
+
|
115
122
|
subject { @store.get(@id) }
|
116
|
-
|
123
|
+
|
117
124
|
it "should retrieve object from the storage client" do
|
118
125
|
@storage_client.should_receive(:get_entity).with(@id, false)
|
119
126
|
subject
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: entity_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mongo
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: '1.6'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.6'
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: bson_ext
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ~>
|
@@ -32,7 +37,12 @@ dependencies:
|
|
32
37
|
version: '1.6'
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.6'
|
36
46
|
description: Event sourced entity store with a Mongo body
|
37
47
|
email: adam.bird@gmail.com
|
38
48
|
executables: []
|
@@ -80,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
80
90
|
version: '0'
|
81
91
|
requirements: []
|
82
92
|
rubyforge_project:
|
83
|
-
rubygems_version: 1.8.
|
93
|
+
rubygems_version: 1.8.24
|
84
94
|
signing_key:
|
85
95
|
specification_version: 3
|
86
96
|
summary: Event sourced entity store with a Mongo body
|