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 +30 -0
- data/lib/history_book.rb +39 -0
- data/lib/history_book/event.rb +14 -0
- data/lib/history_book/memory/configuration.rb +11 -0
- data/lib/history_book/memory/store.rb +32 -0
- data/lib/history_book/sequel/configuration.rb +15 -0
- data/lib/history_book/sequel/store.rb +82 -0
- data/lib/history_book/stream.rb +22 -0
- data/lib/history_book/version.rb +3 -0
- data/spec/history_book/event_spec.rb +29 -0
- data/spec/history_book/memory/configuration_spec.rb +10 -0
- data/spec/history_book/memory/store_spec.rb +24 -0
- data/spec/history_book/sequel/configuration_spec.rb +11 -0
- data/spec/history_book/sequel/store_spec.rb +21 -0
- data/spec/history_book/stream_spec.rb +48 -0
- data/spec/history_book_spec.rb +64 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/resettable_memory_store.rb +11 -0
- data/spec/support/shared_examples_for_stores.rb +63 -0
- metadata +188 -0
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# HistoryBook [](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).
|
data/lib/history_book.rb
ADDED
@@ -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,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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|