euston 1.0.1-java → 1.1.0-java

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/Gemfile CHANGED
@@ -1,3 +1,2 @@
1
1
  source :rubygems
2
2
  gemspec
3
-
data/euston.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'euston'
3
- s.version = '1.0.1'
4
- s.date = '2011-09-27'
3
+ s.version = '1.1.0'
4
+ s.date = '2011-10-03'
5
5
  s.platform = RUBY_PLATFORM.to_s == 'java' ? 'java' : Gem::Platform::RUBY
6
6
  s.authors = ['Lee Henson', 'Guy Boertje']
7
7
  s.email = ['lee.m.henson@gmail.com', 'guyboertje@gmail.com']
@@ -17,10 +17,16 @@ Gem::Specification.new do |s|
17
17
  lib/euston.rb
18
18
  lib/euston/aggregate_command_map.rb
19
19
  lib/euston/aggregate_root.rb
20
+ lib/euston/aggregate_root_dsl_methods.rb
21
+ lib/euston/aggregate_root_private_method_names.rb
22
+ lib/euston/command.rb
20
23
  lib/euston/command_bus.rb
21
24
  lib/euston/command_handler.rb
25
+ lib/euston/command_handler_private_method_names.rb
22
26
  lib/euston/command_headers.rb
27
+ lib/euston/event.rb
23
28
  lib/euston/event_handler.rb
29
+ lib/euston/event_handler_private_method_names.rb
24
30
  lib/euston/event_headers.rb
25
31
  lib/euston/null_logger.rb
26
32
  lib/euston/repository.rb
@@ -35,10 +41,10 @@ Gem::Specification.new do |s|
35
41
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
36
42
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
37
43
 
44
+ s.add_dependency 'activemodel', '~> 3.0.9'
38
45
  s.add_dependency 'activesupport', '~> 3.0.9'
39
- s.add_dependency 'euston-eventstore', '~> 1.0.0'
40
- s.add_dependency 'require_all', '~> 1.2.0'
46
+
41
47
  s.add_development_dependency 'fuubar', '~> 0.0.0'
42
48
  s.add_development_dependency 'rspec', '~> 2.6.0'
43
49
  s.add_development_dependency 'uuid', '~> 2.3.0'
44
- end
50
+ end
data/lib/euston.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'active_support/concern'
2
- require 'require_all'
2
+ require 'active_model'
3
3
  require 'ostruct'
4
4
 
5
5
  module Euston
@@ -21,5 +21,18 @@ end
21
21
 
22
22
  Euston.uuid = Uuid
23
23
 
24
- require 'euston-eventstore'
25
- require_rel 'euston'
24
+ require 'euston/aggregate_command_map'
25
+ require 'euston/aggregate_root_private_method_names'
26
+ require 'euston/aggregate_root_dsl_methods'
27
+ require 'euston/command'
28
+ require 'euston/command_bus'
29
+ require 'euston/command_handler_private_method_names'
30
+ require 'euston/command_handler'
31
+ require 'euston/command_headers'
32
+ require 'euston/event'
33
+ require 'euston/event_handler_private_method_names'
34
+ require 'euston/event_handler'
35
+ require 'euston/event_headers'
36
+ require 'euston/null_logger'
37
+ require 'euston/aggregate_root'
38
+ require 'euston/repository'
@@ -1,50 +1,22 @@
1
1
  module Euston
2
2
  module AggregateRoot
3
3
  extend ActiveSupport::Concern
4
+ include Euston::AggregateRootPrivateMethodNames
5
+ include Euston::AggregateRootDslMethods
6
+ include Euston::EventHandler
4
7
 
5
8
  module ClassMethods
6
- def applies event, version, &consumer
7
- define_method "__consume__#{event}__v#{version}" do |*args| instance_exec *args, &consumer end
8
- end
9
-
10
- def consumes *arguments, &consumer #*args is an array of symbols plus an optional options hash at the end
11
- commands, options = [], {}
12
- while (arg = arguments.shift) do
13
- commands << arg if arg.is_a?(Symbol)
14
- options = arg if arg.is_a?(Hash)
15
- end
16
- commands.each do |command|
17
- define_method "__consume__#{command}" do |*args| instance_exec *args, &consumer end
18
-
19
- map_command :map_command_as_aggregate_method, self, command, options
20
- end
21
- end
22
-
23
- def created_by command, options = {}, &consumer
24
- define_method "__consume__#{command}" do |*args| instance_exec *args, &consumer end
25
-
26
- map_command :map_command_as_aggregate_constructor, self, command, options
27
- end
28
-
29
- def hydrate(stream)
9
+ def hydrate stream, snapshot = nil
30
10
  instance = self.new
