sequent 4.3.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/sequent +1 -1
- data/db/sequent_schema.rb +3 -3
- data/lib/sequent/configuration.rb +19 -1
- data/lib/sequent/core/aggregate_root.rb +2 -6
- data/lib/sequent/core/aggregate_roots.rb +2 -6
- data/lib/sequent/core/command.rb +8 -12
- data/lib/sequent/core/command_record.rb +1 -1
- data/lib/sequent/core/command_service.rb +13 -2
- data/lib/sequent/core/core.rb +1 -0
- data/lib/sequent/core/event.rb +2 -2
- data/lib/sequent/core/event_store.rb +15 -2
- data/lib/sequent/core/ext/ext.rb +17 -0
- data/lib/sequent/core/helpers/attr_matchers/argument_serializer.rb +35 -0
- data/lib/sequent/core/helpers/attr_matchers/attr_matchers.rb +10 -0
- data/lib/sequent/core/helpers/attr_matchers/dsl.rb +23 -0
- data/lib/sequent/core/helpers/attr_matchers/equals.rb +24 -0
- data/lib/sequent/core/helpers/attr_matchers/greater_than.rb +24 -0
- data/lib/sequent/core/helpers/attr_matchers/greater_than_equals.rb +24 -0
- data/lib/sequent/core/helpers/attr_matchers/less_than.rb +24 -0
- data/lib/sequent/core/helpers/attr_matchers/less_than_equals.rb +24 -0
- data/lib/sequent/core/helpers/attr_matchers/not_equals.rb +24 -0
- data/lib/sequent/core/helpers/attribute_support.rb +35 -9
- data/lib/sequent/core/helpers/autoset_attributes.rb +5 -5
- data/lib/sequent/core/helpers/default_validators.rb +3 -0
- data/lib/sequent/core/helpers/message_dispatcher.rb +20 -0
- data/lib/sequent/core/helpers/message_handler.rb +62 -8
- data/lib/sequent/core/helpers/message_handler_option_registry.rb +59 -0
- data/lib/sequent/core/helpers/message_matchers/any.rb +34 -0
- data/lib/sequent/core/helpers/message_matchers/argument_coercer.rb +24 -0
- data/lib/sequent/core/helpers/message_matchers/argument_serializer.rb +20 -0
- data/lib/sequent/core/helpers/message_matchers/dsl.rb +23 -0
- data/lib/sequent/core/helpers/message_matchers/except_opt.rb +24 -0
- data/lib/sequent/core/helpers/message_matchers/has_attrs.rb +54 -0
- data/lib/sequent/core/helpers/message_matchers/instance_of.rb +24 -0
- data/lib/sequent/core/helpers/message_matchers/is_a.rb +34 -0
- data/lib/sequent/core/helpers/message_matchers/message_matchers.rb +10 -0
- data/lib/sequent/core/helpers/message_router.rb +55 -0
- data/lib/sequent/core/helpers/param_support.rb +2 -0
- data/lib/sequent/core/helpers/string_to_value_parsers.rb +5 -0
- data/lib/sequent/core/helpers/time_validator.rb +23 -0
- data/lib/sequent/core/helpers/value_validators.rb +11 -0
- data/lib/sequent/core/middleware/chain.rb +37 -0
- data/lib/sequent/core/middleware/middleware.rb +3 -0
- data/lib/sequent/core/projector.rb +3 -11
- data/lib/sequent/core/workflow.rb +5 -13
- data/lib/sequent/generator/template_project/Rakefile +2 -2
- data/lib/sequent/generator/template_project/db/sequent_schema.rb +3 -3
- data/lib/sequent/generator/template_project/spec/spec_helper.rb +1 -1
- data/lib/sequent/migrations/migrations.rb +1 -0
- data/lib/sequent/migrations/projectors.rb +2 -2
- data/lib/sequent/migrations/sequent_schema.rb +40 -0
- data/lib/sequent/migrations/view_schema.rb +39 -3
- data/lib/sequent/rake/migration_tasks.rb +36 -33
- data/lib/sequent/support/database.rb +29 -13
- data/lib/sequent/test/command_handler_helpers.rb +3 -3
- data/lib/sequent/test/database_helpers.rb +20 -0
- data/lib/sequent/test/time_comparison.rb +2 -5
- data/lib/sequent/test/{event_handler_helpers.rb → workflow_helpers.rb} +24 -10
- data/lib/sequent/test.rb +2 -1
- data/lib/sequent/util/dry_run.rb +1 -1
- data/lib/version.rb +1 -1
- metadata +40 -15
- data/lib/sequent/rake/tasks.rb +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24d2ee32c038b179c4386d2df97025af873487c5fe333952e5fedbe5d3ed47d8
|
4
|
+
data.tar.gz: 8d0464ac057a6e458ee7f131caba027fbddbd751af5b9d4314907ed248d0734d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b064934ae367d2e57baaa081c0b3f392bb932a1bb12fed00db337a6365f32c0949e5c6c4fe3eae6fa4a1374d931db517cf33efa911fdc265883bb122e8ce6ab
|
7
|
+
data.tar.gz: b5570de3a8b9deb24c1006486c37166f6e17d06d23a30d38a9de9b1e0755f1cbf4e57e1a2c1890ff9a68908f2077e90d7f8d1e79240ae3c5943de23f185be6f1
|
data/bin/sequent
CHANGED
@@ -31,7 +31,7 @@ def new_project(args)
|
|
31
31
|
bundle exec rake sequent:migrate:offline
|
32
32
|
|
33
33
|
Run the example specs:
|
34
|
-
|
34
|
+
SEQUENT_ENV=test bundle exec rake sequent:db:create
|
35
35
|
bundle exec rspec spec
|
36
36
|
|
37
37
|
To generate new aggregates use:
|
data/db/sequent_schema.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
ActiveRecord::Schema.define do
|
2
2
|
|
3
3
|
create_table "event_records", :force => true do |t|
|
4
|
-
t.
|
4
|
+
t.uuid "aggregate_id", :null => false
|
5
5
|
t.integer "sequence_number", :null => false
|
6
6
|
t.datetime "created_at", :null => false
|
7
7
|
t.string "event_type", :null => false
|
@@ -27,7 +27,7 @@ CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DES
|
|
27
27
|
|
28
28
|
create_table "command_records", :force => true do |t|
|
29
29
|
t.string "user_id"
|
30
|
-
t.
|
30
|
+
t.uuid "aggregate_id"
|
31
31
|
t.string "command_type", :null => false
|
32
32
|
t.string "event_aggregate_id"
|
33
33
|
t.integer "event_sequence_number"
|
@@ -40,7 +40,7 @@ CREATE INDEX snapshot_events ON event_records (aggregate_id, sequence_number DES
|
|
40
40
|
create_table "stream_records", :force => true do |t|
|
41
41
|
t.datetime "created_at", :null => false
|
42
42
|
t.string "aggregate_type", :null => false
|
43
|
-
t.
|
43
|
+
t.uuid "aggregate_id", :null => false
|
44
44
|
t.integer "snapshot_threshold"
|
45
45
|
end
|
46
46
|
|
@@ -32,6 +32,8 @@ module Sequent
|
|
32
32
|
|
33
33
|
DEFAULT_ERROR_LOCALE_RESOLVER = -> { I18n.locale || :en }
|
34
34
|
|
35
|
+
DEFAULT_TIME_PRECISION = ActiveSupport::JSON::Encoding.time_precision
|
36
|
+
|
35
37
|
attr_accessor :aggregate_repository,
|
36
38
|
:event_store,
|
37
39
|
:command_service,
|
@@ -43,6 +45,7 @@ module Sequent
|
|
43
45
|
:event_record_hooks_class,
|
44
46
|
:command_handlers,
|
45
47
|
:command_filters,
|
48
|
+
:command_middleware,
|
46
49
|
:event_handlers,
|
47
50
|
:uuid_generator,
|
48
51
|
:disable_event_handlers,
|
@@ -56,7 +59,11 @@ module Sequent
|
|
56
59
|
:database_config_directory,
|
57
60
|
:database_schema_directory,
|
58
61
|
:event_store_schema_name,
|
59
|
-
:strict_check_attributes_on_apply_events
|
62
|
+
:strict_check_attributes_on_apply_events,
|
63
|
+
:enable_multiple_database_support,
|
64
|
+
:primary_database_role,
|
65
|
+
:primary_database_key,
|
66
|
+
:time_precision
|
60
67
|
|
61
68
|
attr_reader :migrations_class_name,
|
62
69
|
:versions_table_name,
|
@@ -78,6 +85,7 @@ module Sequent
|
|
78
85
|
self.command_handlers = []
|
79
86
|
self.command_filters = []
|
80
87
|
self.event_handlers = []
|
88
|
+
self.command_middleware = Sequent::Core::Middleware::Chain.new
|
81
89
|
|
82
90
|
self.aggregate_repository = Sequent::Core::AggregateRepository.new
|
83
91
|
self.event_store = Sequent::Core::EventStore.new
|
@@ -107,6 +115,16 @@ module Sequent
|
|
107
115
|
|
108
116
|
self.logger = Logger.new(STDOUT).tap { |l| l.level = Logger::INFO }
|
109
117
|
self.error_locale_resolver = DEFAULT_ERROR_LOCALE_RESOLVER
|
118
|
+
|
119
|
+
self.enable_multiple_database_support = false
|
120
|
+
self.primary_database_role = :writing
|
121
|
+
self.primary_database_key = :primary
|
122
|
+
|
123
|
+
self.time_precision = DEFAULT_TIME_PRECISION
|
124
|
+
end
|
125
|
+
|
126
|
+
def can_use_multiple_databases?
|
127
|
+
enable_multiple_database_support && ActiveRecord.version > Gem::Version.new('6.1.0')
|
110
128
|
end
|
111
129
|
|
112
130
|
def replayed_ids_table_name=(table_name)
|
@@ -39,14 +39,10 @@ module Sequent
|
|
39
39
|
include Helpers::MessageHandler
|
40
40
|
include Helpers::AutosetAttributes
|
41
41
|
include SnapshotConfiguration
|
42
|
+
extend ActiveSupport::DescendantsTracker
|
42
43
|
|
43
44
|
attr_reader :id, :uncommitted_events, :sequence_number, :event_stream
|
44
45
|
|
45
|
-
def self.inherited(subclass)
|
46
|
-
super
|
47
|
-
AggregateRoots << subclass
|
48
|
-
end
|
49
|
-
|
50
46
|
def self.load_from_history(stream, events)
|
51
47
|
first, *rest = events
|
52
48
|
if first.is_a? SnapshotEvent
|
@@ -127,7 +123,7 @@ module Sequent
|
|
127
123
|
# Provide subclasses nice DSL to 'apply' events via:
|
128
124
|
#
|
129
125
|
# def send_invoice
|
130
|
-
# apply InvoiceSentEvent, send_date:
|
126
|
+
# apply InvoiceSentEvent, send_date: Time.now
|
131
127
|
# end
|
132
128
|
#
|
133
129
|
def apply(event, params = {})
|
@@ -3,21 +3,17 @@
|
|
3
3
|
module Sequent
|
4
4
|
module Core
|
5
5
|
#
|
6
|
-
# Utility class containing all subclasses of AggregateRoot
|
6
|
+
# Utility class containing all subclasses of AggregateRoot.
|
7
7
|
#
|
8
8
|
class AggregateRoots
|
9
9
|
class << self
|
10
10
|
def aggregate_roots
|
11
|
-
|
11
|
+
Sequent::AggregateRoot.descendants
|
12
12
|
end
|
13
13
|
|
14
14
|
def all
|
15
15
|
aggregate_roots
|
16
16
|
end
|
17
|
-
|
18
|
-
def <<(aggregate_root)
|
19
|
-
aggregate_roots << aggregate_root
|
20
|
-
end
|
21
17
|
end
|
22
18
|
end
|
23
19
|
end
|
data/lib/sequent/core/command.rb
CHANGED
@@ -28,17 +28,17 @@ module Sequent
|
|
28
28
|
include ActiveModel::Validations
|
29
29
|
include ActiveModel::Validations::Callbacks
|
30
30
|
include Sequent::Core::Helpers::TypeConversionSupport
|
31
|
+
extend ActiveSupport::DescendantsTracker
|
31
32
|
|
32
|
-
attrs created_at:
|
33
|
+
attrs created_at: Time
|
34
|
+
|
35
|
+
define_model_callbacks :initialize, only: :after
|
33
36
|
|
34
37
|
def initialize(args = {})
|
35
38
|
update_all_attributes args
|
36
|
-
@created_at =
|
37
|
-
end
|
39
|
+
@created_at = Time.now
|
38
40
|
|
39
|
-
|
40
|
-
super
|
41
|
-
Commands << subclass
|
41
|
+
_run_initialize_callbacks
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -56,22 +56,18 @@ module Sequent
|
|
56
56
|
end
|
57
57
|
|
58
58
|
#
|
59
|
-
# Utility class containing all subclasses of BaseCommand
|
59
|
+
# Utility class containing all subclasses of BaseCommand.
|
60
60
|
#
|
61
61
|
class Commands
|
62
62
|
class << self
|
63
63
|
def commands
|
64
|
-
|
64
|
+
Sequent::Core::BaseCommand.descendants
|
65
65
|
end
|
66
66
|
|
67
67
|
def all
|
68
68
|
commands
|
69
69
|
end
|
70
70
|
|
71
|
-
def <<(command)
|
72
|
-
commands << command
|
73
|
-
end
|
74
|
-
|
75
71
|
def find(command_name)
|
76
72
|
commands.find { |c| c.name == command_name }
|
77
73
|
end
|
@@ -8,7 +8,7 @@ module Sequent
|
|
8
8
|
module SerializesCommand
|
9
9
|
def command
|
10
10
|
args = Sequent::Core::Oj.strict_load(command_json)
|
11
|
-
Class.const_get(command_type
|
11
|
+
Class.const_get(command_type).deserialize_from_json(args)
|
12
12
|
end
|
13
13
|
|
14
14
|
def command=(command)
|
@@ -50,7 +50,12 @@ module Sequent
|
|
50
50
|
def process_commands
|
51
51
|
Sequent::Util.skip_if_already_processing(:command_service_process_commands) do
|
52
52
|
transaction_provider.transactional do
|
53
|
-
|
53
|
+
until command_queue.empty?
|
54
|
+
command = command_queue.pop
|
55
|
+
command_middleware.invoke(command) do
|
56
|
+
process_command(command)
|
57
|
+
end
|
58
|
+
end
|
54
59
|
Sequent::Util.done_processing(:command_service_process_commands)
|
55
60
|
end
|
56
61
|
ensure
|
@@ -66,7 +71,9 @@ module Sequent
|
|
66
71
|
|
67
72
|
filters.each { |filter| filter.execute(command) }
|
68
73
|
|
69
|
-
|
74
|
+
I18n.with_locale(Sequent.configuration.error_locale_resolver.call) do
|
75
|
+
fail CommandNotValid, command unless command.valid?
|
76
|
+
end
|
70
77
|
|
71
78
|
parsed_command = command.parse_attrs_to_correct_types
|
72
79
|
command_handlers.select do |h|
|
@@ -98,6 +105,10 @@ module Sequent
|
|
98
105
|
def command_handlers
|
99
106
|
Sequent.configuration.command_handlers
|
100
107
|
end
|
108
|
+
|
109
|
+
def command_middleware
|
110
|
+
Sequent.configuration.command_middleware
|
111
|
+
end
|
101
112
|
end
|
102
113
|
|
103
114
|
# Raised when BaseCommand.valid? returns false
|
data/lib/sequent/core/core.rb
CHANGED
@@ -4,6 +4,7 @@ require_relative 'sequent_oj'
|
|
4
4
|
require_relative 'helpers/helpers'
|
5
5
|
require_relative 'persistors/persistors'
|
6
6
|
require_relative 'transactions/transactions'
|
7
|
+
require_relative 'middleware/middleware'
|
7
8
|
require_relative 'event'
|
8
9
|
require_relative 'aggregate_repository'
|
9
10
|
require_relative 'aggregate_root'
|
data/lib/sequent/core/event.rb
CHANGED
@@ -13,14 +13,14 @@ module Sequent
|
|
13
13
|
include Sequent::Core::Helpers::AttributeSupport
|
14
14
|
include Sequent::Core::Helpers::EqualSupport
|
15
15
|
include Sequent::Core::Helpers::StringSupport
|
16
|
-
attrs aggregate_id: String, sequence_number: Integer, created_at:
|
16
|
+
attrs aggregate_id: String, sequence_number: Integer, created_at: Time
|
17
17
|
|
18
18
|
def initialize(args = {})
|
19
19
|
update_all_attributes args
|
20
20
|
fail 'Missing aggregate_id' unless @aggregate_id
|
21
21
|
fail 'Missing sequence_number' unless @sequence_number
|
22
22
|
|
23
|
-
@created_at ||=
|
23
|
+
@created_at ||= Time.now
|
24
24
|
end
|
25
25
|
|
26
26
|
def payload
|
@@ -26,8 +26,21 @@ module Sequent
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
##
|
30
|
+
# Disables event type caching (ie. for in development).
|
31
|
+
#
|
32
|
+
class NoEventTypesCache
|
33
|
+
def fetch_or_store(event_type)
|
34
|
+
yield(event_type)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(cache_event_types: true)
|
39
|
+
@event_types = if cache_event_types
|
40
|
+
ThreadSafe::Cache.new
|
41
|
+
else
|
42
|
+
NoEventTypesCache.new
|
43
|
+
end
|
31
44
|
end
|
32
45
|
|
33
46
|
##
|
data/lib/sequent/core/ext/ext.rb
CHANGED
@@ -68,6 +68,23 @@ class DateTime
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
class Time
|
72
|
+
def self.from_params(value)
|
73
|
+
value.blank? ? nil : Time.iso8601(value.dup)
|
74
|
+
rescue ArgumentError
|
75
|
+
value
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.deserialize_from_json(value)
|
79
|
+
value.blank? ? nil : Time.iso8601(value.dup)
|
80
|
+
rescue ArgumentError => e
|
81
|
+
return Time.parse(value.dup) if e.message =~ /invalid xmlschema format/ # ruby >= 3
|
82
|
+
return Time.parse(value.dup) if e.message =~ /invalid date:/ # ruby 2.7
|
83
|
+
|
84
|
+
raise
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
71
88
|
class Array
|
72
89
|
def self.deserialize_from_json(value)
|
73
90
|
value
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
class ArgumentSerializer
|
8
|
+
class << self
|
9
|
+
def serialize_value(value, enclose_hash: false)
|
10
|
+
return value.to_s if value.respond_to?(:matches_attr?)
|
11
|
+
return %("#{value}") if value.is_a?(String)
|
12
|
+
return serialize_hash(value, enclose_hash: enclose_hash) if value.is_a?(Hash)
|
13
|
+
|
14
|
+
value
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def serialize_hash(hash, enclose_hash:)
|
20
|
+
serialized = hash
|
21
|
+
.map do |(name, value)|
|
22
|
+
"#{name}: #{serialize_value(value, enclose_hash: true)}"
|
23
|
+
end
|
24
|
+
.join(', ')
|
25
|
+
|
26
|
+
return "{#{serialized}}" if enclose_hash
|
27
|
+
|
28
|
+
serialized
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'dsl'
|
4
|
+
require_relative 'argument_serializer'
|
5
|
+
require_relative 'equals'
|
6
|
+
require_relative 'not_equals'
|
7
|
+
require_relative 'greater_than_equals'
|
8
|
+
require_relative 'greater_than'
|
9
|
+
require_relative 'less_than_equals'
|
10
|
+
require_relative 'less_than'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
module DSL
|
8
|
+
def register_matcher(name, matcher_class)
|
9
|
+
if respond_to?(name)
|
10
|
+
fail ArgumentError, "Cannot register attr matcher because it would overwrite existing method '#{name}'"
|
11
|
+
end
|
12
|
+
|
13
|
+
define_method(name) do |*expected|
|
14
|
+
matcher_class.new(*expected)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
extend DSL
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
Equals = Struct.new(:expected_value) do
|
8
|
+
def matches_attr?(actual_value)
|
9
|
+
actual_value == expected_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"eq(#{ArgumentSerializer.serialize_value(expected_value)})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sequent::Core::Helpers::AttrMatchers.register_matcher(
|
22
|
+
:eq,
|
23
|
+
Sequent::Core::Helpers::AttrMatchers::Equals,
|
24
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
GreaterThan = Struct.new(:expected_value) do
|
8
|
+
def matches_attr?(actual_value)
|
9
|
+
actual_value > expected_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"gt(#{ArgumentSerializer.serialize_value(expected_value)})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sequent::Core::Helpers::AttrMatchers.register_matcher(
|
22
|
+
:gt,
|
23
|
+
Sequent::Core::Helpers::AttrMatchers::GreaterThan,
|
24
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
GreaterThanEquals = Struct.new(:expected_value) do
|
8
|
+
def matches_attr?(actual_value)
|
9
|
+
actual_value >= expected_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"gte(#{ArgumentSerializer.serialize_value(expected_value)})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sequent::Core::Helpers::AttrMatchers.register_matcher(
|
22
|
+
:gte,
|
23
|
+
Sequent::Core::Helpers::AttrMatchers::GreaterThanEquals,
|
24
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
LessThan = Struct.new(:expected_value) do
|
8
|
+
def matches_attr?(actual_value)
|
9
|
+
actual_value < expected_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"lt(#{ArgumentSerializer.serialize_value(expected_value)})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sequent::Core::Helpers::AttrMatchers.register_matcher(
|
22
|
+
:lt,
|
23
|
+
Sequent::Core::Helpers::AttrMatchers::LessThan,
|
24
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
LessThanEquals = Struct.new(:expected_value) do
|
8
|
+
def matches_attr?(actual_value)
|
9
|
+
actual_value <= expected_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"lte(#{ArgumentSerializer.serialize_value(expected_value)})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sequent::Core::Helpers::AttrMatchers.register_matcher(
|
22
|
+
:lte,
|
23
|
+
Sequent::Core::Helpers::AttrMatchers::LessThanEquals,
|
24
|
+
)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sequent
|
4
|
+
module Core
|
5
|
+
module Helpers
|
6
|
+
module AttrMatchers
|
7
|
+
NotEquals = Struct.new(:expected_value) do
|
8
|
+
def matches_attr?(actual_value)
|
9
|
+
actual_value != expected_value
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"neq(#{ArgumentSerializer.serialize_value(expected_value)})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sequent::Core::Helpers::AttrMatchers.register_matcher(
|
22
|
+
:neq,
|
23
|
+
Sequent::Core::Helpers::AttrMatchers::NotEquals,
|
24
|
+
)
|
@@ -6,6 +6,7 @@ require_relative 'array_with_type'
|
|
6
6
|
require_relative 'default_validators'
|
7
7
|
require_relative 'type_conversion_support'
|
8
8
|
require_relative 'date_time_validator'
|
9
|
+
require_relative 'time_validator'
|
9
10
|
require_relative 'association_validator'
|
10
11
|
|
11
12
|
module Sequent
|
@@ -40,19 +41,19 @@ module Sequent
|
|
40
41
|
|
41
42
|
# module containing class methods to be added
|
42
43
|
module ClassMethods
|
43
|
-
|
44
|
-
@types ||= {}
|
45
|
-
return @merged_types if @merged_types
|
44
|
+
attr_reader :types
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
# Called when this module is included or when a class which includes this module is inherited from.
|
47
|
+
#
|
48
|
+
# All declared attrs are merged into @types in order to prevent superfluous calculation of types in a class
|
49
|
+
# hierarchy.
|
50
|
+
def initialize_types
|
51
|
+
@types = inherited_types
|
52
52
|
end
|
53
53
|
|
54
54
|
def attrs(args)
|
55
|
-
|
55
|
+
validate_attrs!(args)
|
56
|
+
|
56
57
|
@types.merge!(args)
|
57
58
|
associations = []
|
58
59
|
args.each do |attribute, type|
|
@@ -126,6 +127,18 @@ EOS
|
|
126
127
|
@upcasters.push(block)
|
127
128
|
end
|
128
129
|
|
130
|
+
private
|
131
|
+
|
132
|
+
def inherited_types
|
133
|
+
merged_types = is_a?(Class) && superclass.respond_to?(:types) ? superclass.types.dup : {}
|
134
|
+
|
135
|
+
included_modules
|
136
|
+
.select { |m| m.include? Sequent::Core::Helpers::AttributeSupport }
|
137
|
+
.reduce(merged_types) do |memo, mod|
|
138
|
+
memo.merge(mod.types)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
129
142
|
def upcast!(hash)
|
130
143
|
return if @upcasters.nil?
|
131
144
|
|
@@ -133,11 +146,24 @@ EOS
|
|
133
146
|
upcaster.call(hash)
|
134
147
|
end
|
135
148
|
end
|
149
|
+
|
150
|
+
def validate_attrs!(args)
|
151
|
+
duplicate_attrs = types.keys & args.keys
|
152
|
+
|
153
|
+
fail ArgumentError, "Attributes already defined: #{duplicate_attrs.join(', ')}" if duplicate_attrs.any?
|
154
|
+
end
|
155
|
+
|
156
|
+
def inherited(subclass)
|
157
|
+
super
|
158
|
+
|
159
|
+
subclass.initialize_types
|
160
|
+
end
|
136
161
|
end
|
137
162
|
|
138
163
|
# extend host class with class methods when we're included
|
139
164
|
def self.included(host_class)
|
140
165
|
host_class.extend(ClassMethods)
|
166
|
+
host_class.initialize_types
|
141
167
|
end
|
142
168
|
|
143
169
|
def attributes
|
@@ -36,11 +36,11 @@ module Sequent
|
|
36
36
|
event_class.types.keys.reject { |k| @@autoset_ignore_attributes.include?(k.to_s) }
|
37
37
|
end
|
38
38
|
|
39
|
-
def autoset_attributes_for_events(*
|
40
|
-
|
41
|
-
on
|
42
|
-
self.class.event_attribute_keys(
|
43
|
-
instance_variable_set(:"@#{attribute_name}", event.
|
39
|
+
def autoset_attributes_for_events(*args)
|
40
|
+
args.each do |arg|
|
41
|
+
on arg do |event|
|
42
|
+
self.class.event_attribute_keys(event.class).each do |attribute_name|
|
43
|
+
instance_variable_set(:"@#{attribute_name}", event.public_send(attribute_name.to_sym))
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -17,6 +17,9 @@ module Sequent
|
|
17
17
|
Date => ->(klass, field) do
|
18
18
|
klass.validates field, 'sequent::Core::Helpers::Date' => true
|
19
19
|
end,
|
20
|
+
Time => ->(klass, field) do
|
21
|
+
klass.validates field, 'sequent::Core::Helpers::Time' => true
|
22
|
+
end,
|
20
23
|
DateTime => ->(klass, field) do
|
21
24
|
klass.validates field, 'sequent::Core::Helpers::DateTime' => true
|
22
25
|
end,
|