sandthorn 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c7be6683c105bcc9b6c0366a63d191c15260735
4
+ data.tar.gz: be5e8b9362f8ec00c1fa2949532c5064f81784e4
5
+ SHA512:
6
+ metadata.gz: 064291bac86efb3730d596afd4079dc3d71085f2974aa036df707594561886761a85f3ff73bcaec9d5cfc24cfb47c712b0a4a3263e2c636da9a3079dda319c1a
7
+ data.tar.gz: a28989f9f69f112666b7e21c5b81c01f952451b3141d79012df97ab01964ebe462e28fd74bb9d2bfc897fa520112b07b8d8796c726036598fdd9fb43929efa84
data/.autotest ADDED
@@ -0,0 +1,3 @@
1
+ Autotest.add_hook :initialize do |at|
2
+ %w{.git spec/db}.each {|exception| at.add_exception(exception)}
3
+ end
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ *.sqlite3
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format d
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ sandthorn
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sandthorn.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,72 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sandthorn (0.0.1)
5
+ dirty_hashy
6
+ uuidtools
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.0.4)
12
+ i18n (~> 0.6, >= 0.6.9)
13
+ minitest (~> 4.2)
14
+ multi_json (~> 1.3)
15
+ thread_safe (~> 0.1)
16
+ tzinfo (~> 0.3.37)
17
+ atomic (1.1.16)
18
+ autotest-standalone (4.5.11)
19
+ awesome_print (1.2.0)
20
+ coderay (1.1.0)
21
+ diff-lcs (1.2.5)
22
+ dirty_hashy (0.2.1)
23
+ activesupport
24
+ gem-release (0.7.1)
25
+ i18n (0.6.9)
26
+ method_source (0.8.2)
27
+ minitest (4.7.5)
28
+ multi_json (1.9.0)
29
+ pg (0.17.1)
30
+ pry (0.9.12.6)
31
+ coderay (~> 1.0)
32
+ method_source (~> 0.8)
33
+ slop (~> 3.4)
34
+ pry-doc (0.6.0)
35
+ pry (~> 0.9)
36
+ yard (~> 0.8)
37
+ rake (10.1.1)
38
+ rspec (2.14.1)
39
+ rspec-core (~> 2.14.0)
40
+ rspec-expectations (~> 2.14.0)
41
+ rspec-mocks (~> 2.14.0)
42
+ rspec-core (2.14.8)
43
+ rspec-expectations (2.14.5)
44
+ diff-lcs (>= 1.1.3, < 2.0)
45
+ rspec-mocks (2.14.6)
46
+ sandthorn_driver_sequel (1.0.5)
47
+ pg
48
+ sequel
49
+ sequel (4.8.0)
50
+ slop (3.5.0)
51
+ sqlite3 (1.3.9)
52
+ thread_safe (0.2.0)
53
+ atomic (>= 1.1.7, < 2)
54
+ tzinfo (0.3.39)
55
+ uuidtools (2.1.4)
56
+ yard (0.8.7.3)
57
+
58
+ PLATFORMS
59
+ ruby
60
+
61
+ DEPENDENCIES
62
+ autotest-standalone
63
+ awesome_print
64
+ bundler (~> 1.3)
65
+ gem-release
66
+ pry
67
+ pry-doc
68
+ rake
69
+ rspec
70
+ sandthorn!
71
+ sandthorn_driver_sequel
72
+ sqlite3
data/Gemfile.lock.old ADDED
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sandthorn (0.0.1)
5
+ upptec_event_sequel_driver (~> 1.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ autotest-standalone (4.5.11)
11
+ awesome_print (1.2.0)
12
+ coderay (1.1.0)
13
+ diff-lcs (1.2.5)
14
+ gem-release (0.7.1)
15
+ method_source (0.8.2)
16
+ pg (0.17.1)
17
+ pry (0.9.12.6)
18
+ coderay (~> 1.0)
19
+ method_source (~> 0.8)
20
+ slop (~> 3.4)
21
+ pry-doc (0.5.1)
22
+ pry (>= 0.9)
23
+ yard (>= 0.8)
24
+ rake (10.1.1)
25
+ rspec (2.14.1)
26
+ rspec-core (~> 2.14.0)
27
+ rspec-expectations (~> 2.14.0)
28
+ rspec-mocks (~> 2.14.0)
29
+ rspec-core (2.14.8)
30
+ rspec-expectations (2.14.5)
31
+ diff-lcs (>= 1.1.3, < 2.0)
32
+ rspec-mocks (2.14.6)
33
+ sequel (4.7.0)
34
+ slop (3.4.7)
35
+ sqlite3 (1.3.9)
36
+ upptec_event_sequel_driver (1.2.2)
37
+ pg
38
+ sequel
39
+ yard (0.8.7.3)
40
+
41
+ PLATFORMS
42
+ ruby
43
+
44
+ DEPENDENCIES
45
+ autotest-standalone
46
+ awesome_print
47
+ bundler (~> 1.3)
48
+ gem-release
49
+ pry
50
+ pry-doc
51
+ rake
52
+ rspec
53
+ sandthorn!
54
+ sqlite3
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Sandthorn Event Sourcing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Lars Krantz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Sandthorn Event Sourcing
2
+ A ruby framwork for saving an object's state as a series of events, and tracking non state changing events.
3
+
4
+ ## What is Event Sourcing
5
+
6
+ "Capture all changes to an application state as a sequence of events."
7
+ [Event Sourcing](http://martinfowler.com/eaaDev/EventSourcing.html)
8
+
9
+ ## The short story
10
+
11
+ Think of it as an object database where you not only what the new value of the attribute is but when and why it changed.
12
+ _Example:_
13
+
14
+ ```ruby
15
+
16
+ #Setup the Aggregate
17
+
18
+ require 'sandthorn/aggregate_root_dirty_hashy' #the one available right now
19
+
20
+ class Ship
21
+ include Sandthorn::AggregateRoot::DirtyHashy
22
+ attr_reader :name
23
+
24
+ def initialize name: nil, shipping_company: nil
25
+ @name = name
26
+ end
27
+
28
+ # state-changing command
29
+ def rename! new_name: ""
30
+ unless new_name.empty? or new_name == name
31
+ @name = new_name
32
+ ship_was_renamed
33
+ end
34
+ end
35
+ private
36
+ # commit the event and state-change is automatically recorded.
37
+ def ship_was_renamed
38
+ commit
39
+ end
40
+ end
41
+
42
+ #Setup the framework with the sequel driver for persistance
43
+ url = "path to sql" #Example sqlite://path/sequel_driver.sqlite3
44
+ catch_all_config = [ { driver: SandthornDriverSequel.driver_from_url(url: url) } ]
45
+ Sandthorn.configuration = catch_all_config
46
+
47
+ #migrate db schema for the sequel driver
48
+ migrator = SandthornDriverSequel::Migration.new url: url
49
+ SandthornDriverSequel.migrate_db url: url
50
+
51
+ #Usage
52
+ ship = Ship.new "Titanic"
53
+ ship.rename! "Vasa"
54
+ puts ship.aggregate_events
55
+ ship.save
56
+
57
+ new_ship = Ship.find ship.id
58
+ puts ship.name
59
+ .
60
+ .
61
+
62
+ For more info look at the specs.
63
+
64
+ ```
65
+
66
+ ## Installation
67
+
68
+ Add this line to your application's Gemfile:
69
+
70
+ gem 'sandthorn'
71
+
72
+ And then execute:
73
+
74
+ $ bundle
75
+
76
+ Or install it yourself as:
77
+
78
+ $ gem install sandthorn
79
+
80
+ ## Usage
81
+
82
+ TODO: Write usage instructions here
83
+
84
+ ## Contributing
85
+
86
+ 1. Fork it
87
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
88
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
89
+ 4. Push to the branch (`git push origin my-new-feature`)
90
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/sandthorn.rb ADDED
@@ -0,0 +1,78 @@
1
+ require "sandthorn/version"
2
+ require "sandthorn/errors"
3
+ require 'uuidtools'
4
+ require 'yaml'
5
+
6
+ module Sandthorn
7
+ class << self
8
+ def configuration= configuration
9
+ @configuration = configuration
10
+ end
11
+ def configuration
12
+ @configuration ||= []
13
+ end
14
+
15
+ def serialize data
16
+ #Marshal.dump(data)
17
+ YAML::dump(data)
18
+ #Oj.dump(data)
19
+ #MessagePack.pack(data, symbolize_keys: true)
20
+ end
21
+
22
+ def deserialize data
23
+ #Marshal.load(data)
24
+ YAML::load(data)
25
+ #Oj.load(data)
26
+ #MessagePack.unpack(data, symbolize_keys: true)
27
+ end
28
+
29
+ def generate_aggregate_id
30
+ UUIDTools::UUID.random_create.to_s
31
+ end
32
+
33
+ def get_aggregate_events aggregate_id, class_name
34
+ driver_for(class_name).get_aggregate_events aggregate_id, class_name
35
+ end
36
+
37
+ def save_events aggregate_events, originating_aggregate_version, aggregate_id, class_name
38
+ #begin
39
+ driver_for(class_name).save_events aggregate_events, originating_aggregate_version, aggregate_id, *class_name
40
+ #rescue UpptecEventSequelDriver::Errors::WrongAggregateVersionError => sequel_error
41
+ # raise UpptecEventFramework::Errors::ConcurrencyError.new sequel_error.message
42
+ #end
43
+ end
44
+
45
+ def get_aggregate aggregate_id, class_name
46
+ driver_for(class_name).get_aggregate aggregate_id, class_name
47
+ end
48
+
49
+ def save_snapshot aggregate_snapshot, aggregate_id, class_name
50
+ driver_for(class_name).save_snapshot aggregate_snapshot, aggregate_id, class_name
51
+ end
52
+
53
+ private
54
+ def driver_for class_name, &block
55
+ driver = identify_driver_from_class class_name
56
+ block.call(driver) if block_given?
57
+ driver
58
+ end
59
+ def identify_driver_from_class class_name
60
+ matches = configuration.select do |conf|
61
+ r = Regexp.new "^#{conf[:aggregate_pattern]}"
62
+ pattern = class_name.to_s
63
+ conf[:aggregate_pattern].nil? || r.match(pattern)
64
+ end
65
+ raise Sandthorn::Errors::ConfigurationError.new "Aggregate class #{class_name} is not configured for EventStore" if matches.empty?
66
+ first_match = matches.first
67
+ first_match[:driver]
68
+ #UpptecEventSequelDriver.driver_from_url url: first_match[:url], context: first_match[:context]
69
+ end
70
+ # def drivers_for_class_names class_names: []
71
+ # return all_drivers if class_names.empty?
72
+ # class_names.map { |e| driver_for e }.uniq { |d| { url: d.url, context: d.context } }
73
+ # end
74
+ # def all_drivers
75
+ # configuration.map { |e| UpptecEventSequelDriver.driver_from_url url: e[:url], context: e[:context] }
76
+ # end
77
+ end
78
+ end
@@ -0,0 +1,186 @@
1
+ module Sandthorn
2
+ module AggregateRoot
3
+ module Base
4
+
5
+ attr_reader :aggregate_id
6
+ attr_reader :aggregate_events
7
+ attr_reader :aggregate_current_event_version
8
+ attr_reader :aggregate_originating_version
9
+ attr_reader :aggregate_stored_serialized_object
10
+
11
+ alias :id :aggregate_id
12
+
13
+
14
+ def aggregate_base_initialize
15
+ @aggregate_current_event_version = 0
16
+ @aggregate_originating_version = 0
17
+ @aggregate_events = []
18
+ end
19
+
20
+
21
+
22
+ def save
23
+ aggregate_events.each do |event|
24
+ event[:event_data] = Sandthorn.serialize event[:event_args]
25
+ event[:event_args] = nil #Not send extra data over the wire
26
+ end
27
+ unless aggregate_events.empty?
28
+ Sandthorn.save_events( aggregate_events, aggregate_originating_version, aggregate_id, self.class.name)
29
+ @aggregate_events = []
30
+ @aggregate_originating_version = @aggregate_current_event_version
31
+ end
32
+ self
33
+ end
34
+
35
+ def commit *args
36
+ increase_current_aggregate_version!
37
+ method_name = caller[0][/`.*'/][1..-2]
38
+ aggregate_attribute_deltas = get_delta
39
+
40
+ unless aggregate_attribute_deltas.empty?
41
+ data = {:method_name => method_name, :method_args => args, :attribute_deltas => aggregate_attribute_deltas}
42
+ data.merge!({trace: @aggregate_trace_information}) unless @aggregate_trace_information.nil? || @aggregate_trace_information.empty?
43
+ @aggregate_events << ({:aggregate_version => @aggregate_current_event_version, :event_name => method_name, :event_args => data})
44
+ end
45
+ self
46
+ end
47
+
48
+ alias :record_event :commit
49
+
50
+
51
+ def all
52
+ end
53
+
54
+ def aggregate_trace args
55
+ @aggregate_trace_information = args
56
+ yield self
57
+ @aggregate_trace_information = nil
58
+ end
59
+
60
+ module ClassMethods
61
+
62
+ @@aggregate_trace_information = nil
63
+ def aggregate_trace args
64
+ @@aggregate_trace_information = args
65
+ @aggregate_trace_information = args
66
+ yield self
67
+ @@aggregate_trace_information = nil
68
+ @aggregate_trace_information = nil
69
+ end
70
+
71
+ def find aggregate_id
72
+ class_name = self.respond_to?(:name) ? self.name : self.class # to be able to extend a string for example.
73
+ events = Sandthorn.get_aggregate(aggregate_id, class_name)
74
+ raise Sandthorn::Errors::AggregateNotFound unless events and !events.empty?
75
+
76
+ transformed_events = events.map { |e| e.merge(event_args: Sandthorn.deserialize(e[:event_data])) }
77
+ aggregate_build transformed_events
78
+ end
79
+
80
+ def new *args
81
+ aggregate = super
82
+ aggregate.aggregate_base_initialize
83
+ aggregate.aggregate_trace @@aggregate_trace_information do |aggr|
84
+ aggr.aggregate_initialize
85
+ aggr.send :set_aggregate_id, Sandthorn.generate_aggregate_id
86
+ aggr.send :commit, *args
87
+ return aggr
88
+ end
89
+ end
90
+
91
+
92
+ def aggregate_build events
93
+ first_event = events.first()
94
+ current_aggregate_version = 0
95
+ if first_event[:event_name] == "aggregate_set_from_snapshot"
96
+ aggregate = first_event[:event_args][0]
97
+ current_aggregate_version = aggregate.aggregate_originating_version
98
+ events.shift
99
+ # elsif first_event[:event_name] == "instance_extended_as_aggregate"
100
+ # aggregate = first_event[:event_args][0]
101
+ #aggregate.extend AggregateRoot
102
+ # events.pop
103
+ else
104
+ new_args = events.first()[:event_args][:method_args]
105
+
106
+ if new_args.nil?
107
+ aggregate = new
108
+ else
109
+ aggregate = new *new_args
110
+ end
111
+ aggregate.send :aggregate_clear_current_event_version!
112
+ end
113
+
114
+ attributes = {}
115
+ events.each do |event|
116
+ event_args = event[:event_args]
117
+ event_name = event[:event_name]
118
+
119
+ if event_name == "aggregate_set_from_snapshot"
120
+ #aggregate.send event_name,*event_args
121
+ #set the attribute hash instead of instance varaibles directly
122
+ next
123
+ end
124
+ next if event_name == "instance_extended_as_aggregate"
125
+
126
+ attribute_deltas = event_args[:attribute_deltas]
127
+ if (event[:aggregate_version].nil? == false)
128
+ current_aggregate_version = event[:aggregate_version]
129
+ end
130
+
131
+ if !attribute_deltas.nil?
132
+ deltas = {}
133
+ attribute_deltas.each do |delta|
134
+ deltas[delta[:attribute_name]] = delta[:new_value]
135
+ end
136
+ attributes.merge! deltas
137
+ end
138
+ end
139
+ aggregate.send :clear_aggregate_events
140
+ aggregate.send :set_orginating_aggregate_version!, current_aggregate_version
141
+ aggregate.send :set_current_aggregate_version!, current_aggregate_version
142
+ aggregate.send :set_instance_variables!, attributes
143
+ aggregate
144
+ end
145
+ end
146
+
147
+ private
148
+
149
+ def set_instance_variables! attributes
150
+ attributes.each_pair do |k,v|
151
+ self.instance_variable_set "@#{k}", v
152
+ end
153
+ end
154
+
155
+ def extract_relevant_aggregate_instance_variables
156
+ instance_variables.select { |i| i.to_s != "@hashy" && (!i.to_s.start_with?("@aggregate_") || i.to_s == "@aggregate_id") }
157
+ end
158
+
159
+ def set_orginating_aggregate_version! aggregate_version
160
+ @aggregate_originating_version = aggregate_version
161
+ end
162
+
163
+ def increase_current_aggregate_version!
164
+ @aggregate_current_event_version += 1
165
+ end
166
+
167
+ def set_current_aggregate_version! aggregate_version
168
+ @aggregate_current_event_version = aggregate_version
169
+ end
170
+
171
+ def clear_aggregate_events
172
+ @aggregate_events = []
173
+ @aggregate_attribute_deltas = []
174
+ end
175
+
176
+ def aggregate_clear_current_event_version!
177
+ @aggregate_current_event_version = 0
178
+ end
179
+
180
+ def set_aggregate_id aggregate_id
181
+ @aggregate_id = aggregate_id
182
+ end
183
+
184
+ end
185
+ end
186
+ end