31
- instance.send :reconstitute_from_history, stream
11
+ instance.send :apply_snapshot, snapshot unless snapshot.nil?
12
+ instance.send :apply_stream, stream
32
13
  instance
33
14
  end
34
-
35
- private
36
-
37
- def map_command(entry_point, type, command, opts)
38
- id = opts.has_key?(:id) ? opts[:id] : :id
39
- to_i = opts.key?(:to_i) ? opts[:to_i] : []
40
-
41
- Euston::AggregateCommandMap.send entry_point, type, command, id, to_i
42
- end
43
15
  end
44
16
 
45
17
  module InstanceMethods
46
18
  def initialize aggregate_id = nil
47
- @aggregate_id = aggregate_id unless aggregate_id.nil?
19
+ @aggregate_id = aggregate_id
48
20
  end
49
21
 
50
22
  attr_reader :aggregate_id
@@ -76,6 +48,19 @@ module Euston
76
48
  self
77
49
  end
78
50
 
51
+ def take_snapshot
52
+ methods = self.class.instance_methods
53
+ regex = self.class.take_snapshot_regexp
54
+ methods = methods.map { |m| regex.match m }.compact
55
+
56
+ raise "You tried to take a snapshot of #{self.class.name} but no snapshot method was found." if methods.empty?
57
+
58
+ version = methods.map { |m| m[1].to_i }.sort.last
59
+ name = self.class.take_snapshot_method_name version
60
+
61
+ { :version => version, :payload => send(name) }
62
+ end
63
+
79
64
  def replay_event(headers, event)
80
65
  headers = Euston::EventHeaders.from_hash(headers) if headers.is_a?(Hash)
81
66
  command = headers.command
@@ -92,7 +77,7 @@ module Euston
92
77
  protected
93
78
 
94
79
  def apply_event(type, version, body = {})
95
- event = Euston::EventStore::EventMessage.new(body.is_a?(Hash) ? body : body.marshal_dump)
80
+ event = Euston::Event.new(body.is_a?(OpenStruct) ? body.marshal_dump : body)
96
81
  event.headers.merge! :id => Euston.uuid.generate,
97
82
  :type => type,
98
83
  :version => version,
@@ -106,32 +91,48 @@ module Euston
106
91
  uncommitted_events << event
107
92
  end
108
93
 
109
- def handle_command(headers, command)
110
- name = "__consume__#{headers.type}"
111
- method(name).call OpenStruct.new(command).freeze
112
- end
94
+ def apply_snapshot snapshot
95
+ if !snapshot.nil?
96
+ version = snapshot.headers[:version]
97
+ raise "Trying to load a snapshot of aggregate #{self.class.name} but it does not have a load_snapshot method for version #{version}!" unless respond_to? self.class.load_snapshot_method_name(version)
113
98
 
114
- def handle_event(headers, event)
115
- name = "__consume__#{headers.type}__v#{headers.version}"
116
- if respond_to? name.to_sym
117
- method(name).call OpenStruct.new(event).freeze
118
- else
119
- raise "Couldn't find an event handler for #{headers.type} (v#{headers.version}) on #{self.class}. Did you forget an 'applies' block?"
99
+ name = self.class.load_snapshot_method_name version
100
+ self.send name, snapshot.payload
120
101
  end
121
102
  end
122
103
 
123
- def reconstitute_from_history(stream)
104
+ def apply_stream stream
105
+ @aggregate_id = stream.stream_id
106
+
124
107
  events = stream.committed_events
125
108
  return if events.empty?
126
109
 
127
110
  raise "This aggregate cannot apply a historical event stream because it is not empty." unless uncommitted_events.empty? && initial_version == 0
