history_book 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.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # HistoryBook [![Build Status](https://secure.travis-ci.org/jtdowney/history_book.png?branch=master)](http://travis-ci.org/jtdowney/history_book)
2
+
3
+ HistoryBook is a ruby implementation of the [event sourcing](http://martinfowler.com/eaaDev/EventSourcing.html) pattern.
4
+
5
+ This library provides an interface for event sourcing over a pluggable back end data store. Currently it supports the [Sequel gem](http://sequel.rubyforge.org/) and its data stores as well as an in-memory data store for testing.
6
+
7
+ ## Usage
8
+
9
+ To run the example below you will need to install (or place in your Gemfile) both the sequel gem and the pg gem.
10
+
11
+ ```ruby
12
+ HistoryBook.configure(:sequel) do |config|
13
+ config.connection_string = 'postgres://localhost/history_book'
14
+ end
15
+
16
+ HistoryBook.open('test_id') do |stream|
17
+ stream << HistoryBook::Event.new(:customer_created, :name => 'Joe Tester', :address => '100 West Washington')
18
+ stream.commit
19
+ end
20
+
21
+ HistoryBook.open('test_id') do |stream|
22
+ stream.events.each do |event|
23
+ puts "Playback event #{event.type} with #{event.data.inspect}"
24
+ end
25
+ end
26
+ ```
27
+
28
+ ## License
29
+
30
+ HistoryBook is released under the [MIT license](http://www.opensource.org/licenses/MIT).
@@ -0,0 +1,39 @@
1
+ require 'active_support/core_ext/hash'
2
+ require 'active_support/inflector'
3
+ require 'multi_json'
4
+ require 'thread'
5
+
6
+ require 'history_book/event'
7
+ require 'history_book/stream'
8
+ require 'history_book/version'
9
+
10
+ module HistoryBook
11
+ class << self
12
+ attr_reader :config
13
+ end
14
+
15
+ def self.configure(driver)
16
+ driver_klass = _load_driver(driver)
17
+
18
+ unless @config.instance_of?(driver_klass)
19
+ @config = driver_klass.new
20
+ end
21
+
22
+ if block_given?
23
+ yield @config
24
+ end
25
+ end
26
+
27
+ def self.open(id)
28
+ store = config.create_store
29
+ yield HistoryBook::Stream.new(id, store)
30
+ end
31
+
32
+ def self._load_driver(driver)
33
+ path = "history_book/#{driver}/configuration"
34
+ require path
35
+ path.classify.constantize
36
+ rescue LoadError
37
+ raise "Unable to load #{driver} driver for history_book"
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ module HistoryBook
2
+ class Event
3
+ attr_reader :type, :data
4
+
5
+ def initialize(type, data = {})
6
+ @type = type
7
+ @data = data.with_indifferent_access
8
+ end
9
+
10
+ def ==(other)
11
+ self.type == other.type && self.data == other.data
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ require 'history_book/memory/store'
2
+
3
+ module HistoryBook
4
+ module Memory
5
+ class Configuration
6
+ def create_store
7
+ HistoryBook::Memory::Store.new
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ module HistoryBook
2
+ module Memory
3
+ class Store
4
+ @@events = {}
5
+ @@events_lock = Mutex.new
6
+
7
+ def load_events(id, options = {})
8
+ options = {:from => 1, :to => nil}.merge(options)
9
+ from, to = options.values_at(:from, :to)
10
+
11
+ events = @@events.fetch(id, [])
12
+ _filter_events(events, from, to)
13
+ end
14
+
15
+ def store_events(id, new_events)
16
+ new_events = Array(new_events)
17
+ @@events_lock.synchronize do
18
+ @@events[id] ||= []
19
+ @@events[id].concat(new_events)
20
+ end
21
+ end
22
+
23
+ def _filter_events(events, from, to)
24
+ if to
25
+ events = events.reject.with_index { |e, i| i > to - 1 }
26
+ end
27
+
28
+ events.select.with_index { |e, i| i >= from - 1 }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ require 'sequel'
2
+ require 'history_book/sequel/store'
3
+
4
+ module HistoryBook
5
+ module Sequel
6
+ class Configuration
7
+ attr_accessor :connection_string
8
+
9
+ def create_store
10
+ db = ::Sequel.connect(connection_string)
11
+ HistoryBook::Sequel::Store.new(db)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,82 @@
1
+ module HistoryBook
2
+ module Sequel
3
+ class Store
4
+ def initialize(db)
5
+ @db = db
6
+
7
+ _create_events_table
8
+ end
9
+
10
+ def load_events(id, options = {})
11
+ options = {:from => 1, :to => nil}.merge(options)
12
+ from, to = options.values_at(:from, :to)
13
+
14
+ rows = _load_rows(id, from, to)
15
+ rows.map do |row|
16
+ _transform_row(row)
17
+ end
18
+ end
19
+
20
+ def store_events(id, new_events)
21
+ @db.transaction do
22
+ max_revision = _max_revision(id)
23
+
24
+ new_events = Array(new_events)
25
+ new_events.each do |new_event|
26
+ max_revision += 1
27
+ @db[:events].insert(
28
+ :stream_id => id,
29
+ :revision => max_revision,
30
+ :timestamp => DateTime.now.utc,
31
+ :type => new_event.type.to_s,
32
+ :data => _encode_data(new_event.data)
33
+ )
34
+ end
35
+ end
36
+ end
37
+
38
+ def _create_events_table
39
+ @db.create_table?(:events) do
40
+ primary_key :id
41
+ String :stream_id
42
+ Integer :revision
43
+ DateTime :timestamp
44
+ String :type
45
+ File :data
46
+ index [:stream_id, :revision], :unique => true
47
+ end
48
+ end
49
+
50
+ def _load_rows(id, from, to)
51
+ rows = @db.
52
+ from(:events).
53
+ where(:stream_id => id).
54
+ where('revision >= ?', from)
55
+
56
+ if to
57
+ rows = rows.where('revision <= ?', to)
58
+ end
59
+
60
+ rows
61
+ end
62
+
63
+ def _max_revision(id)
64
+ @db.from(:events).where(:stream_id => id).max(:revision) || 0
65
+ end
66
+
67
+ def _encode_data(data)
68
+ MultiJson.encode(data.to_hash).to_sequel_blob
69
+ end
70
+
71
+ def _decode_data(data)
72
+ MultiJson.decode(data).with_indifferent_access
73
+ end
74
+
75
+ def _transform_row(row)
76
+ type = row[:type].to_sym
77
+ data = _decode_data(row[:data])
78
+ HistoryBook::Event.new(type, data)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,22 @@
1
+ module HistoryBook
2
+ class Stream
3
+ attr_reader :events, :uncommitted_events
4
+
5
+ def initialize(id, store)
6
+ @id = id
7
+ @store = store
8
+ @events = store.load_events(id)
9
+ @uncommitted_events = []
10
+ end
11
+
12
+ def <<(event)
13
+ @uncommitted_events << event
14
+ end
15
+
16
+ def commit
17
+ @store.store_events(@id, @uncommitted_events)
18
+ @events.concat(@uncommitted_events)
19
+ @uncommitted_events = []
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module HistoryBook
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe HistoryBook::Event do
4
+ describe '==' do
5
+ it 'should be false if nothing is equal' do
6
+ event1 = HistoryBook::Event.new(:foo, :bar => 1)
7
+ event2 = HistoryBook::Event.new(:bar, :foo => 2)
8
+ event1.should_not == event2
9
+ end
10
+
11
+ it 'should be false if only type is the same' do
12
+ event1 = HistoryBook::Event.new(:foo, :bar => 1)
13
+ event2 = HistoryBook::Event.new(:foo, :foo => 2)
14
+ event1.should_not == event2
15
+ end
16
+
17
+ it 'should be false if only data is the same' do
18
+ event1 = HistoryBook::Event.new(:foo, :bar => 1)
19
+ event2 = HistoryBook::Event.new(:bar, :bar => 1)
20
+ event1.should_not == event2
21
+ end
22
+
23
+ it 'should be true if everything is the same' do
24
+ event1 = HistoryBook::Event.new(:foo, :bar => 1)
25
+ event2 = HistoryBook::Event.new(:foo, :bar => 1)
26
+ event1.should == event2
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe HistoryBook::Memory::Configuration do
4
+ describe 'create_store' do
5
+ it 'should return a memory store' do
6
+ config = HistoryBook::Memory::Configuration.new
7
+ config.create_store.should be_instance_of(HistoryBook::Memory::Store)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe HistoryBook::Memory::Store do
4
+ before(:each) do
5
+ HistoryBook::Memory::Store.reset!
6
+ @store = HistoryBook::Memory::Store.new
7
+ end
8
+
9
+ it_behaves_like 'a storage driver'
10
+
11
+ describe 'load_events' do
12
+ it 'should return events saved across instances' do
13
+ event1 = HistoryBook::Event.new(:foobar, :foo => 1)
14
+ event2 = HistoryBook::Event.new(:foobar, :foo => 2)
15
+
16
+ store1 = HistoryBook::Memory::Store.new
17
+ store1.store_events('test_id', event1)
18
+
19
+ store2 = HistoryBook::Memory::Store.new
20
+ store2.store_events('test_id', event2)
21
+ store2.load_events('test_id').should == [event1, event2]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe HistoryBook::Sequel::Configuration do
4
+ describe 'create_store' do
5
+ it 'should return a memory store' do
6
+ config = HistoryBook::Sequel::Configuration.new
7
+ config.connection_string = 'sqlite:/'
8
+ config.create_store.should be_instance_of(HistoryBook::Sequel::Store)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe HistoryBook::Sequel::Store do
4
+ around(:each) do |example|
5
+ @db = Sequel.sqlite(':memory:')
6
+ @store = HistoryBook::Sequel::Store.new(@db)
7
+ @db.transaction do
8
+ example.run
9
+ end
10
+ end
11
+
12
+ it_behaves_like 'a storage driver'
13
+
14
+ describe 'initialize' do
15
+ it 'should create an events table' do
16
+ @db.drop_table?(:events)
17
+ HistoryBook::Sequel::Store.new(@db)
18
+ @db.table_exists?(:events).should be_true
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe HistoryBook::Stream do
4
+ before(:each) do
5
+ HistoryBook::Memory::Store.reset!
6
+ @store = HistoryBook::Memory::Store.new
7
+
8
+ @stream = HistoryBook::Stream.new('test_id', @store)
9
+ end
10
+
11
+ describe '<<' do
12
+ it 'should append uncommitted events' do
13
+ event = HistoryBook::Event.new(:foobar, :foo => 1)
14
+ @stream << event
15
+ @stream.uncommitted_events.should == [event]
16
+ end
17
+ end
18
+
19
+ describe 'commit' do
20
+ it 'should commit all uncommitted events' do
21
+ event = HistoryBook::Event.new(:foobar, :foo => 1)
22
+ @stream << event
23
+ @stream.commit
24
+ @stream.events.should == [event]
25
+ @stream.uncommitted_events.should be_empty
26
+ end
27
+ end
28
+
29
+ describe 'events' do
30
+ it 'should be empty if there are no prior events' do
31
+ @stream.events.should be_empty
32
+ end
33
+
34
+ it 'should be populated with existing events' do
35
+ event = HistoryBook::Event.new(:foobar, :foo => 1)
36
+ @stream << event
37
+ @stream.commit
38
+ @stream = HistoryBook::Stream.new('test_id', @store)
39
+ @stream.events.should == [event]
40
+ end
41
+ end
42
+
43
+ describe 'uncommitted_events' do
44
+ it 'should be empty to start' do
45
+ @stream.uncommitted_events.should be_empty
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe HistoryBook do
4
+ before(:each) do
5
+ HistoryBook.configure(:memory)
6
+ HistoryBook::Memory::Store.reset!
7
+ end
8
+
9
+ describe 'self.configure' do
10
+ it 'should create a driver specific configuration' do
11
+ HistoryBook.config.should be_instance_of(HistoryBook::Memory::Configuration)
12
+ end
13
+
14
+ it 'should yield a configuration object' do
15
+ yielded = false
16
+ HistoryBook.configure(:memory) do |config|
17
+ yielded = true
18
+ config.should == HistoryBook.config
19
+ end
20
+ yielded.should be_true
21
+ end
22
+
23
+ it 'should not overwrite an existing configuration object of the same type' do
24
+ old_config = HistoryBook.config
25
+ HistoryBook.configure(:memory)
26
+ HistoryBook.config.object_id.should == old_config.object_id
27
+ end
28
+
29
+ it 'should overwrite an existing configuration object not of the same type' do
30
+ old_config = HistoryBook.config
31
+ HistoryBook.configure(:sequel)
32
+ HistoryBook.config.object_id.should_not == old_config.object_id
33
+ end
34
+
35
+ it 'should raise an error if driver does not exist' do
36
+ expect do
37
+ HistoryBook.configure(:asdf)
38
+ end.to raise_error("Unable to load asdf driver for history_book")
39
+ end
40
+ end
41
+
42
+ describe 'self.open' do
43
+ it 'should open an event stream for reading and writting' do
44
+ event = HistoryBook::Event.new(:something_happened, :foo => 'bar')
45
+ HistoryBook.open('test_id') do |stream|
46
+ stream << event
47
+ stream.commit
48
+ end
49
+
50
+ HistoryBook.open('test_id') do |stream|
51
+ stream.events.should == [event]
52
+ end
53
+ end
54
+
55
+ it 'should yield an event stream' do
56
+ yielded = false
57
+ HistoryBook.open('test_id') do |stream|
58
+ yielded = true
59
+ stream.should_not be_nil
60
+ end
61
+ yielded.should be_true
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,13 @@
1
+ require 'history_book'
2
+ require 'history_book/memory/configuration'
3
+ require 'history_book/sequel/configuration'
4
+
5
+ support_dir = File.expand_path('../support', __FILE__)
6
+ Dir.glob("#{support_dir}/**/*.rb") do |file|
7
+ require file
8
+ end
9
+
10
+ RSpec.configure do |config|
11
+ config.treat_symbols_as_metadata_keys_with_true_values = true
12
+ config.order = 'random'
13
+ end
@@ -0,0 +1,11 @@
1
+ module HistoryBook
2
+ module Memory
3
+ class Store
4
+ def self.reset!
5
+ @@events_lock.synchronize do
6
+ @@events = {}
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,63 @@
1
+ shared_examples 'a storage driver' do
2
+ describe 'load_events' do
3
+ it 'should be empty when no events are stored' do
4
+ @store.load_events('empty_id').should be_empty
5
+ end
6
+
7
+ it 'should return previously stored events' do
8
+ event = HistoryBook::Event.new(:foobar, :foo => 1)
9
+ @store.store_events('test_id', event)
10
+ @store.load_events('test_id').should == [event]
11
+ end
12
+
13
+ it 'should return all events up to a specified sequence' do
14
+ event1 = HistoryBook::Event.new(:foobar, :foo => 1)
15
+ event2 = HistoryBook::Event.new(:foobar, :foo => 2)
16
+ @store.store_events('test_id', event1)
17
+ @store.store_events('test_id', event2)
18
+ @store.load_events('test_id', :to => 1).should == [event1]
19
+ end
20
+
21
+ it 'should return all events since a specified sequence' do
22
+ event1 = HistoryBook::Event.new(:foobar, :foo => 1)
23
+ event2 = HistoryBook::Event.new(:foobar, :foo => 2)
24
+ @store.store_events('test_id', event1)
25
+ @store.store_events('test_id', event2)
26
+ @store.load_events('test_id', :from => 2).should == [event2]
27
+ end
28
+
29
+ it 'should return all events in a specified range' do
30
+ event1 = HistoryBook::Event.new(:foobar, :foo => 1)
31
+ event2 = HistoryBook::Event.new(:foobar, :foo => 2)
32
+ event3 = HistoryBook::Event.new(:foobar, :foo => 3)
33
+ event4 = HistoryBook::Event.new(:foobar, :foo => 4)
34
+ @store.store_events('test_id', [event1, event2, event3, event4])
35
+ @store.load_events('test_id', :from => 2, :to => 3).should == [event2, event3]
36
+ end
37
+ end
38
+
39
+ describe 'store_events' do
40
+ it 'should store events in order' do
41
+ event1 = HistoryBook::Event.new(:foobar, :foo => 1)
42
+ event2 = HistoryBook::Event.new(:foobar, :foo => 2)
43
+ @store.store_events('test_id', event1)
44
+ @store.store_events('test_id', event2)
45
+ @store.load_events('test_id').should == [event1, event2]
46
+ end
47
+
48
+ it 'should store an array of events' do
49
+ event1 = HistoryBook::Event.new(:foobar, :foo => 1)
50
+ event2 = HistoryBook::Event.new(:foobar, :foo => 2)
51
+ @store.store_events('test_id', [event1, event2])
52
+ @store.load_events('test_id').should == [event1, event2]
53
+ end
54
+
55
+ it 'should store events by id' do
56
+ event1 = HistoryBook::Event.new(:foobar, :foo => 1)
57
+ event2 = HistoryBook::Event.new(:foobar, :foo => 2)
58
+ @store.store_events('test_id1', event1)
59
+ @store.store_events('test_id2', event2)
60
+ @store.load_events('test_id1').should == [event1]
61
+ end
62
+ end
63
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: history_book
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - John Downey
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
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: '3.0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: multi_json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '1.0'
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.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.11'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.11'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: sequel
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '3.40'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '3.40'
94
+ - !ruby/object:Gem::Dependency
95
+ name: sqlite3-ruby
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '1.3'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '1.3'
110
+ - !ruby/object:Gem::Dependency
111
+ name: yajl-ruby
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '1.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '1.1'
126
+ description: This library provides an interface for event sourcing over a pluggable
127
+ back end data store. Currently it supports the Sequel gem and its data stores as
128
+ well as an in-memory data store for testing.
129
+ email:
130
+ - jdowney@gmail.com
131
+ executables: []
132
+ extensions: []
133
+ extra_rdoc_files: []
134
+ files:
135
+ - lib/history_book/event.rb
136
+ - lib/history_book/memory/configuration.rb
137
+ - lib/history_book/memory/store.rb
138
+ - lib/history_book/sequel/configuration.rb
139
+ - lib/history_book/sequel/store.rb
140
+ - lib/history_book/stream.rb
141
+ - lib/history_book/version.rb
142
+ - lib/history_book.rb
143
+ - spec/history_book/event_spec.rb
144
+ - spec/history_book/memory/configuration_spec.rb
145
+ - spec/history_book/memory/store_spec.rb
146
+ - spec/history_book/sequel/configuration_spec.rb
147
+ - spec/history_book/sequel/store_spec.rb
148
+ - spec/history_book/stream_spec.rb
149
+ - spec/history_book_spec.rb
150
+ - spec/spec_helper.rb
151
+ - spec/support/resettable_memory_store.rb
152
+ - spec/support/shared_examples_for_stores.rb
153
+ - README.md
154
+ homepage: http://github.com/jtdowney/history_book
155
+ licenses: []
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ none: false
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 1.8.23
175
+ signing_key:
176
+ specification_version: 3
177
+ summary: HistoryBook is a ruby implementation of the event sourcing pattern.
178
+ test_files:
179
+ - spec/history_book/event_spec.rb
180
+ - spec/history_book/memory/configuration_spec.rb
181
+ - spec/history_book/memory/store_spec.rb
182
+ - spec/history_book/sequel/configuration_spec.rb
183
+ - spec/history_book/sequel/store_spec.rb
184
+ - spec/history_book/stream_spec.rb
185
+ - spec/history_book_spec.rb
186
+ - spec/spec_helper.rb
187
+ - spec/support/resettable_memory_store.rb
188
+ - spec/support/shared_examples_for_stores.rb