euston 1.0.1-java → 1.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
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