128
111
 
129
- @aggregate_id = stream.stream_id
130
-
131
112
  events.each_with_index do |event, i|
132
113
  replay_event Euston::EventHeaders.from_hash(event.headers), event.body
133
114
  end
134
115
  end
116
+
117
+ def handle_command headers, command
118
+ deliver_message headers, command, :consumes_method_name, 'a command', "a 'consumes' block"
119
+ end
120
+
121
+ def handle_event headers, event
122
+ deliver_message headers, event, :applies_method_name, 'an event', "an 'applies' block"
123
+ end
124
+
125
+ private
126
+
127
+ def deliver_message headers, message, name_method, message_kind, expected_block_kind
128
+ name = self.class.send name_method, headers.type, headers.version
129
+
130
+ if respond_to? name.to_sym
131
+ method(name).call OpenStruct.new(message).freeze
132
+ else
133
+ raise "Couldn't deliver #{message_kind} (#{headers.type} v#{headers.version}) to #{self.class}. Did you forget #{expected_block_kind}?"
134
+ end
135
+ end
135
136
  end
136
137
  end
137
138
  end
@@ -0,0 +1,54 @@
1
+ module Euston
2
+ module AggregateRootDslMethods
3
+ extend ActiveSupport::Concern
4
+ include Euston::AggregateRootPrivateMethodNames
5
+
6
+ module ClassMethods
7
+ def applies event, version = 1, &block
8
+ define_private_method applies_method_name(event, version), &block
9
+ end
10
+
11
+ def consumes *arguments, &block #*args is an array of symbols plus an optional options hash at the end
12
+ commands, options = [], {}
13
+
14
+ while (arg = arguments.shift) do
15
+ commands << { :name => arg, :version => 1 } if arg.is_a?(Symbol)
16
+ commands.last[:version] = arg if arg.is_a?(Integer)
17
+ options = arg if arg.is_a?(Hash)
18
+ end
19
+
20
+ commands.each do |command|
21
+ define_private_method consumes_method_name(command[:name], command[:version]), &block
22
+ map_command :map_command_as_aggregate_method, self, command[:name], options
23
+ end
24
+ end
25
+
26
+ def created_by command, version = 1, options = {}, &block
27
+ define_method consumes_method_name(command, version), &block
28
+
29
+ map_command :map_command_as_aggregate_constructor, self, command, options
30
+ end
31
+
32
+ def load_snapshot version, &block
33
+ define_private_method load_snapshot_method_name(version), &block
34
+ end
35
+
36
+ def take_snapshot version, &block
37
+ define_private_method take_snapshot_method_name(version), &block
38
+ end
39
+
40
+ private
41
+
42
+ def define_private_method name, &block
43
+ define_method name do |*args| instance_exec *args, &block end
44
+ end
45
+
46
+ def map_command(entry_point, type, command, opts)
47
+ id = opts.has_key?(:id) ? opts[:id] : :id
48
+ to_i = opts.key?(:to_i) ? opts[:to_i] : []
49
+
50
+ Euston::AggregateCommandMap.send entry_point, type, command, id, to_i
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,31 @@
1
+ module Euston
2
+ module AggregateRootPrivateMethodNames
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def applies_method_name event, version
7
+ "__apply__#{event}__v#{version}__"
8
+ end
9
+
10
+ def consumes_method_name command, version
11
+ "__consume__#{command}__v#{version}__"
12
+ end
13
+
14
+ def id_from_event_method_name type, version
15
+ "__id_from_event_#{type}__v#{version}__"
16
+ end
17
+
18
+ def load_snapshot_method_name version
19
+ "__load_snapshot__v#{version}__"
20
+ end
21
+
22
+ def take_snapshot_method_name version
23
+ "__take_snapshot__v#{version}__"
24
+ end
25
+
26
+ def take_snapshot_regexp
27
+ /__take_snapshot__v(\d+)__/
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,27 @@
1
+ module Euston
2
+ class Command
3
+ include ActiveModel::Validations
4
+
5
+ def initialize body
6
+ @headers = { :id => Uuid.generate,
7
+ :type => self.class.to_s.split('::').pop.underscore.to_sym }
8
+ @body = body
9
+ end
10
+
11
+ def read_attribute_for_validation key
12
+ @body[key]
13
+ end
14
+
15
+ def to_hash
16
+ { :headers => @headers.merge(:version => version), :body => @body }
17
+ end
18
+
19
+ def id
20
+ @headers[:id]
21
+ end
22
+
23
+ def version
24
+ 1
25
+ end
26
+ end
27
+ end
@@ -1,10 +1,11 @@
1
1
  module Euston
