entity_store 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,42 @@
1
1
  module EntityStore
2
2
  module Entity
3
3
  attr_accessor :id
4
+
5
+ # Holds a reference to the store used to load this entity so the same store
6
+ # can be used for related entities
7
+ attr_accessor :related_entity_loader
8
+
4
9
  attr_writer :version
5
10
 
11
+ def self.included(klass)
12
+ klass.class_eval do
13
+ extend ClassMethods
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ def related_entities(*names)
20
+ names.each do |name|
21
+ # attr accessor for the id
22
+ define_method("#{name}_id") { instance_variable_get("@#{name}_id")}
23
+ define_method("#{name}_id=") do |value| instance_variable_set("@#{name}_id", value) end
24
+
25
+ # lazy loader for related entity
26
+ define_method(name) {
27
+ if instance_variable_get("@#{name}_id") && related_entity_loader
28
+ instance_variable_get("@_#{name}") || instance_variable_set("@_#{name}", related_entity_loader.get(instance_variable_get("@#{name}_id")))
29
+ end
30
+ }
31
+ end
32
+
33
+ define_method(:loaded_related_entities) {
34
+ names.collect{ |name| instance_variable_get("@_#{name}") }.select{|entity| !entity.nil? }
35
+ }
36
+ end
37
+
38
+ end
39
+
6
40
  def initialize(attr={})
7
41
  attr.each_pair { |k,v| self.send("#{k}=", v) }
8
42
  end
@@ -11,12 +11,12 @@ module EntityStore
11
11
 
12
12
  def open_store
13
13
  uri = URI.parse(EntityStore.connection_profile)
14
- connection = Connection.from_uri(EntityStore.connection_profile)
14
+ connection = Connection.from_uri(EntityStore.connection_profile, :connect_timeout => connect_timeout)
15
15
  connection.db(uri.path.gsub(/^\//, ''))
16
16
  end
17
17
 
18
18
  def connect_timeout
19
- ENV['ENTITY_STORE_CONNECT_TIMEOUT'] || '2000'
19
+ (ENV['ENTITY_STORE_CONNECT_TIMEOUT'] || '2').to_i
20
20
  end
21
21
 
22
22
  def entities
@@ -7,20 +7,32 @@ module EntityStore
7
7
  def add(entity)
8
8
  entity.id = storage_client.add_entity(entity)
9
9
  add_events(entity)
10
- return entity
10
+ 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
+ do_save entity
18
+ entity.loaded_related_entities.each do |e| do_save e end if entity.respond_to?(:loaded_related_entities)
19
+ entity
20
+ end
21
+
22
+ def do_save(entity)
17
23
  # need to look at concurrency if we start storing version on client
18
- entity.version += 1
19
- storage_client.save_entity(entity)
20
- add_events(entity)
21
- return entity
24
+ unless entity.pending_events.empty?
25
+ entity.version += 1
26
+ if entity.id
27
+ storage_client.save_entity(entity)
28
+ else
29
+ entity.id = storage_client.add_entity(entity)
30
+ end
31
+ add_events(entity)
32
+ end
33
+ entity
22
34
  rescue => e
23
- EntityStore.logger.error { "Store#save error: #{e.inspect} - #{entity.inspect}" }
35
+ EntityStore.logger.error { "Store#do_save error: #{e.inspect} - #{entity.inspect}" }
24
36
  raise e
25
37
  end
26
38
 
@@ -40,8 +52,10 @@ module EntityStore
40
52
  def get(id, raise_exception=false)
41
53
  if entity = storage_client.get_entity(id, raise_exception)
42
54
  storage_client.get_events(id).each { |e| e.apply(entity) }
55
+ # assign this entity loader to allow lazy loading of related entities
56
+ entity.related_entity_loader = self
43
57
  end
44
- return entity
58
+ entity
45
59
  end
46
60
 
47
61
  # Public : USE AT YOUR PERIL this clears the ENTIRE data store
@@ -1,3 +1,3 @@
1
1
  module EntityStore
2
- VERSION = "0.0.5".freeze
2
+ VERSION = "0.0.7".freeze
3
3
  end
@@ -0,0 +1,69 @@
1
+ require "spec_helper"
2
+
3
+ class DummyEntity
4
+ include Entity
5
+
6
+ related_entities :club, :user
7
+
8
+ end
9
+
10
+ describe Entity do
11
+ describe ".related_entities" do
12
+ before(:each) do
13
+ @entity_loader = mock(Store)
14
+ @club = mock('Entity', :id => random_string)
15
+ @user = mock('Entity', :id => random_string)
16
+ @entity = DummyEntity.new(:related_entity_loader => @entity_loader, :club_id => @club.id, :user_id => @user.id)
17
+ @entity_loader.stub(:get) { |id|
18
+ case id
19
+ when @club.id
20
+ @club
21
+ when @user.id
22
+ @user
23
+ end
24
+ }
25
+ end
26
+
27
+ it "should have the club_id set" do
28
+ @entity.club_id.should eq(@club.id)
29
+ end
30
+ it "should load club" do
31
+ @entity.club.should eq(@club)
32
+ end
33
+ it "should call entity_loader with club id" do
34
+ @entity_loader.should_receive(:get).with(@club.id)
35
+ @entity.club
36
+ end
37
+ it "should have the user_id set" do
38
+ @entity.user_id.should eq(@user.id)
39
+ end
40
+ it "should load user" do
41
+ @entity.user.should eq(@user)
42
+ end
43
+ it "should call entity_loader with user id" do
44
+ @entity_loader.should_receive(:get).with(@user.id)
45
+ @entity.user
46
+ end
47
+
48
+ context "when only user loaded" do
49
+ before(:each) do
50
+ @entity.user
51
+ end
52
+
53
+ it "should only have user in the loaded related entities collection" do
54
+ @entity.loaded_related_entities.should eq([@user])
55
+ end
56
+ end
57
+
58
+ context "when both user and club loaded" do
59
+ before(:each) do
60
+ @entity.club
61
+ @entity.user
62
+ end
63
+
64
+ it "should only have user in the loaded related entities collection" do
65
+ @entity.loaded_related_entities.should eq([@club, @user])
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,20 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
- class DummyEntity
3
+ class DummyEntityForStore
4
4
  include Entity
5
5
 
6
6
  attr_accessor :name
7
7
 
8
- def initialize(name)
9
- @name = name
10
- end
11
8
  end
12
9
 
13
10
  describe Store do
14
11
  describe "#add" do
15
12
  before(:each) do
16
13
  @new_id = random_string
17
- @entity = DummyEntity.new(random_string)
14
+ @entity = DummyEntityForStore.new(:name => random_string)
18
15
  @storage_client = mock("StorageClient", :add_entity => @new_id)
19
16
  @store = Store.new
20
17
  @store.stub(:add_events)
@@ -38,7 +35,7 @@ describe Store do
38
35
 
39
36
  describe "#add_events" do
40
37
  before(:each) do
41
- @entity = DummyEntity.new(random_string)
38
+ @entity = DummyEntityForStore.new(:name => random_string)
42
39
  @entity.id = random_string
43
40
  @entity.version = random_integer
44
41
  @entity.pending_events << mock(Event, :entity_id= => true, :entity_version= => true)
@@ -79,16 +76,41 @@ describe Store do
79
76
  end
80
77
 
81
78
  describe "#save" do
79
+ context "when entity has related entities loaded" do
80
+ before(:each) do
81
+ @entity = DummyEntityForStore.new(:id => random_string)
82
+ @store = Store.new
83
+ @related_entity = mock('Entity')
84
+ @entity.stub(:loaded_related_entities) { [ @related_entity ] }
85
+ @store.stub(:do_save)
86
+ end
87
+
88
+ subject { @store.save(@entity) }
89
+
90
+ it "should save the entity" do
91
+ @store.should_receive(:do_save).with(@entity)
92
+ subject
93
+ end
94
+ it "should save them as well" do
95
+ @store.should_receive(:do_save).with(@related_entity)
96
+ subject
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ describe "#do_save" do
82
103
  before(:each) do
83
104
  @new_id = random_string
84
- @entity = DummyEntity.new(random_string)
105
+ @entity = DummyEntityForStore.new(:id => random_string)
85
106
  @storage_client = mock("StorageClient", :save_entity => true)
86
107
  @store = Store.new
87
108
  @store.stub(:add_events)
88
109
  @store.stub(:storage_client) { @storage_client }
110
+ @entity.stub(:pending_events) { [ mock('Event') ] }
89
111
  end
90
112
 
91
- subject { @store.save(@entity) }
113
+ subject { @store.do_save(@entity) }
92
114
 
93
115
  it "increments the entity version number" do
94
116
  @entity.should_receive(:version=).with(@entity.version + 1)
@@ -102,16 +124,40 @@ describe Store do
102
124
  @store.should_receive(:add_events).with(@entity)
103
125
  subject
104
126
  end
105
- it "returns a reference to the ride" do
127
+ it "returns a reference to the entity" do
106
128
  subject.should eq(@entity)
107
129
  end
130
+ context "when no pending events" do
131
+ before(:each) do
132
+ @entity.stub(:pending_events) { [] }
133
+ end
134
+ it "should not save the entity" do
135
+ @storage_client.should_not_receive(:save_entity)
136
+ subject
137
+ end
138
+ it "should not add the events" do
139
+ @storage_client.should_not_receive(:add_events)
140
+ subject
141
+ end
142
+ end
143
+ context "when entity doesn't have an id" do
144
+ before(:each) do
145
+ @entity.id = nil
146
+ @id = random_string
147
+ @storage_client.stub(:add_entity) { @id }
148
+ end
149
+ it "should add the entity" do
150
+ @storage_client.should_receive(:add_entity).with(@entity)
151
+ subject
152
+ end
153
+ end
108
154
  end
109
155
 
110
156
  describe "#get" do
111
157
  before(:each) do
112
158
  @id = random_integer
113
- @entity = DummyEntity.new(random_string)
114
- DummyEntity.stub(:new).and_return(@ride)
159
+ @entity = DummyEntityForStore.new
160
+ DummyEntityForStore.stub(:new).and_return(@ride)
115
161
  @events = [mock("Event", :apply => true), mock("Event", :apply => true)]
116
162
 
117
163
  @storage_client = mock("StorageClient", :get_entity => @entity, :get_events => @events)
@@ -135,6 +181,9 @@ describe Store do
135
181
  end
136
182
  subject
137
183
  end
184
+ it "should assign itself as the related_entity_loader" do
185
+ subject.related_entity_loader.should eq(@store)
186
+ end
138
187
  it "should return a ride" do
139
188
  subject.should eq(@entity)
140
189
  end
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.5
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-31 00:00:00.000000000 Z
12
+ date: 2012-10-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongo
@@ -62,6 +62,7 @@ files:
62
62
  - lib/entity_store/version.rb
63
63
  - lib/entity_store.rb
64
64
  - lib/tasks/entity_store.rake
65
+ - spec/entity_store/entity_spec.rb
65
66
  - spec/entity_store/entity_value_spec.rb
66
67
  - spec/entity_store/event_bus_spec.rb
67
68
  - spec/entity_store/event_spec.rb
@@ -95,6 +96,7 @@ signing_key:
95
96
  specification_version: 3
96
97
  summary: Event sourced entity store with a Mongo body
97
98
  test_files:
99
+ - spec/entity_store/entity_spec.rb
98
100
  - spec/entity_store/entity_value_spec.rb
99
101
  - spec/entity_store/event_bus_spec.rb
100
102
  - spec/entity_store/event_spec.rb