entity_store 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MTNjMDA0M2EyMWNmMWM5Y2U1NDc2MjMwOWU0NWQ5Mjk0ZWFlYjUxOA==
5
+ data.tar.gz: !binary |-
6
+ ZWMzNDEzNTYzYzU4ZjE1ZjVjOGNkZTJhMjQxMWMzOWUwNTllOGUwNQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MThjMmU5N2RhMzVjMjRkMGJmNDFjOTMxNjU5OWI5MzU3NWJkYzI5NDM4OTJm
10
+ ZjJjNzg3M2I4ZWJhMmIzMzUwYWFjOGU1Njk4OWMxY2Q0ZDA5ZGUyZjIzNGUz
11
+ MmQyMDE5YzgyOTFhZDI4Y2RlYzQzZDQ4M2Y3Yjg3YjYyNTdjNTU=
12
+ data.tar.gz: !binary |-
13
+ NDE3ODdjNGI2MzJiYzlhZDk0OTA2NzkwOTBlM2I1OGNmYWRhMjBlMTAyZjIw
14
+ ZTk0NmFmYTZmZWJjZTI3MmQzNDJkNmFlY2JhN2VkZWIzNGYzY2I2NTUxMDU1
15
+ ZTdlY2NkMjk4ZmU1MjExNmYwZjM5Yjk2N2Y4YzE1ZmQ3M2M0ZWQ=
data/lib/entity_store.rb CHANGED
@@ -1,68 +1,29 @@
1
1
  module EntityStore
2
2
 
3
- require 'hatchet'
4
- require 'entity_store/entity'
5
- require 'entity_store/entity_value'
6
- require 'entity_store/event'
7
- require 'entity_store/store'
8
- require 'entity_store/external_store'
9
- require 'entity_store/event_data_object'
10
- require 'entity_store/mongo_entity_store'
11
- require 'entity_store/event_bus'
12
- require 'entity_store/not_found'
13
- require 'entity_store/hash_serialization'
14
- require 'entity_store/attributes'
3
+ require_relative 'entity_store/logging'
4
+ require_relative 'entity_store/config'
5
+ require_relative 'entity_store/entity'
6
+ require_relative 'entity_store/entity_value'
7
+ require_relative 'entity_store/event'
8
+ require_relative 'entity_store/store'
9
+ require_relative 'entity_store/event_data_object'
10
+ require_relative 'entity_store/event_bus'
11
+ require_relative 'entity_store/not_found'
12
+ require_relative 'entity_store/hash_serialization'
13
+ require_relative 'entity_store/attributes'
14
+
15
+ if defined?(Mongo)
16
+ require_relative 'entity_store/mongo_entity_store'
17
+ require_relative 'entity_store/external_store'
18
+ end
15
19
 
16
20
  class << self
17
- attr_reader :mongo_connection, :external_mongo_connection, :entity_db, :external_db
18
- attr_accessor :connection_profile, :external_connection_profile
19
21
  def setup
20
- yield self
21
-
22
- @mongo_connection = open_store(@connection_profile)
23
- @entity_db = extract_db(@connection_profile)
24
- @external_mongo_connection = open_store(@external_connection_profile)
25
- @external_db = extract_db(@external_connection_profile)
22
+ yield EntityStore::Config.setup
26
23
  end
27
24
 
28
25
  def event_subscribers