2
2
  module CommandHandler
3
3
  extend ActiveSupport::Concern
4
+ include Euston::CommandHandlerPrivateMethodNames
4
5
 
5
6
  module ClassMethods
6
7
  def version number, &consumer
7
- define_method "__version__#{number}" do |*args|
8
+ define_method command_handler_method_name(number) do |*args|
8
9
  if block_given?
9
10
  instance_exec *args, &consumer
10
11
  else
@@ -22,4 +23,4 @@ module Euston
22
23
  end
23
24
  end
24
25
  end
25
- end
26
+ end
@@ -0,0 +1,11 @@
1
+ module Euston
2
+ module CommandHandlerPrivateMethodNames
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def command_handler_method_name version
7
+ "__version__#{version}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -21,5 +21,9 @@ module Euston
21
21
  def self.from_hash hash
22
22
  self.new hash[:id], hash[:type].to_sym, hash[:version], ( hash[:log_completion] || false )
23
23
  end
24
+
25
+ def to_s
26
+ "#{id} #{type} (v#{version})"
27
+ end
24
28
  end
25
29
  end
@@ -0,0 +1,18 @@
1
+ module Euston
2
+ class Event
3
+ def initialize data = {}
4
+ if (data.keys & ['body', 'headers']).size == 2
5
+ @body, @headers = data.values_at 'body', 'headers'
6
+ else
7
+ @headers = {}
8
+ @body = data
9
+ end
10
+ end
11
+
12
+ attr_reader :headers, :body
13
+
14
+ def to_hash
15
+ { :headers => @headers, :body => @body }
16
+ end
17
+ end
18
+ end
@@ -1,13 +1,26 @@
1
1
  module Euston
2
2
  module EventHandler
3
3
  extend ActiveSupport::Concern
4
+ include Euston::EventHandlerPrivateMethodNames
4
5
 
5
6
  module ClassMethods
6
- def consumes type, version, &consumer
7
- define_method "__event_handler__#{type}__#{version}" do |*args|
7
+ def subscribes type, version = 1, opts = nil, &consumer
8
+ if self.include? Euston::AggregateRoot
9
+ opts = opts || { :id => :id }
10
+
11
+ self.class.send :define_method, id_from_event_method_name(type, version) do |event|
12
+ if opts[:id].respond_to? :call
13
+ opts[:id].call event
14
+ else
15
+ event[opts[:id]]
16
+ end
17
+ end
18
+ end
19
+
20
+ define_method event_handler_method_name(type, version) do |*args|
8
21
  instance_exec *args, &consumer
9
22
  end
10
23
  end
11
24
  end
12
25
  end
13
- end
26
+ end
@@ -0,0 +1,15 @@
1
+ module Euston
2
+ module EventHandlerPrivateMethodNames
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def id_from_event_method_name type, version
7
+ "__id_from_event_#{type}__v#{version}__"
8
+ end
9
+
10
+ def event_handler_method_name type, version
11
+ "__event_handler__#{type}__#{version}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,18 +3,8 @@ module Euston
3
3
  class << self
4
4
  attr_accessor :event_store
5
5
 
6
- def find type, id
7
- stream = event_store.open_stream :stream_id => id
8
- return nil if stream.committed_events.empty?
9
-
10
- type.hydrate stream
11
- end
12
-
13
- def save aggregate
14
- stream = event_store.open_stream :stream_id => aggregate.aggregate_id
15
- aggregate.uncommitted_events.each { |e| stream << e }
16
- stream.commit_changes Euston.uuid.generate
17
- end
6
+ # def find(type, id) mixed in by event store implementation
7
+ # def save(aggregate) mixed in by event store implementation
18
8
  end
19
9
  end
