event_sourced_record 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 +25 -0
- data/Appraisals +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +131 -0
- data/Rakefile +10 -0
- data/event_sourced_record.gemspec +37 -0
- data/lib/event_sourced_record/calculator.rb +101 -0
- data/lib/event_sourced_record/event/event_type_config.rb +30 -0
- data/lib/event_sourced_record/event.rb +94 -0
- data/lib/event_sourced_record/version.rb +3 -0
- data/lib/event_sourced_record.rb +7 -0
- data/lib/generators/event_sourced_record/USAGE +8 -0
- data/lib/generators/event_sourced_record/calculator_generator.rb +19 -0
- data/lib/generators/event_sourced_record/event_generator.rb +40 -0
- data/lib/generators/event_sourced_record/event_sourced_record_generator.rb +41 -0
- data/lib/generators/event_sourced_record/observer_generator.rb +34 -0
- data/lib/generators/event_sourced_record/projection_generator.rb +50 -0
- data/lib/generators/event_sourced_record/templates/calculator.rb +7 -0
- data/lib/generators/event_sourced_record/templates/event_model.rb +13 -0
- data/lib/generators/event_sourced_record/templates/observer.rb +8 -0
- data/lib/generators/event_sourced_record/templates/projection_model.rb +15 -0
- data/lib/generators/rspec/service_generator.rb +15 -0
- data/lib/generators/rspec/templates/service_spec.rb +6 -0
- data/lib/generators/test_unit/service_generator.rb +15 -0
- data/lib/generators/test_unit/templates/service_test.rb +9 -0
- data/test/event_sourced_record/calculator_test.rb +111 -0
- data/test/event_sourced_record/event_test.rb +112 -0
- data/test/generators/calculator_generator_test.rb +25 -0
- data/test/generators/event_generator_test.rb +35 -0
- data/test/generators/event_sourced_record_generator_test.rb +38 -0
- data/test/generators/observer_generator_test.rb +19 -0
- data/test/generators/projection_generator_test.rb +36 -0
- data/test/generators/templates/application.rb +12 -0
- data/test/test_helper.rb +82 -0
- metadata +217 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
<% module_namespacing do -%>
|
2
|
+
class <%= projection_class_name %> < <%= projection_parent_class_name.classify %>
|
3
|
+
<% attributes.select(&:reference?).each do |attribute| -%>
|
4
|
+
belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %>
|
5
|
+
<% end -%>
|
6
|
+
has_many :<%= file_name %>_events
|
7
|
+
<% if attributes.any?(&:password_digest?) -%>
|
8
|
+
has_secure_password
|
9
|
+
<% end -%>
|
10
|
+
|
11
|
+
validates :uuid, uniqueness: true
|
12
|
+
end
|
13
|
+
<% end -%>
|
14
|
+
|
15
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'generators/rspec'
|
2
|
+
|
3
|
+
module Rspec
|
4
|
+
module Generators # :nodoc:
|
5
|
+
class ServiceGenerator < Base # :nodoc:
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
check_class_collision suffix: "ServiceTest"
|
8
|
+
|
9
|
+
def create_service_files
|
10
|
+
template 'service_spec.rb', File.join('spec/services', class_path, "#{file_name}_spec.rb")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators/test_unit'
|
2
|
+
|
3
|
+
module TestUnit # :nodoc:
|
4
|
+
module Generators # :nodoc:
|
5
|
+
class ServiceGenerator < Base # :nodoc:
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
check_class_collision suffix: "ServiceTest"
|
8
|
+
|
9
|
+
def create_service_files
|
10
|
+
template 'service_test.rb', File.join('test/services', class_path, "#{file_name}_test.rb")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventSourcedRecord::CalculatorTest < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@event = SubscriptionEvent.creation.create!(
|
6
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 12345
|
7
|
+
)
|
8
|
+
assert @event.subscription_uuid
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_run_when_the_sourced_record_doesnt_exist
|
12
|
+
calculator = SubscriptionCalculator.new(@event.subscription_uuid)
|
13
|
+
subscription = calculator.run
|
14
|
+
assert_equal(1, subscription.bottles_per_shipment)
|
15
|
+
assert_equal(6, subscription.bottles_left)
|
16
|
+
assert_equal(@event.subscription_uuid, subscription.uuid)
|
17
|
+
assert !subscription.persisted?
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_run_when_the_sourced_record_already_exists
|
21
|
+
subscription = Subscription.create!(uuid: @event.subscription_uuid)
|
22
|
+
calculator = SubscriptionCalculator.new(@event.subscription_uuid)
|
23
|
+
subscription_prime = calculator.run
|
24
|
+
assert_equal(1, subscription_prime.bottles_per_shipment)
|
25
|
+
assert_equal(6, subscription_prime.bottles_left)
|
26
|
+
assert_equal(@event.subscription_uuid, subscription.uuid)
|
27
|
+
assert_equal(subscription.id, subscription_prime.id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_run_and_save_when_the_sourced_record_doesnt_exist
|
31
|
+
calculator = SubscriptionCalculator.new(@event.subscription_uuid)
|
32
|
+
subscription = calculator.run.tap(&:save!)
|
33
|
+
assert_equal(1, subscription.bottles_per_shipment)
|
34
|
+
assert_equal(6, subscription.bottles_left)
|
35
|
+
assert_equal(@event.subscription_uuid, subscription.uuid)
|
36
|
+
assert subscription.persisted?
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_run_and_save_when_the_sourced_record_already_exists
|
40
|
+
subscription = Subscription.create!(uuid: @event.subscription_uuid)
|
41
|
+
subscription_count = Subscription.count
|
42
|
+
calculator = SubscriptionCalculator.new(@event.subscription_uuid)
|
43
|
+
subscription_prime = calculator.run.tap(&:save!)
|
44
|
+
assert_equal(subscription_count, Subscription.count)
|
45
|
+
assert_equal(1, subscription_prime.bottles_per_shipment)
|
46
|
+
assert_equal(6, subscription_prime.bottles_left)
|
47
|
+
assert_equal(@event.subscription_uuid, subscription.uuid)
|
48
|
+
assert_equal(subscription.id, subscription_prime.id)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_run_with_a_different_event_class
|
52
|
+
event = SubscriptionEvent.creation.create!(
|
53
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 99999
|
54
|
+
)
|
55
|
+
subscription = SubscriptionCalculator.new(event.subscription_uuid).run.tap(&:save!)
|
56
|
+
assert_equal(6, subscription.bottles_left)
|
57
|
+
Shipment.create!(subscription_id: subscription.id, num_bottles: 3)
|
58
|
+
SubscriptionCalculator.new(event.subscription_uuid).run.tap(&:save!)
|
59
|
+
subscription.reload
|
60
|
+
assert_equal(3, subscription.bottles_left)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_initialize_by_projection_or_id_or_event_or_associated_event
|
64
|
+
event = SubscriptionEvent.creation.create!(
|
65
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 99999
|
66
|
+
)
|
67
|
+
subscription = SubscriptionCalculator.new(event).run.tap(&:save!)
|
68
|
+
shipment = Shipment.create!(
|
69
|
+
subscription_id: subscription.id, num_bottles: 3
|
70
|
+
)
|
71
|
+
lookups = [subscription, subscription.id, event, shipment]
|
72
|
+
lookups.each do |lookup|
|
73
|
+
calculator = SubscriptionCalculator.new(lookup)
|
74
|
+
assert_equal(
|
75
|
+
subscription, calculator.run,
|
76
|
+
"Can't find subscription with lookup #{lookup.inspect}"
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_run_modifies_a_different_instance
|
82
|
+
event = SubscriptionEvent.creation.create!(
|
83
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 99999
|
84
|
+
)
|
85
|
+
subscription = SubscriptionCalculator.new(event).run.tap(&:save!)
|
86
|
+
subscription.bottles_left = 999
|
87
|
+
SubscriptionCalculator.new(subscription).run
|
88
|
+
assert_equal(999, subscription.bottles_left)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_last_event_time
|
92
|
+
event = SubscriptionEvent.creation.create!(
|
93
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 99999,
|
94
|
+
created_at: Time.utc(2015,1,1)
|
95
|
+
)
|
96
|
+
subscription = SubscriptionCalculator.new(event).run.tap(&:save!)
|
97
|
+
Shipment.create!(
|
98
|
+
subscription_id: subscription.id, num_bottles: 3,
|
99
|
+
created_at: Time.utc(2015,1,4,12)
|
100
|
+
)
|
101
|
+
calculator = SubscriptionCalculator.new(subscription)
|
102
|
+
result1 = calculator.run(last_event_time: Date.new(2015,1,1))
|
103
|
+
assert_equal(6, result1.bottles_left)
|
104
|
+
result2 = calculator.run(last_event_time: Time.utc(2015,1,4))
|
105
|
+
assert_equal(6, result2.bottles_left)
|
106
|
+
result3 = calculator.run(last_event_time: Time.utc(2015,1,5))
|
107
|
+
assert_equal(3, result3.bottles_left)
|
108
|
+
result4 = calculator.run(last_event_time: Date.new(2015,1,5))
|
109
|
+
assert_equal(3, result4.bottles_left)
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventSourcedRecord::EventTest < MiniTest::Unit::TestCase
|
4
|
+
def test_creation_auto_generates_uuid
|
5
|
+
event = SubscriptionEvent.creation.new
|
6
|
+
assert event.subscription_uuid
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_creation_data_assignment
|
10
|
+
event = SubscriptionEvent.creation.new(
|
11
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 999
|
12
|
+
)
|
13
|
+
assert_equal(999, event.data['user_id'])
|
14
|
+
assert_equal(999, event.user_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_creation_accessors_on_reload
|
18
|
+
event = SubscriptionEvent.creation.new(
|
19
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 999
|
20
|
+
)
|
21
|
+
event.save!
|
22
|
+
event_prime = SubscriptionEvent.find(event.id)
|
23
|
+
assert_equal(999, event_prime.user_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_creation_reserved_attributes
|
27
|
+
event = SubscriptionEvent.creation.new(
|
28
|
+
subscription_uuid: 'asdf'
|
29
|
+
)
|
30
|
+
assert_equal('asdf', event.subscription_uuid)
|
31
|
+
assert_nil event.data['subscription_uuid']
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_creation_dont_accept_random_attribute
|
35
|
+
assert_raises(ActiveRecord::UnknownAttributeError) do
|
36
|
+
SubscriptionEvent.creation.new(foo: 'bar')
|
37
|
+
end
|
38
|
+
assert_raises(ActiveRecord::UnknownAttributeError) do
|
39
|
+
SubscriptionEvent.change_settings.new(user_id: 123)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_creation_validation_errors
|
44
|
+
event = SubscriptionEvent.creation.new
|
45
|
+
assert !event.valid?
|
46
|
+
assert event.errors[:bottles_per_shipment].include?("can't be blank")
|
47
|
+
assert event.errors[:bottles_per_shipment].include?('is not a number')
|
48
|
+
assert event.errors[:bottles_purchased].include?("can't be blank")
|
49
|
+
assert event.errors[:bottles_purchased].include?('is not a number')
|
50
|
+
assert event.errors[:user_id].include?("can't be blank")
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_creation_valid
|
54
|
+
event = SubscriptionEvent.creation.new(
|
55
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 999
|
56
|
+
)
|
57
|
+
assert_equal(6, event.bottles_purchased)
|
58
|
+
assert event.valid?
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_creation_create
|
62
|
+
events_before = SubscriptionEvent.count
|
63
|
+
SubscriptionEvent.creation.create!(
|
64
|
+
bottles_per_shipment: 1, bottles_purchased: 6, user_id: 999
|
65
|
+
)
|
66
|
+
assert_equal(events_before + 1, SubscriptionEvent.count)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_creation_settable_attributes
|
70
|
+
event = SubscriptionEvent.creation.new
|
71
|
+
event.bottles_per_shipment = 1
|
72
|
+
assert_equal(1, event.bottles_per_shipment)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_change_settings_validation_errors
|
76
|
+
event = SubscriptionEvent.change_settings.new
|
77
|
+
assert !event.valid?
|
78
|
+
assert event.errors[:bottles_per_shipment].include?('is not a number')
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_change_settings_valid
|
82
|
+
event = SubscriptionEvent.change_settings.new(:bottles_per_shipment => 2)
|
83
|
+
assert event.valid?
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_new_without_scope
|
87
|
+
event = SubscriptionEvent.new(
|
88
|
+
event_type: 'creation', bottles_per_shipment: 1, bottles_purchased: 6,
|
89
|
+
user_id: 999
|
90
|
+
)
|
91
|
+
assert event.valid?
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_event_type_required
|
95
|
+
event = SubscriptionEvent.new
|
96
|
+
assert !event.valid?
|
97
|
+
assert event.errors[:event_type].include?("can't be blank")
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_event_type_inclusion
|
101
|
+
event = SubscriptionEvent.new(event_type: 'wrong')
|
102
|
+
assert !event.valid?
|
103
|
+
assert event.errors[:event_type].include?("is not a valid event type")
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_event_type_unsettable
|
107
|
+
event = SubscriptionEvent.creation.new
|
108
|
+
assert_raises(EventSourcedRecord::Event::EventTypeImmutableError) do
|
109
|
+
event.event_type = 'change_settings'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventSourcedRecord::CalculatorGeneratorTest < Rails::Generators::TestCase
|
4
|
+
destination 'tmp/calculator_generator_test'
|
5
|
+
setup :prepare_destination
|
6
|
+
tests EventSourcedRecord::CalculatorGenerator
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@generate_calls = Hash.new { |h,k| h[k] = [] }
|
10
|
+
EventSourcedRecord::CalculatorGenerator.any_instance.stubs(:generate).with { |name, arg_string|
|
11
|
+
@generate_calls[name] << arg_string
|
12
|
+
}
|
13
|
+
run_generator %w(subscription_calculator)
|
14
|
+
end
|
15
|
+
|
16
|
+
test "creates a calculator" do
|
17
|
+
assert_file("app/services/subscription_calculator.rb") do |contents|
|
18
|
+
assert_match(
|
19
|
+
/class SubscriptionCalculator < EventSourcedRecord::Calculator/,
|
20
|
+
contents
|
21
|
+
)
|
22
|
+
assert_match(/def advance_creation\(event\)/, contents)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventSourcedRecord::EventGeneratorTest < Rails::Generators::TestCase
|
4
|
+
destination 'tmp/event_generator_test'
|
5
|
+
setup :prepare_destination
|
6
|
+
tests EventSourcedRecord::EventGenerator
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@generate_calls = Hash.new { |h,k| h[k] = [] }
|
10
|
+
EventSourcedRecord::EventGenerator.any_instance.stubs(:generate).with { |name, arg_string|
|
11
|
+
@generate_calls[name] << arg_string
|
12
|
+
}
|
13
|
+
run_generator %w(
|
14
|
+
subscription_event
|
15
|
+
subscription_uuid:string:index
|
16
|
+
event_type:string
|
17
|
+
data:text
|
18
|
+
created_at:datetime
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
test "creates a migration for the event class" do
|
23
|
+
assert @generate_calls['migration'].include?(
|
24
|
+
"create_subscription_events subscription_uuid:string:index event_type:string data:text created_at:datetime"
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
test "creates a model for the event class" do
|
29
|
+
assert_file("app/models/subscription_event.rb") do |contents|
|
30
|
+
assert_match(/class SubscriptionEvent < ActiveRecord::Base/, contents)
|
31
|
+
assert_match(/include EventSourcedRecord::Event/, contents)
|
32
|
+
assert_match(/event_type :creation do/, contents)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventSourcedRecord::EventSourcedRecordGeneratorTest < Rails::Generators::TestCase
|
4
|
+
destination 'tmp/event_sourced_record_generator_test'
|
5
|
+
setup :prepare_destination
|
6
|
+
tests EventSourcedRecord::EventSourcedRecordGenerator
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@generate_calls = Hash.new { |h,k| h[k] = [] }
|
10
|
+
EventSourcedRecord::EventSourcedRecordGenerator.any_instance.stubs(:generate).with { |name, arg_string|
|
11
|
+
@generate_calls[name] << arg_string
|
12
|
+
}
|
13
|
+
run_generator %w(
|
14
|
+
ShampooSubscription
|
15
|
+
user_id:integer
|
16
|
+
bottles_per_shipment:integer
|
17
|
+
bottles_left:integer
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "calls the event generator" do
|
22
|
+
assert @generate_calls['event_sourced_record:event'].include?(
|
23
|
+
"shampoo_subscription_event shampoo_subscription_uuid:string:index event_type:string data:text created_at:datetime"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
test "calls the projection generator" do
|
28
|
+
assert @generate_calls['event_sourced_record:projection'].include?(
|
29
|
+
"shampoo_subscription user_id:integer bottles_per_shipment:integer bottles_left:integer"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
test "calls the calculator generator" do
|
34
|
+
assert @generate_calls['event_sourced_record:calculator'].include?(
|
35
|
+
"shampoo_subscription_calculator"
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventSourcedRecord::ObserverGeneratorTest < Rails::Generators::TestCase
|
4
|
+
Destination = 'tmp/observer_generator_test'
|
5
|
+
destination Destination
|
6
|
+
setup :prepare_destination
|
7
|
+
tests EventSourcedRecord::ObserverGenerator
|
8
|
+
|
9
|
+
setup do
|
10
|
+
config_path = File.join(Destination, 'config')
|
11
|
+
FileUtils.mkdir(config_path)
|
12
|
+
FileUtils.cp("test/generators/templates/application.rb", config_path)
|
13
|
+
run_generator %w(subscription_event_observer)
|
14
|
+
end
|
15
|
+
|
16
|
+
test "creates a file for the observer" do
|
17
|
+
assert_file("app/observers/subscription_event_observer.rb")
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EventSourcedRecord::ProjectionGeneratorTest < Rails::Generators::TestCase
|
4
|
+
destination 'tmp/projection_generator_test'
|
5
|
+
setup :prepare_destination
|
6
|
+
tests EventSourcedRecord::ProjectionGenerator
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@generate_calls = Hash.new { |h,k| h[k] = [] }
|
10
|
+
EventSourcedRecord::ProjectionGenerator.any_instance.stubs(:generate).with { |name, arg_string|
|
11
|
+
@generate_calls[name] << arg_string
|
12
|
+
}
|
13
|
+
run_generator %w(
|
14
|
+
subscription
|
15
|
+
user_id:integer
|
16
|
+
bottles_per_shipment:integer
|
17
|
+
bottles_left:integer
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
test "creates a migration for the projection class" do
|
22
|
+
assert(
|
23
|
+
@generate_calls['migration'].include?(
|
24
|
+
"create_subscriptions user_id:integer bottles_per_shipment:integer bottles_left:integer uuid:string:uniq"
|
25
|
+
),
|
26
|
+
@generate_calls.inspect
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
test "creates a model for the projection class" do
|
31
|
+
assert_file("app/models/subscription.rb") do |contents|
|
32
|
+
assert_match(/class Subscription < ActiveRecord::Base/, contents)
|
33
|
+
assert_match(/validates :uuid, uniqueness: true/, contents)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path('../boot', __FILE__)
|
2
|
+
|
3
|
+
require 'rails/all'
|
4
|
+
|
5
|
+
# Require the gems listed in Gemfile, including any gems
|
6
|
+
# you've limited to :test, :development, or :production.
|
7
|
+
Bundler.require(*Rails.groups)
|
8
|
+
|
9
|
+
module ShampooExpress
|
10
|
+
class Application < Rails::Application
|
11
|
+
end
|
12
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'rails/test_help'
|
3
|
+
require 'rails/generators/test_case'
|
4
|
+
require 'pry'
|
5
|
+
require 'mocha/test_unit'
|
6
|
+
$: << 'lib'
|
7
|
+
require 'event_sourced_record'
|
8
|
+
require 'generators/event_sourced_record/event_sourced_record_generator'
|
9
|
+
require 'generators/event_sourced_record/calculator_generator'
|
10
|
+
require 'generators/event_sourced_record/event_generator'
|
11
|
+
require 'generators/event_sourced_record/observer_generator'
|
12
|
+
require 'generators/event_sourced_record/projection_generator'
|
13
|
+
require 'active_record'
|
14
|
+
|
15
|
+
ActiveRecord::Base.establish_connection(
|
16
|
+
:adapter => 'sqlite3', :database => 'tmp/test.sqlite3'
|
17
|
+
)
|
18
|
+
|
19
|
+
silence_stream(STDOUT) do
|
20
|
+
ActiveRecord::Schema.define do
|
21
|
+
create_table "shipments", force: true do |t|
|
22
|
+
t.integer "subscription_id"
|
23
|
+
t.integer "num_bottles"
|
24
|
+
t.datetime "created_at"
|
25
|
+
t.datetime "updated_at"
|
26
|
+
end
|
27
|
+
|
28
|
+
create_table 'subscription_events', :force => true do |t|
|
29
|
+
t.string "subscription_uuid"
|
30
|
+
t.string "event_type"
|
31
|
+
t.text "data"
|
32
|
+
t.datetime "created_at"
|
33
|
+
end
|
34
|
+
|
35
|
+
create_table "subscriptions", force: true do |t|
|
36
|
+
t.integer "user_id"
|
37
|
+
t.integer "bottles_per_shipment"
|
38
|
+
t.integer "bottles_left"
|
39
|
+
t.string "uuid"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Shipment < ActiveRecord::Base
|
45
|
+
end
|
46
|
+
|
47
|
+
class SubscriptionEvent < ActiveRecord::Base
|
48
|
+
include EventSourcedRecord::Event
|
49
|
+
|
50
|
+
event_type :creation do
|
51
|
+
attributes :bottles_per_shipment, :bottles_purchased, :user_id
|
52
|
+
|
53
|
+
validates :bottles_per_shipment, presence: true, numericality: true
|
54
|
+
validates :bottles_purchased, presence: true, numericality: true
|
55
|
+
validates :user_id, presence: true
|
56
|
+
end
|
57
|
+
|
58
|
+
event_type :change_settings do
|
59
|
+
attributes :bottles_per_shipment
|
60
|
+
|
61
|
+
validates :bottles_per_shipment, numericality: true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Subscription < ActiveRecord::Base
|
66
|
+
has_many :subscription_events
|
67
|
+
|
68
|
+
validates :uuid, uniqueness: true
|
69
|
+
end
|
70
|
+
|
71
|
+
class SubscriptionCalculator < EventSourcedRecord::Calculator
|
72
|
+
events :subscription_events, :shipments
|
73
|
+
|
74
|
+
def advance_creation(event)
|
75
|
+
@subscription.bottles_per_shipment = event.bottles_per_shipment
|
76
|
+
@subscription.bottles_left = event.bottles_purchased
|
77
|
+
end
|
78
|
+
|
79
|
+
def advance_shipment(shipment)
|
80
|
+
@subscription.bottles_left -= shipment.num_bottles
|
81
|
+
end
|
82
|
+
end
|