29
- @_event_subscribers ||=[]
30
- end
31
-
32
- # Public - indicates the version increment that is used to
33
- # decided whether a snapshot of an entity should be created when it's saved
34
- def snapshot_threshold
35
- @_snapshot_threshold ||= 10
36
- end
37
-
38
- def snapshot_threshold=(value)
39
- @_snapshot_threshold = value
40
- end
41
-
42
- # Allows config to pass in a lambda or Proc to use as the type loader in place
43
- # of the default.
44
- # Original use case was migration of entity classes to new module namespace when
45
- # extracting to a shared library
46
- attr_accessor :type_loader
47
-
48
- def load_type(type_name)
49
- if EntityStore.type_loader
50
- EntityStore.type_loader.call(type_name)
51
- else
52
- type_name.split('::').inject(Object) {|obj, name| obj.const_get(name) }
53
- end
54
- end
55
-
56
- def open_store(url)
57
- Mongo::MongoClient.from_uri(url, :connect_timeout => connect_timeout)
58
- end
59
-
60
- def extract_db(url)
61
- URI.parse(url).path.gsub(/^\//, '')
62
- end
63
-
64
- def connect_timeout
65
- (ENV['ENTITY_STORE_CONNECT_TIMEOUT'] || '2').to_i
26
+ EntityStore::Config.event_subscribers
66
27
  end
67
28
  end
68
29
 
@@ -0,0 +1,53 @@
1
+ module EntityStore
2
+ module Config
3
+ class << self
4
+ # Stores
5
+ attr_accessor :store, :feed_store
6
+
7
+ # Allows config to pass in a lambda or Proc to use as the type loader in place
8
+ # of the default.
9
+ # Original use case was migration of entity classes to new module namespace when
10
+ # extracting to a shared library
11
+ attr_accessor :type_loader
12
+
13
+ # Logger can be assigned
14
+ attr_accessor :logger
15
+
16
+ def setup
17
+ yield self
18
+
19
+ raise StandardError.new("store not assigned") unless store
20
+ store.open
21
+ feed_store.open if feed_store
22
+ end
23
+
24
+ def event_subscribers
25
+ @_event_subscribers ||=[]
26
+ end
27
+
28
+ # Public - indicates the version increment that is used to
29
+ # decided whether a snapshot of an entity should be created when it's saved
30
+ def snapshot_threshold
31
+ @_snapshot_threshold ||= 10
32
+ end
33
+
34
+ def snapshot_threshold=(value)
35
+ @_snapshot_threshold = value
36
+ end
37
+
38
+
39
+ def load_type(type_name)
40
+ if EntityStore::Config.type_loader
41
+ EntityStore::Config.type_loader.call(type_name)
42
+ else
43
+ type_name.split('::').inject(Object) {|obj, name| obj.const_get(name) }
44
+ end
45
+ end
46
+
47
+ def connect_timeout
48
+ (ENV['ENTITY_STORE_CONNECT_TIMEOUT'] || '2').to_i
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -1,7 +1,5 @@
1
1
  module EntityStore
2
2
  module Entity
3
- include Hatchet
4
-
5
3
  attr_accessor :id
6
4
 
7
5
  def self.included(klass)
@@ -24,8 +24,17 @@ module EntityStore
24
24
  class_eval do
25
25
  names.each do |name|
26
26
  define_method "#{name}=" do |value|
27
- require 'time'
28
- instance_variable_set("@#{name}", value.kind_of?(String) ? Time.parse(value) : value)
27
+ if value.kind_of?(String)
28
+ # implementing parsing here rather than using std-lib Time.parse to
29
+ # allow portability across platforms
30
+ parts = /(?:(\d+))-(?:(\d+))-(?:(\d+))\s(?:(\d+)):(?:(\d+)):(?:(\d+))\s(?:(.+))/.match(value)
31
+ offset = parts[7].gsub(/\d{4}/) do |m| m.scan(/../).join(":") end
32
+ new_value = Time.new(parts[1].to_i, parts[2].to_i, parts[3].to_i, parts[4].to_i, parts[5].to_i, parts[6].to_i, offset)
33
+ else
34
+ new_value = value
35
+ end
36
+
37
+ instance_variable_set "@#{name}", new_value
29
38
  end
30
39
  define_method name do
31
40
  instance_variable_get "@#{name}"
@@ -1,18 +1,16 @@
1
1
  module EntityStore
2
2
  class EventBus
3
- include Hatchet
3
+ include Logging
4
4
 
5
5
  def publish(entity_type, event)
6
- publish_externally entity_type, event
7
-
8
- logger.debug { "publishing #{event.inspect}" }
6
+ publish_to_feed entity_type, event
9
7
 
10
8
  subscribers_to(event.receiver_name).each do |s|
11
9
  begin
12
10
  s.new.send(event.receiver_name, event)
13
- logger.debug { "called #{s.name}##{event.receiver_name} with #{event.inspect}" }
11
+ log_debug { "called #{s.name}##{event.receiver_name} with #{event.inspect}" }
14
12
  rescue => e
15
- logger.error "#{e.message} when calling #{s.name}##{event.receiver_name} with #{event.inspect}", e
13
+ log_error "#{e.message} when calling #{s.name}##{event.receiver_name} with #{event.inspect}", e
16
14
  end
17
15
  end
18
16
  end
@@ -22,15 +20,15 @@ module EntityStore
22
20
  end
23
21
 
24
22
  def subscribers
25
- EntityStore.event_subscribers
23
+ EntityStore::Config.event_subscribers
26
24
  end
27
25
 
28
- def publish_externally(entity_type, event)
29
- external_store.add_event(entity_type, event)
26
+ def publish_to_feed(entity_type, event)
27
+ feed_store.add_event(entity_type, event) if feed_store
30
28
  end
31
29
 
32
- def external_store
33
- @_external_store ||= ExternalStore.new
30
+ def feed_store
31
+ EntityStore::Config.feed_store
34
32
  end
35
33
 
36
34
  # Public - replay events of a given type to a given subscriber
@@ -42,19 +40,19 @@ module EntityStore
42
40
  # Returns nothing
43
41
  def replay(since, type, subscriber)
44
42
  max_items = 100
45
- event_data_objects = external_store.get_events(since, type, max_items)
43
+ event_data_objects = feed_store.get_events(since, type, max_items)
46
44
 
47
45
  while event_data_objects.count > 0 do
48
46
  event_data_objects.each do |event_data_object|
49
47
  begin
50
- event = EntityStore.load_type(event_data_object.type).new(event_data_object.attrs)
48
+ event = EntityStore::Config.load_type(event_data_object.type).new(event_data_object.attrs)
51
49
  subscriber.new.send(event.receiver_name, event)
52
- logger.info { "replayed #{event.inspect} to #{subscriber.name}##{event.receiver_name}" }
50
+ log_info { "replayed #{event.inspect} to #{subscriber.name}##{event.receiver_name}" }
53
51
  rescue => e
54
- logger.error "#{e.message} when replaying #{event_data_object.inspect} to #{subscriber}", e
52
+ log_error "#{e.message} when replaying #{event_data_object.inspect} to #{subscriber}", e
55
53
  end
56
54
  end
57
- event_data_objects = external_store.get_events(event_data_objects.last.id, type, max_items)
55
+ event_data_objects = feed_store.get_events(event_data_objects.last.id, type, max_items)
58
56
  end
59
57
  end
60
58
  end
@@ -4,9 +4,22 @@ require 'uri'
4
4
  module EntityStore
5
5
  class ExternalStore
6
6
  include Mongo
7
-
7
+
8
+ class << self
9
+ attr_accessor :connection_profile
10
+ attr_writer :connect_timeout
11
+
12
+ def connection
13
+ @_connection ||= Mongo::MongoClient.from_uri(ExternalStore.connection_profile, :connect_timeout => EntityStore::Config.connect_timeout)
14
+ end
15
+
16
+ def database
17
+ URI.parse(ExternalStore.connection_profile).path.gsub(/^\//, '')
18
+ end
19
+ end
20
+
8
21
  def open_connection
9
- EntityStore.external_mongo_connection.db(EntityStore.external_db)
22
+ ExternalStore.connection.db(ExternalStore.database)
10
23
  end
11
24
 
12
25
  def collection
@@ -9,15 +9,19 @@ module EntityStore
9
9
  # did use flatten but this came a-cropper when the attribute value was an array
10
10
  def attributes
11
11
  attrs = {}
12
- public_methods
13
- .select { |m| m =~ /\w\=$/ }
14
- .select { |m| respond_to?(m.to_s.chop) }
15
- .collect { |m| m.to_s.chop.to_sym }
12
+ attribute_methods
16
13
  .collect { |m| [m, attribute_value(send(m))] }
17
14
  .each do |item| attrs[item[0]] = item[1] end
18
15
  attrs
19
16
  end
20
17
 
18
+ def attribute_methods
19
+ public_methods
20
+ .select { |m| m =~ /\w\=$/ }
21
+ .collect { |m| m.to_s.chop.to_sym }
22
+ .select { |m| respond_to?(m) }
23
+ end
24
+
21
25
  def attribute_value(value)
22
26
  if value.respond_to?(:attributes)
23
27
  value.attributes
@@ -0,0 +1,17 @@
1
+ module EntityStore
2
+ module Logging
3
+
4
+ [:debug, :info, :warn].each do |level|
5
+ define_method("log_#{level}") do |message=nil, &block|
6
+ Config.logger.send(level, message || block) if Config.logger
7
+ end
8
+ end
9
+
10
+ def log_error(message, exception)
11
+ if Config.logger
12
+ Config.logger.error message
13
+ Config.logger.error exception.backtrace
14
+ end
15
+ end
16
+ end
17
+ end
@@ -4,18 +4,38 @@ require 'uri'
4
4
  module EntityStore
5
5
  class MongoEntityStore
6
6
  include Mongo
7
- include Hatchet
7
+ include Logging
8
8
 
9
- def open_connection
10
- EntityStore.mongo_connection.db(EntityStore.entity_db)
9
+ class << self
10
+ attr_accessor :connection_profile
11
+ attr_writer :connect_timeout
12
+
13
+ def connection
14
+ @_connection ||= Mongo::MongoClient.from_uri(MongoEntityStore.connection_profile, :connect_timeout => EntityStore::Config.connect_timeout)
15
+ end
16
+
17
+ def database
18
+ URI.parse(MongoEntityStore.connection_profile).path.gsub(/^\//, '')
19
+ end
20
+ end
21
+
22
+ def open
23
+ MongoEntityStore.connection.db(MongoEntityStore.database)
11
24
  end
12
25
 
13
26
  def entities
14
- @entities_collection ||= open_connection['entities']
27
+ @entities_collection ||= open['entities']
15
28
  end
16
29
 
17
30
  def events
18
- @events_collection ||= open_connection['entity_events']
31
+ @events_collection ||= open['entity_events']
32
+ end
33
+
34
+ def clear
35
+ entities.drop
36
+ @entities_collection = nil
37
+ events.drop
38
+ @events_collection = nil
19
39
  end
20
40
 
21
41
  def ensure_indexes
@@ -63,24 +83,13 @@ module EntityStore
63
83
  def get_entity(id, raise_exception=false)
64
84
  if attrs = entities.find_one('_id' => BSON::ObjectId.from_string(id))
65
85
  begin
66
- entity = EntityStore.load_type(attrs['_type']).new(attrs['snapshot'] || {'id' => id, 'version' => attrs['version']})
86
+ entity_type = EntityStore::Config.load_type(attrs['_type'])
87
+ entity = entity_type.new(attrs['snapshot'] || {'id' => id, 'version' => attrs['version']})
67
88
  rescue => e
68
- logger.error "Error loading type #{attrs['_type']}", e
89
+ log_error "Error loading type #{attrs['_type']}", e
69
90
  raise
70
91
  end
71
-
72
- since_version = attrs['snapshot'] ? attrs['snapshot']['version'] : nil
73
-
74
- get_events(id, since_version).each do |event|
75
- begin
76
- event.apply(entity)
77
- logger.debug { "Applied #{event.inspect} to #{id}" }
78
- rescue => e
79
- logger.error "Failed to apply #{event.class.name} #{event.attributes} to #{id} with #{e.inspect}", e
80
- end
81
- entity.version = event.entity_version
82
- end
83
-
92
+
84
93
  entity
85
94
  else
86
95
  raise NotFound.new(id) if raise_exception
@@ -102,9 +111,9 @@ module EntityStore
102
111
 
103
112
  events.find(query, options).collect do |attrs|
104
113
  begin
105
- EntityStore.load_type(attrs['_type']).new(attrs)
114
+ EntityStore::Config.load_type(attrs['_type']).new(attrs)
106
115
  rescue => e
107
- logger.error "Error loading type #{attrs['_type']}", e
116
+ log_error "Error loading type #{attrs['_type']}", e
108
117
  nil
109
118
  end
110
119
  end.select { |e| !e.nil? }
@@ -1,9 +1,9 @@
1
1
  module EntityStore
2
2
  class Store
3
- include Hatchet
3
+ include Logging
4
4
 
5
5
  def storage_client
6
- @_storage_client ||= MongoEntityStore.new
6
+ @_storage_client ||= EntityStore::Config.store
7
7
  end
8
8
 
9
9
  def add(entity)
@@ -31,16 +31,16 @@ module EntityStore
31
31
  entity.id = storage_client.add_entity(entity)
32
32
  end
33
33
  add_events(entity)
34
- snapshot_entity(entity) if entity.version % EntityStore.snapshot_threshold == 0
34
+ snapshot_entity(entity) if entity.version % Config.snapshot_threshold == 0
35
35
  end
36
36
  entity
37
37
  rescue => e
38
- logger.error { "Store#do_save error: #{e.inspect} - #{entity.inspect}" }
38
+ log_error "Store#do_save error: #{e.inspect} - #{entity.inspect}", e
39
39
  raise e
40
40
  end
41
41
 
42
42
  def snapshot_entity(entity)
43
- logger.info { "Store#snapshot_entity : Snapshotting #{entity.id}"}
43
+ log_info { "Store#snapshot_entity : Snapshotting #{entity.id}"}
44
44
  storage_client.snapshot_entity(entity)
45
45
  end
46
46
 
@@ -64,6 +64,17 @@ module EntityStore
64
64
 
65
65
  def get(id, raise_exception=false)
66
66
  if entity = storage_client.get_entity(id, raise_exception)
67
+
68
+ storage_client.get_events(id, entity.version).each do |event|
69
+ begin
70
+ event.apply(entity)
71
+ log_debug { "Applied #{event.inspect} to #{id}" }
72
+ rescue => e
73
+ log_error "Failed to apply #{event.class.name} #{event.attributes} to #{id} with #{e.inspect}", e
74
+ end
75
+ entity.version = event.entity_version
76
+ end
77
+
67
78
  # assign this entity loader to allow lazy loading of related entities
68
79
  entity.related_entity_loader = self
69
80
  end
@@ -74,8 +85,7 @@ module EntityStore
74
85
  #
75
86
  # Returns nothing
76
87
  def clear_all
77
- storage_client.entities.drop
78
- storage_client.events.drop
88
+ storage_client.clear
79
89
  @_storage_client = nil
80
90
  end
81
91
 
@@ -1,3 +1,3 @@
1
1
  module EntityStore
2
- VERSION = "0.1.6".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ module Level1
4
+ module Level2
5
+ class MyClass
6
+ end
7
+ class AnotherClass
8
+ end
9
+ end
10
+ end
11
+
12
+ describe EntityStore::Config do
13
+
14
+ describe "load_type" do
15
+
16
+ subject { EntityStore::Config.load_type('Level1::Level2::MyClass') }
17
+
18
+ it "should be an Level1::Level2::MyClass" do
19
+ subject.should eq(Level1::Level2::MyClass)
20
+ end
21
+
22
+ context "when type_loader set" do
23
+ before(:each) do
24
+ EntityStore::Config.type_loader = lambda { |type_name|
25
+ Level1::Level2::AnotherClass
26
+ }
27
+ end
28
+
29
+ it "should return the result of that type loader" do
30
+ subject.should eq(Level1::Level2::AnotherClass)
31
+ end
32
+
33
+ after(:each) do
34
+ EntityStore::Config.type_loader = nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -23,7 +23,7 @@ describe EventBus do
23
23
  DummySubscriber.stub(:new) { @subscriber }
24
24
  @subscriber_class2 = mock("SubscriberClass", :instance_methods => ['bilge'], :name => "SubscriberClass")
25
25
  @event_bus.stub(:subscribers).and_return([DummySubscriber, @subscriber_class2])
26
- @event_bus.stub(:publish_externally)
26
+ @event_bus.stub(:publish_to_feed)
27
27
  end
28
28
 
29
29
  subject { @event_bus.publish(@entity_type, @event) }
@@ -37,21 +37,21 @@ describe EventBus do
37
37
  subject
38
38
  end
39
39
  it "publishes event to the external event push" do
40
- @event_bus.should_receive(:publish_externally).with(@entity_type, @event)
40
+ @event_bus.should_receive(:publish_to_feed).with(@entity_type, @event)
41
41
  subject
42
42
  end
43
43
  end
44
44
 
45
- describe ".publish_externally" do
45
+ describe ".publish_to_feed" do
46
46
  before(:each) do
47
- @external_store = mock(ExternalStore)
48
- @event_bus.stub(:external_store) { @external_store }
47
+ @feed_store = mock(ExternalStore)
48
+ @event_bus.stub(:feed_store) { @feed_store }
49
49
  end
50
50
 
51
- subject { @event_bus.publish_externally @entity_type, @event }
51
+ subject { @event_bus.publish_to_feed @entity_type, @event }
52
52
 
53
53
  it "should publish to the external store" do
54
- @external_store.should_receive(:add_event).with(@entity_type, @event)
54
+ @feed_store.should_receive(:add_event).with(@entity_type, @event)
55
55
  subject
56
56
  end
57
57
  end
@@ -63,18 +63,18 @@ describe EventBus do
63
63
  @subscriber = mock("Subscriber", :dummy_event => true)
64
64
  DummySubscriber.stub(:new) { @subscriber }
65
65
 
66
- @external_store = mock(ExternalStore)
66
+ @feed_store = mock(ExternalStore)
67
67
  @id = random_object_id
68
- @external_store.stub(:get_events) { |since| since == @id ? [] : [
68
+ @feed_store.stub(:get_events) { |since| since == @id ? [] : [
69
69
  EventDataObject.new('_id' => @id, '_type' => DummyEvent.name, 'name' => random_string)
70
70
  ]}
71
- @event_bus.stub(:external_store) { @external_store }
71
+ @event_bus.stub(:feed_store) { @feed_store }
72
72
  end
73
73
 
74
74
  subject { @event_bus.replay(@since, @type, DummySubscriber) }
75
75
 
76
76
  it "gets the events for that period" do
77
- @external_store.should_receive(:get_events).with(@since, @type, 100)
77
+ @feed_store.should_receive(:get_events).with(@since, @type, 100)
78
78
  subject
79
79
  end
80
80
  it "publishes them to the subscriber" do
@@ -12,6 +12,7 @@ end
12
12
 
13
13
  describe ExternalStore do
14
14
  before(:each) do
15
+ ExternalStore.connection_profile = "mongodb://localhost/external_store_default"
15
16
  ExternalStore.new.collection.drop
16
17
  @store = ExternalStore.new
17
18
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  module MongoEntityStoreSpec
4
4
  class DummyEntity
5
- include Entity
5
+ include EntityStore::Entity
6
6
 
7
7
  attr_accessor :name, :description
8
8
 
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  describe MongoEntityStore do
13
13
  before(:each) do
14
- EntityStore.connection_profile = "mongodb://localhost/entity_store_default"
14
+ MongoEntityStore.connection_profile = "mongodb://localhost/entity_store_default"
15
15
  @store = MongoEntityStore.new
16
16
  end
17
17
 
@@ -19,7 +19,7 @@ describe MongoEntityStore do
19
19
  before(:each) do
20
20
  @id = random_object_id
21
21
  @attrs = {
22
- '_type' => "MongoEntityStoreSpec::DummyEntity",
22
+ '_type' => MongoEntityStoreSpec::DummyEntity.name,
23
23
  'version' => @version = random_integer
24
24
  }
25
25
  @entity = MongoEntityStoreSpec::DummyEntity.new
@@ -42,18 +42,6 @@ describe MongoEntityStore do
42
42
  MongoEntityStoreSpec::DummyEntity.should_receive(:new).with({'id' => @id, 'version' => @version})
43
43
  subject
44
44
  end
45
- it "should retrieve it's events" do
46
- @store.should_receive(:get_events).with(@id, nil)
47
- subject
48
- end
49
- it "should apply each event to the entity" do
50
- @events.each do |event| event.should_receive(:apply).with(@entity) end
51
- subject
52
- end
53
- it "should set the entity version to that of the last event" do
54
- subject
55
- @entity.version.should eq(@events.last.entity_version)
56
- end
57
45
  it "should return the entity" do
58
46
  subject.should eq(@entity)
59
47
  end
@@ -68,10 +56,6 @@ describe MongoEntityStore do
68
56
  MongoEntityStoreSpec::DummyEntity.should_receive(:new).with(@attrs['snapshot'])
69
57
  subject
70
58
  end
71
- it "should load the events since the snapshot version" do
72
- @store.should_receive(:get_events).with(@id, @snapshot_version)
73
- subject
74
- end
75
59
  end
76
60
  end
77
61
 
@@ -80,7 +80,7 @@ describe Store do
80
80
  context "when entity has related entities loaded" do
81
81
  before(:each) do
82
82
  @entity = DummyEntityForStore.new(:id => random_string)
83
- @entity.version = random_integer * EntityStore.snapshot_threshold + 1
83
+ @entity.version = random_integer * EntityStore::Config.snapshot_threshold + 1
84
84
  @store = Store.new
85
85
  @related_entity = mock('Entity')
86
86
  @entity.stub(:loaded_related_entities) { [ @related_entity ] }
@@ -104,7 +104,7 @@ describe Store do
104
104
  before(:each) do
105
105
  @new_id = random_string
106
106
  @entity = DummyEntityForStore.new(:id => random_string)
107
- @entity.version = random_integer * EntityStore.snapshot_threshold
107
+ @entity.version = random_integer * EntityStore::Config.snapshot_threshold
108
108
  @storage_client = mock("StorageClient", :save_entity => true)
109
109
  @store = Store.new
110
110
  @store.stub(:add_events)
@@ -161,7 +161,7 @@ describe Store do
161
161
 
162
162
  context "when entity version is commensurate with snapshotting" do
163
163
  before(:each) do
164
- @entity.version = random_integer * EntityStore.snapshot_threshold - 1
164
+ @entity.version = random_integer * EntityStore::Config.snapshot_threshold - 1
165
165
  end
166
166
 
167
167
  it "should snapshot the entity" do
@@ -175,9 +175,12 @@ describe Store do
175
175
  describe "#get" do
176
176
  before(:each) do
177
177
  @id = random_integer
178
- @entity = DummyEntityForStore.new
178
+ @entity = DummyEntityForStore.new(id: random_string, version: random_integer)
179
179
  DummyEntityForStore.stub(:new).and_return(@entity)
180
- @events = [mock("Event", :apply => true), mock("Event", :apply => true)]
180
+ @events = [
181
+ mock("Event", apply: true, entity_version: @entity.version + 1),
182
+ mock("Event", apply: true, entity_version: @entity.version + 2)
183
+ ]
181
184
 
182
185
  @storage_client = mock("StorageClient", :get_entity => @entity, :get_events => @events)
183
186
  @store = Store.new
@@ -197,5 +200,17 @@ describe Store do
197
200
  it "should return a ride" do
198
201
  subject.should eq(@entity)
199
202
  end
203
+ it "should retrieve it's events" do
204
+ @storage_client.should_receive(:get_events).with(@id, @entity.version)
205
+ subject
206
+ end
207
+ it "should apply each event to the entity" do
208
+ @events.each do |event| event.should_receive(:apply).with(@entity) end
209
+ subject
210
+ end
211
+ it "should set the entity version to that of the last event" do
212
+ subject
213
+ @entity.version.should eq(@events.last.entity_version)
214
+ end
200
215
  end
201
216
  end
@@ -1,34 +1 @@
1
- require 'spec_helper'
2
-
3
- module Level1
4
- module Level2
5
- class MyClass
6
- end
7
- class AnotherClass
8
- end
9
- end
10
- end
11
-
12
- describe EntityStore do
13
-
14
- describe "load_type" do
15
-
16
- subject { EntityStore.load_type('Level1::Level2::MyClass') }
17
-
18
- it "should be an Level1::Level2::MyClass" do
19
- subject.should eq(Level1::Level2::MyClass)
20
- end
21
-
22
- context "when type_loader set" do
23
- before(:each) do
24
- EntityStore.type_loader = lambda { |type_name|
25
- Level1::Level2::AnotherClass
26
- }
27
- end
28
-
29
- it "should return the result of that type loader" do
30
- subject.should eq(Level1::Level2::AnotherClass)
31
- end
32
- end
33
- end
34
- end
1
+ require 'spec_helper'
data/spec/spec_helper.rb CHANGED
@@ -1,29 +1,18 @@
1
1
  require 'rake'
2
2
  require 'rspec'
3
+ require 'mongo'
3
4
  require "#{Rake.application.original_dir}/lib/entity_store"
4
5
 
5
6
  RSpec.configure do |config|
6
7
  config.color_enabled = true
7
8
  end
8
9
 
9
- Hatchet.configure do |config|
10
- # Reset the logging configuration
11
- config.reset!
12
- config.level :error
13
- # Use the format without time, etc so we don't duplicate it
14
- config.formatter = Hatchet::SimpleFormatter.new
15
- # Set up a STDOUT appender
16
- config.appenders << Hatchet::LoggerAppender.new do |appender|
17
- appender.logger = Logger.new(STDOUT)
18
- end
19
- end
20
-
21
10
  include EntityStore
22
11
 
23
- EntityStore.setup do |config|
24
- config.connection_profile = "mongodb://localhost/entity_store_test"
25
- config.external_connection_profile = "mongodb://localhost/external_entity_store_test"
26
- end
12
+ require 'logger'
13
+ logger = ::Logger.new(STDOUT)
14
+ logger.level = ::Logger::ERROR
15
+ EntityStore::Config.logger = logger
27
16
 
28
17
  def random_string
29
18
  (0...24).map{ ('a'..'z').to_a[rand(26)] }.join
metadata CHANGED
@@ -1,52 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: entity_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
5
- prerelease:
4
+ version: 0.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Adam Bird
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-31 00:00:00.000000000 Z
11
+ date: 2013-03-30 00:00:00.000000000 Z
13
12
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: mongo
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: '1.6'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '1.6'
30
- - !ruby/object:Gem::Dependency
31
- name: bson_ext
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - ~>
36
- - !ruby/object:Gem::Version
37
- version: '1.6'
38
- type: :runtime
39
- prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- version: '1.6'
46
13
  - !ruby/object:Gem::Dependency
47
14
  name: hatchet
48
15
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
16
  requirements:
51
17
  - - ~>
52
18
  - !ruby/object:Gem::Version
@@ -54,12 +20,11 @@ dependencies:
54
20
  type: :runtime
55
21
  prerelease: false
56
22
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
23
  requirements:
59
24
  - - ~>
60
25
  - !ruby/object:Gem::Version
61
26
  version: 0.0.20
62
- description: Event sourced entity store with a Mongo body
27
+ description: Event sourced entity store with a replaceable body
63
28
  email: adam.bird@gmail.com
64
29
  executables: []
65
30
  extensions: []
@@ -74,12 +39,14 @@ files:
74
39
  - lib/entity_store/event_data_object.rb
75
40
  - lib/entity_store/external_store.rb
76
41
  - lib/entity_store/hash_serialization.rb
42
+ - lib/entity_store/logging.rb
77
43
  - lib/entity_store/mongo_entity_store.rb
78
44
  - lib/entity_store/not_found.rb
79
45
  - lib/entity_store/store.rb
80
46
  - lib/entity_store/version.rb
81
47
  - lib/entity_store.rb
82
48
  - lib/tasks/entity_store.rake
49
+ - spec/entity_store/config_spec.rb
83
50
  - spec/entity_store/entity_spec.rb
84
51
  - spec/entity_store/entity_value_spec.rb
85
52
  - spec/entity_store/event_bus_spec.rb
@@ -91,29 +58,29 @@ files:
91
58
  - spec/spec_helper.rb
92
59
  homepage: http://github.com/adambird/entity_store
93
60
  licenses: []
61
+ metadata: {}
94
62
  post_install_message:
95
63
  rdoc_options: []
96
64
  require_paths:
97
65
  - lib
98
66
  required_ruby_version: !ruby/object:Gem::Requirement
99
- none: false
100
67
  requirements:
101
68
  - - ! '>='
102
69
  - !ruby/object:Gem::Version
103
70
  version: '0'
104
71
  required_rubygems_version: !ruby/object:Gem::Requirement
105
- none: false
106
72
  requirements:
107
73
  - - ! '>='
108
74
  - !ruby/object:Gem::Version
109
75
  version: '0'
110
76
  requirements: []
111
77
  rubyforge_project:
112
- rubygems_version: 1.8.24
78
+ rubygems_version: 2.0.3
113
79
  signing_key:
114
- specification_version: 3
115
- summary: Event sourced entity store with a Mongo body
80
+ specification_version: 4
81
+ summary: Event sourced entity store with a replaceable body
116
82
  test_files:
83
+ - spec/entity_store/config_spec.rb
117
84
  - spec/entity_store/entity_spec.rb
118
85
  - spec/entity_store/entity_value_spec.rb
119
86
  - spec/entity_store/event_bus_spec.rb
@@ -123,4 +90,3 @@ test_files:
123
90
  - spec/entity_store/store_spec.rb
124
91
  - spec/entity_store_spec.rb
125
92
  - spec/spec_helper.rb
126
- has_rdoc: