command_post 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 +7 -0
- data/.gitignore +22 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +28 -0
- data/README.md +310 -0
- data/Rakefile +1 -0
- data/command_post.gemspec +21 -0
- data/lib/command_post/command/command.rb +375 -0
- data/lib/command_post/command_post.rb +5 -0
- data/lib/command_post/db/connection.rb +12 -0
- data/lib/command_post/db/postgresql.sql +45 -0
- data/lib/command_post/event_sourcing/aggregate.rb +82 -0
- data/lib/command_post/event_sourcing/aggregate_event.rb +107 -0
- data/lib/command_post/identity/identity.rb +75 -0
- data/lib/command_post/identity/sequence_generator.rb +44 -0
- data/lib/command_post/persistence/aggregate_pointer.rb +18 -0
- data/lib/command_post/persistence/auto_load.rb +70 -0
- data/lib/command_post/persistence/data_validation.rb +113 -0
- data/lib/command_post/persistence/persistence.rb +157 -0
- data/lib/command_post/persistence/schema_validation.rb +137 -0
- data/lib/command_post/util/hash_util.rb +23 -0
- data/lib/command_post/util/string_util.rb +18 -0
- data/lib/command_post/version.rb +3 -0
- data/spec/command_post/command/command_spec.rb +0 -0
- data/spec/command_post/identity/identity_lookup_value_aggregate_id_spec.rb +89 -0
- data/spec/command_post/identity/identity_lookup_value_checksum_spec.rb +108 -0
- data/spec/command_post/identity/identity_lookup_value_field_spec.rb +83 -0
- data/spec/command_post/persistence/data_validation_spec.rb +74 -0
- data/spec/command_post/persistence/nested_remote_spec.rb +0 -0
- data/spec/command_post/persistence/schema_validation_spec.rb +269 -0
- data/spec/command_post/require.rb +9 -0
- data/spec/spec_helper.rb +0 -0
- metadata +112 -0
@@ -0,0 +1,375 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../util/string_util')
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../identity/identity')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../event_sourcing/aggregate_event')
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
module CommandPost
|
9
|
+
|
10
|
+
class Command
|
11
|
+
|
12
|
+
def validate_persistent_fields object, errors
|
13
|
+
object.schema_fields.each do |field_name, field_info|
|
14
|
+
if field_info[:type].superclass == Persistence
|
15
|
+
if object.send(field_name.to_sym).valid? == false
|
16
|
+
errors += object.send(field_name.to_sym).data_errors
|
17
|
+
else
|
18
|
+
errors += validate_persistent_fields(object.send(field_name.to_sym), errors)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
errors
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def hashify_persistent_objects_before_save object
|
27
|
+
object.schema_fields.each do |field_name, field_info|
|
28
|
+
if field_info[:type].superclass == Persistence
|
29
|
+
hashify_persistent_objects_before_save (object.send field_name.to_sym)
|
30
|
+
hash = object.send(field_name.to_sym).to_h
|
31
|
+
method_name = "#{field_name}=".to_sym
|
32
|
+
object.send(method_name, hash)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
def self.auto_generate persistent_class
|
40
|
+
@@commands ||= Hash.new
|
41
|
+
return if @@commands.keys.include? persistent_class
|
42
|
+
@@commands[persistent_class] = []
|
43
|
+
self.create_field_correction_commands persistent_class
|
44
|
+
self.create_aggregate_creation_commands persistent_class
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
def self.create_aggregate_creation_commands persistent_class
|
51
|
+
|
52
|
+
|
53
|
+
modules = persistent_class.to_s.split('::').length - 1
|
54
|
+
parts = persistent_class.to_s.split('::')
|
55
|
+
if parts.length == 1
|
56
|
+
name = "Command#{persistent_class.to_s}Create#{persistent_class.to_s}"
|
57
|
+
elsif parts.length == 2
|
58
|
+
name = "Command#{parts[1]}Create#{parts[1]}"
|
59
|
+
end
|
60
|
+
klass = Object::const_set(name.intern, Class::new(Command) do end )
|
61
|
+
|
62
|
+
if modules == 1
|
63
|
+
klass.const_set parts[0], klass
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def_init = Array.new
|
68
|
+
def_init << "def self.execute params, user_id "
|
69
|
+
def_init << ' AggregateEvent.publish self.create_event( params, user_id)'
|
70
|
+
def_init << "end "
|
71
|
+
|
72
|
+
|
73
|
+
klass.instance_eval (def_init.join("\n"))
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
def_init = Array.new
|
79
|
+
|
80
|
+
def_init << 'def self.build_web_submission_form '
|
81
|
+
def_init << " "
|
82
|
+
|
83
|
+
def_init << " hash = Hash.new "
|
84
|
+
def_init << " hash['page_header'] = 'Create Person'"
|
85
|
+
def_init << " hash['command_class'] = '#{klass.to_s}'"
|
86
|
+
|
87
|
+
|
88
|
+
def_init << "\n"
|
89
|
+
def_init << " hash['current_state'] = {} "
|
90
|
+
|
91
|
+
def_init << " hash['new_state'] = { "
|
92
|
+
|
93
|
+
|
94
|
+
current_state = Array.new
|
95
|
+
persistent_class.schema.each do |form_field, form_field_info|
|
96
|
+
current_state << " '#{form_field}' => { 'label' => '#{StringUtil.to_label(form_field, persistent_class.upcase?(form_field))}', 'html' => \"<input type='text' id='#{form_field}' name='#{form_field}'/>\" } "
|
97
|
+
end
|
98
|
+
|
99
|
+
def_init << current_state.join(", \n")
|
100
|
+
|
101
|
+
def_init << ' }'
|
102
|
+
|
103
|
+
def_init << " hash['command_description'] = 'Submit command to create a new #{persistent_class.to_s}.'"
|
104
|
+
def_init << " hash['command_sub_description'] = ''"
|
105
|
+
|
106
|
+
def_init << ' hash '
|
107
|
+
|
108
|
+
|
109
|
+
def_init << 'end'
|
110
|
+
|
111
|
+
|
112
|
+
klass.instance_eval (def_init.join("\n"))
|
113
|
+
|
114
|
+
|
115
|
+
def_init = Array.new
|
116
|
+
def_init << 'def self.validate params'
|
117
|
+
def_init << ' []'
|
118
|
+
def_init << 'end '
|
119
|
+
|
120
|
+
klass.instance_eval (def_init.join("\n"))
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
fields = persistent_class.schema.keys.collect {|field| ":#{field}" }
|
125
|
+
|
126
|
+
array_of_fields = fields.join(',')
|
127
|
+
|
128
|
+
def_init = Array.new
|
129
|
+
def_init << 'def self.fields'
|
130
|
+
def_init << " [#{array_of_fields}]" # I LEFT OFF HERE - TRYING TO PUT A STRINGIFIED LIST OF SYMBOLS INTO A HASH
|
131
|
+
def_init << 'end '
|
132
|
+
|
133
|
+
klass.instance_eval (def_init.join("\n"))
|
134
|
+
|
135
|
+
|
136
|
+
def_init = Array.new
|
137
|
+
def_init << 'def self.aggregate_type'
|
138
|
+
def_init << " #{persistent_class.to_s}"
|
139
|
+
def_init << 'end '
|
140
|
+
|
141
|
+
klass.instance_eval (def_init.join("\n"))
|
142
|
+
|
143
|
+
|
144
|
+
def_init = Array.new
|
145
|
+
def_init << 'def self.event_description'
|
146
|
+
def_init << " Created Person"
|
147
|
+
def_init << 'end '
|
148
|
+
|
149
|
+
klass.instance_eval (def_init.join("\n"))
|
150
|
+
|
151
|
+
@@commands[persistent_class] << klass
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
def self.create_field_correction_commands persistent_class
|
167
|
+
|
168
|
+
persistent_class.schema.each do |field_name, field_info|
|
169
|
+
|
170
|
+
|
171
|
+
modules = persistent_class.to_s.split('::').length - 1
|
172
|
+
parts = persistent_class.to_s.split('::')
|
173
|
+
|
174
|
+
names = persistent_class.to_s.split('::')
|
175
|
+
if modules == 0
|
176
|
+
name = "Command#{persistent_class.to_s}Correct#{CommandPost::StringUtil.to_camel_case(field_name,persistent_class.upcase?(field_name))}"
|
177
|
+
elsif modules == 1
|
178
|
+
name = "Command#{names[1]}Correct#{CommandPost::StringUtil.to_camel_case(field_name,persistent_class.upcase?(field_name))}"
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
klass = Object::const_set(name, Class::new(Command) do end )
|
183
|
+
|
184
|
+
if modules == 1
|
185
|
+
klass.const_set parts[0], klass
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
|
190
|
+
def_init = Array.new
|
191
|
+
def_init << "def self.execute params, user_id "
|
192
|
+
def_init << ' AggregateEvent.publish self.create_event( params, user_id)'
|
193
|
+
def_init << "end "
|
194
|
+
|
195
|
+
|
196
|
+
klass.instance_eval (def_init.join("\n"))
|
197
|
+
|
198
|
+
|
199
|
+
def_init = Array.new
|
200
|
+
|
201
|
+
def_init << 'def self.get_web_submission_form aggregate_id'
|
202
|
+
def_init << " object = Aggregate.get_by_aggregate_id(#{persistent_class.to_s}, aggregate_id)"
|
203
|
+
def_init << " "
|
204
|
+
|
205
|
+
def_init << " hash = Hash.new "
|
206
|
+
|
207
|
+
|
208
|
+
def_init << " hash['command_class'] = '#{klass.to_s}'"
|
209
|
+
def_init << " hash['page_header'] = 'Correct #{StringUtil.to_label(field_name, persistent_class.upcase?(field_name))}'"
|
210
|
+
def_init << " hash['command_description'] = 'Command to correct #{field_name}.'"
|
211
|
+
def_init << " hash['command_sub_description'] = ''"
|
212
|
+
def_init << "\n"
|
213
|
+
def_init << " hash['current_state'] = {"
|
214
|
+
|
215
|
+
|
216
|
+
|
217
|
+
|
218
|
+
current_state = Array.new
|
219
|
+
persistent_class.schema.each do |form_field, form_field_info|
|
220
|
+
current_state << " '#{StringUtil.to_label(form_field, persistent_class.upcase?(form_field))}' => object.#{form_field}.to_s "
|
221
|
+
end
|
222
|
+
def_init << current_state.join(", ")
|
223
|
+
def_init << " } "
|
224
|
+
def_init << " hash['new_state'] = { } "
|
225
|
+
|
226
|
+
|
227
|
+
|
228
|
+
def_init << " hash"
|
229
|
+
def_init << 'end'
|
230
|
+
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
klass.instance_eval (def_init.join("\n"))
|
235
|
+
|
236
|
+
|
237
|
+
def_init = Array.new
|
238
|
+
def_init << 'def self.validate params'
|
239
|
+
def_init << ' []'
|
240
|
+
def_init << 'end '
|
241
|
+
|
242
|
+
klass.instance_eval (def_init.join("\n"))
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
|
248
|
+
def_init = Array.new
|
249
|
+
def_init << 'def self.fields'
|
250
|
+
def_init << " [:#{field_name}]"
|
251
|
+
def_init << 'end '
|
252
|
+
|
253
|
+
klass.instance_eval (def_init.join("\n"))
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
def_init = Array.new
|
259
|
+
def_init << 'def self.aggregate_type'
|
260
|
+
def_init << " #{persistent_class.to_s}"
|
261
|
+
def_init << 'end '
|
262
|
+
|
263
|
+
klass.instance_eval (def_init.join("\n"))
|
264
|
+
|
265
|
+
|
266
|
+
|
267
|
+
|
268
|
+
|
269
|
+
|
270
|
+
def_init = Array.new
|
271
|
+
def_init << 'def self.event_description'
|
272
|
+
def_init << " 'Corrected #{field_name}.'"
|
273
|
+
def_init << 'end '
|
274
|
+
|
275
|
+
klass.instance_eval (def_init.join("\n"))
|
276
|
+
|
277
|
+
|
278
|
+
|
279
|
+
|
280
|
+
|
281
|
+
@@commands[persistent_class] << klass
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
|
290
|
+
|
291
|
+
def self.publish_event command_class, params, user_id
|
292
|
+
|
293
|
+
event = AggregateEvent.new
|
294
|
+
event.aggregate_type = command_class.aggregate_type
|
295
|
+
event.event_description
|
296
|
+
#if command_class.to_s.start_with?("Command#{command_class.aggregate_type.to_s}Create")
|
297
|
+
event.aggregate_id = 1000
|
298
|
+
#end
|
299
|
+
event.transaction_id = SequenceGenerator.transaction_id
|
300
|
+
event.user_id = user_id
|
301
|
+
event.event_description = 'Person created'
|
302
|
+
|
303
|
+
object = command_class.aggregate_type.new
|
304
|
+
|
305
|
+
|
306
|
+
|
307
|
+
command_class.fields.each do |field|
|
308
|
+
info = command_class.aggregate_type.schema[field.to_s]
|
309
|
+
if info[:type] == Date
|
310
|
+
dt = Date._parse(params[field])
|
311
|
+
date = Date.new(dt[:year], dt[:mon], dt[:mday])
|
312
|
+
object.send("#{field.to_s}=".to_sym, date )
|
313
|
+
else
|
314
|
+
object.send("#{field.to_s}=".to_sym, params[field])
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
if params[:aggregate_id]
|
320
|
+
event.aggregate_id = params[:aggregate_id]
|
321
|
+
event.object = object
|
322
|
+
else
|
323
|
+
event.object = object
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
event.publish
|
328
|
+
|
329
|
+
|
330
|
+
end
|
331
|
+
|
332
|
+
|
333
|
+
|
334
|
+
def self.commands persistent_class
|
335
|
+
|
336
|
+
@@commands[persistent_class]
|
337
|
+
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
|
342
|
+
|
343
|
+
|
344
|
+
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
|
349
|
+
|
350
|
+
|
351
|
+
|
352
|
+
|
353
|
+
|
354
|
+
|
355
|
+
|
356
|
+
|
357
|
+
|
358
|
+
|
359
|
+
|
360
|
+
|
361
|
+
|
362
|
+
|
363
|
+
|
364
|
+
|
365
|
+
|
366
|
+
|
367
|
+
|
368
|
+
|
369
|
+
|
370
|
+
|
371
|
+
|
372
|
+
|
373
|
+
|
374
|
+
|
375
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
--drop table aggregate_events;
|
2
|
+
--drop table aggregates;
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
create table aggregate_events (
|
7
|
+
|
8
|
+
transaction_id bigint primary key,
|
9
|
+
aggregate_id bigint,
|
10
|
+
aggregate_type varchar(100),
|
11
|
+
event_description text,
|
12
|
+
content text,
|
13
|
+
call_stack text,
|
14
|
+
user_id varchar(100) not null,
|
15
|
+
transacted timestamp
|
16
|
+
|
17
|
+
) ;
|
18
|
+
|
19
|
+
create index aggregate_id_idx on aggregate_events(aggregate_id,aggregate_type);
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
create table aggregates (
|
26
|
+
|
27
|
+
aggregate_id bigint not null primary key,
|
28
|
+
aggregate_lookup_value varchar(100) not null,
|
29
|
+
aggregate_type varchar(100) not null,
|
30
|
+
content text not null
|
31
|
+
|
32
|
+
) ;
|
33
|
+
|
34
|
+
create index aggregate_lookup_idx on aggregates(aggregate_lookup_value);
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
CREATE SEQUENCE aggregate START 1;
|
42
|
+
|
43
|
+
CREATE SEQUENCE transaction START 1;
|
44
|
+
|
45
|
+
CREATE SEQUENCE misc START 1;
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'json'
|
4
|
+
require 'sequel'
|
5
|
+
|
6
|
+
require_relative '../db/connection'
|
7
|
+
require_relative '../identity/sequence_generator'
|
8
|
+
require_relative '../util/hash_util'
|
9
|
+
|
10
|
+
module CommandPost
|
11
|
+
|
12
|
+
|
13
|
+
$DB ||= Connection.db_cqrs
|
14
|
+
|
15
|
+
class Aggregate
|
16
|
+
|
17
|
+
@@prep_stmt_insert ||= $DB[:aggregates].prepare(:insert, :insert_aggregate, :aggregate_id => :$aggregate_id, :aggregate_type => :$aggregate_type, :content => :$content, :aggregate_lookup_value => :$aggregate_lookup_value )
|
18
|
+
@@prep_stmt_update ||= $DB[:aggregates].prepare(:update, :update_content_aggregate_lookup_value, :aggregate_id => :$aggregate_id, :content => :$content, :aggregate_lookup_value => :$aggregate_lookup_value )
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def self.replace object, aggregate_lookup_value
|
23
|
+
|
24
|
+
|
25
|
+
content = JSON.generate object
|
26
|
+
aggregate_id = object[:aggregate_info][:aggregate_id]
|
27
|
+
aggregate_type = object[:aggregate_info][:aggregate_type]
|
28
|
+
version = object[:aggregate_info][:version].to_i
|
29
|
+
|
30
|
+
|
31
|
+
if (version) == 1
|
32
|
+
@@prep_stmt_insert.call(:aggregate_id => aggregate_id, :aggregate_type => aggregate_type.to_s , :content => content, :aggregate_lookup_value => aggregate_lookup_value )
|
33
|
+
else
|
34
|
+
$DB["UPDATE aggregates set content = ?, aggregate_lookup_value = ? where aggregate_id = ?", content, aggregate_lookup_value, aggregate_id ].update
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
def self.get_by_aggregate_id aggregate_type ,aggregate_id
|
41
|
+
hash = Hash.new
|
42
|
+
$DB.fetch("SELECT * FROM aggregates WHERE aggregate_id = ?", aggregate_id ) do |row|
|
43
|
+
hash = JSON.parse(row[:content])
|
44
|
+
end
|
45
|
+
aggregate_type.load_from_hash( aggregate_type, HashUtil.symbolize_keys(hash))
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
def self.where(aggregate_type)
|
51
|
+
results = Array.new
|
52
|
+
$DB.fetch("SELECT * FROM aggregates WHERE aggregate_type = ?", aggregate_type.to_s) do |row|
|
53
|
+
hash = JSON.parse(row[:content])
|
54
|
+
results << aggregate_type.load_from_hash( aggregate_type, HashUtil.symbolize_keys(hash))
|
55
|
+
end
|
56
|
+
results
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
def self.exists? aggregate_type, aggregate_lookup_value
|
62
|
+
$DB.fetch("SELECT count(*) as cnt FROM aggregates WHERE aggregate_type = ? and aggregate_lookup_value = ? ", aggregate_type.to_s, aggregate_lookup_value) do |rec|
|
63
|
+
return rec[:cnt].to_i > 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
def self.get_aggregate_by_lookup_value aggregate_type, aggregate_lookup_value
|
70
|
+
hash = Hash.new
|
71
|
+
$DB.fetch("SELECT content FROM aggregates WHERE aggregate_type = ? and aggregate_lookup_value = ? ", aggregate_type.to_s, aggregate_lookup_value) do |rec|
|
72
|
+
hash = JSON.parse(rec[:content])
|
73
|
+
end
|
74
|
+
if hash.nil? || hash == {}
|
75
|
+
{}
|
76
|
+
else
|
77
|
+
aggregate_type.load_from_hash( aggregate_type, HashUtil.symbolize_keys(hash))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative './aggregate.rb'
|
2
|
+
|
3
|
+
|
4
|
+
module CommandPost
|
5
|
+
|
6
|
+
class AggregateEvent
|
7
|
+
|
8
|
+
attr_accessor :aggregate_type, :aggregate_id, :content, :transaction_id, :transacted, :event_description , :object, :changes, :user_id, :call_stack
|
9
|
+
|
10
|
+
@@prepared_statement ||= $DB[:aggregate_events].prepare(:insert, :insert_aggregate_event,
|
11
|
+
:aggregate_type => :$aggregate_type,
|
12
|
+
:aggregate_id => :$aggregate_id,
|
13
|
+
:content => :$content,
|
14
|
+
:transaction_id => :$transaction_id,
|
15
|
+
:transacted => :$transacted,
|
16
|
+
:event_description => :$event_description,
|
17
|
+
:user_id => :$user_id )
|
18
|
+
@@required_by_txn = [ "aggregate_type", "aggregate_id", "event_description", "content", "transaction_id", "transacted" ]
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@transaction_id = SequenceGenerator.transaction_id
|
22
|
+
@transacted = Time.now
|
23
|
+
@object = @changes = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def publish
|
27
|
+
if changes
|
28
|
+
save_event changes
|
29
|
+
elsif object
|
30
|
+
@aggregate_lookup_value = object.aggregate_lookup_value
|
31
|
+
save_event object.to_h
|
32
|
+
else
|
33
|
+
raise 'Event has no state to publish.'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.publish event
|
38
|
+
event.publish
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
def self.get_history_by_aggregate_id aggregate_id
|
46
|
+
hash = Hash.new
|
47
|
+
cnt = 0
|
48
|
+
results = Array.new
|
49
|
+
$DB.fetch("SELECT * FROM aggregate_events WHERE aggregate_id = ? order by transacted", aggregate_id ) do |row|
|
50
|
+
hash = JSON.parse(row[:content])
|
51
|
+
results << "==================================================================================="
|
52
|
+
results << "Version: #{cnt += 1} "
|
53
|
+
results << "Event Description: #{row[:event_description]} "
|
54
|
+
results << "Change Made By: #{row[:user_id]}"
|
55
|
+
results << "------ change data ------------"
|
56
|
+
results << hash.pretty_inspect
|
57
|
+
end
|
58
|
+
results
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
private
|
64
|
+
def save_event change
|
65
|
+
|
66
|
+
puts "@aggregate_type = #{@aggregate_type} "
|
67
|
+
puts "@aggregate_id = #{@aggregate_id} "
|
68
|
+
puts "@transaction_id = #{@transaction_id} "
|
69
|
+
puts "@transacted = #{@transacted} "
|
70
|
+
puts "@event_description = #{@event_description} "
|
71
|
+
puts "@user_id = #{@user_id} "
|
72
|
+
|
73
|
+
|
74
|
+
json = JSON.generate(change)
|
75
|
+
@@prepared_statement.call(
|
76
|
+
:aggregate_type => @aggregate_type.to_s,
|
77
|
+
:aggregate_id => @aggregate_id,
|
78
|
+
:content => json,
|
79
|
+
:transaction_id => @transaction_id,
|
80
|
+
:transacted => @transacted,
|
81
|
+
:event_description => @event_description,
|
82
|
+
:user_id => @user_id
|
83
|
+
)
|
84
|
+
Aggregate.replace get_current_object, @aggregate_lookup_value
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
def get_current_object
|
90
|
+
|
91
|
+
version = 0
|
92
|
+
accumulated_object = Hash.new
|
93
|
+
$DB.fetch("SELECT * FROM aggregate_events WHERE aggregate_id = ? order by transacted", @aggregate_id) do |row|
|
94
|
+
accumulated_object.merge!(HashUtil.symbolize_keys(JSON.parse(row[:content])))
|
95
|
+
aggregate_details = Hash.new
|
96
|
+
aggregate_details[:aggregate_type] = row[:aggregate_type]
|
97
|
+
aggregate_details[:aggregate_id] = row[:aggregate_id]
|
98
|
+
aggregate_details[:version] = "#{version += 1}"
|
99
|
+
aggregate_details[:most_recent_transaction] = row[:transaction_id]
|
100
|
+
accumulated_object[:aggregate_info] = aggregate_details
|
101
|
+
end
|
102
|
+
|
103
|
+
accumulated_object
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
module CommandPost
|
3
|
+
|
4
|
+
|
5
|
+
module Identity
|
6
|
+
|
7
|
+
|
8
|
+
def aggregate_lookup_value
|
9
|
+
field = (schema_fields[:lookup][:use])
|
10
|
+
(self.send field.to_sym).to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def aggregate_id
|
14
|
+
|
15
|
+
@data[:aggregate_info][:aggregate_id]
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def aggregate_pointer
|
20
|
+
|
21
|
+
AggregatePointer.new(aggregate_info)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def self.generate_checksum value
|
26
|
+
sha = Digest::SHA2.new
|
27
|
+
sha.update(value)
|
28
|
+
sha.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def checksum
|
33
|
+
data = Array.new
|
34
|
+
@data.keys.select {|x| x != :aggregate_info}.each {|x| data << @data[x] }
|
35
|
+
Identity.generate_checksum(data.join())
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def self.select (&block)
|
40
|
+
Aggregate.where(self).select
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def aggregate_info
|
45
|
+
if @data.keys.include? :aggregate_info
|
46
|
+
@data[:aggregate_info]
|
47
|
+
else
|
48
|
+
raise "A request was made for 'AggregateInfo at a time when it was not present in object state."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def self.find aggregate_id
|
54
|
+
|
55
|
+
Aggregate.get_by_aggregate_id self, aggregate_id
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|