20
- end
10
+ end
@@ -1,3 +1,3 @@
1
1
  module Euston
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: euston
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.0.1
5
+ version: 1.1.0
6
6
  platform: java
7
7
  authors:
8
8
  - Lee Henson
@@ -10,73 +10,62 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-09-27 00:00:00.000000000 +01:00
13
+ date: 2011-10-03 00:00:00.000000000 +01:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
- name: activesupport
18
- version_requirements: &2164 !ruby/object:Gem::Requirement
17
+ name: activemodel
18
+ version_requirements: &2192 !ruby/object:Gem::Requirement
19
19
  requirements:
20
20
  - - ~>
21
21
  - !ruby/object:Gem::Version
22
22
  version: 3.0.9
23
23
  none: false
24
- requirement: *2164
25
- prerelease: false
26
- type: :runtime
27
- - !ruby/object:Gem::Dependency
28
- name: euston-eventstore
29
- version_requirements: &2182 !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ~>
32
- - !ruby/object:Gem::Version
33
- version: 1.0.0
34
- none: false
35
- requirement: *2182
24
+ requirement: *2192
36
25
  prerelease: false
37
26
  type: :runtime
38
27
  - !ruby/object:Gem::Dependency
39
- name: require_all
40
- version_requirements: &2198 !ruby/object:Gem::Requirement
28
+ name: activesupport
29
+ version_requirements: &2210 !ruby/object:Gem::Requirement
41
30
  requirements:
42
31
  - - ~>
43
32
  - !ruby/object:Gem::Version
44
- version: 1.2.0
33
+ version: 3.0.9
45
34
  none: false
46
- requirement: *2198
35
+ requirement: *2210
47
36
  prerelease: false
48
37
  type: :runtime
49
38
  - !ruby/object:Gem::Dependency
50
39
  name: fuubar
51
- version_requirements: &2214 !ruby/object:Gem::Requirement
40
+ version_requirements: &2226 !ruby/object:Gem::Requirement
52
41
  requirements:
53
42
  - - ~>
54
43
  - !ruby/object:Gem::Version
55
44
  version: 0.0.0
56
45
  none: false
57
- requirement: *2214
46
+ requirement: *2226
58
47
  prerelease: false
59
48
  type: :development
60
49
  - !ruby/object:Gem::Dependency
61
50
  name: rspec
62
- version_requirements: &2232 !ruby/object:Gem::Requirement
51
+ version_requirements: &2244 !ruby/object:Gem::Requirement
63
52
  requirements:
64
53
  - - ~>
65
54
  - !ruby/object:Gem::Version
66
55
  version: 2.6.0
67
56
  none: false
68
- requirement: *2232
57
+ requirement: *2244
69
58
  prerelease: false
70
59
  type: :development
71
60
  - !ruby/object:Gem::Dependency
72
61
  name: uuid
73
- version_requirements: &2248 !ruby/object:Gem::Requirement
62
+ version_requirements: &2260 !ruby/object:Gem::Requirement
74
63
  requirements:
75
64
  - - ~>
76
65
  - !ruby/object:Gem::Version
77
66
  version: 2.3.0
78
67
  none: false
79
- requirement: *2248
68
+ requirement: *2260
80
69
  prerelease: false
81
70
  type: :development
82
71
  description: ''
@@ -93,10 +82,16 @@ files:
93
82
  - lib/euston.rb
94
83
  - lib/euston/aggregate_command_map.rb
95
84
  - lib/euston/aggregate_root.rb
85
+ - lib/euston/aggregate_root_dsl_methods.rb
86
+ - lib/euston/aggregate_root_private_method_names.rb
87
+ - lib/euston/command.rb
96
88
  - lib/euston/command_bus.rb
97
89
  - lib/euston/command_handler.rb
90
+ - lib/euston/command_handler_private_method_names.rb
98
91
  - lib/euston/command_headers.rb
92
+ - lib/euston/event.rb
99
93
  - lib/euston/event_handler.rb
94
+ - lib/euston/event_handler_private_method_names.rb
100
95
  - lib/euston/event_headers.rb
101
96
  - lib/euston/null_logger.rb
102
97
  - lib/euston/repository.rb