sequent 4.0.0 → 4.3.0
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 +4 -4
- data/bin/sequent +33 -26
- data/lib/notices.rb +2 -0
- data/lib/sequent/application_record.rb +2 -0
- data/lib/sequent/configuration.rb +24 -31
- data/lib/sequent/core/aggregate_repository.rb +48 -13
- data/lib/sequent/core/aggregate_root.rb +36 -7
- data/lib/sequent/core/aggregate_roots.rb +24 -0
- data/lib/sequent/core/aggregate_snapshotter.rb +8 -5
- data/lib/sequent/core/base_command_handler.rb +4 -2
- data/lib/sequent/core/command.rb +17 -9
- data/lib/sequent/core/command_record.rb +8 -3
- data/lib/sequent/core/command_service.rb +18 -18
- data/lib/sequent/core/core.rb +2 -0
- data/lib/sequent/core/current_event.rb +2 -0
- data/lib/sequent/core/event.rb +16 -11
- data/lib/sequent/core/event_publisher.rb +16 -15
- data/lib/sequent/core/event_record.rb +7 -7
- data/lib/sequent/core/event_store.rb +89 -51
- data/lib/sequent/core/ext/ext.rb +9 -1
- data/lib/sequent/core/helpers/array_with_type.rb +4 -1
- data/lib/sequent/core/helpers/association_validator.rb +9 -7
- data/lib/sequent/core/helpers/attribute_support.rb +45 -28
- data/lib/sequent/core/helpers/autoset_attributes.rb +4 -4
- data/lib/sequent/core/helpers/boolean_validator.rb +6 -1
- data/lib/sequent/core/helpers/copyable.rb +2 -2
- data/lib/sequent/core/helpers/date_time_validator.rb +4 -1
- data/lib/sequent/core/helpers/date_validator.rb +6 -1
- data/lib/sequent/core/helpers/default_validators.rb +12 -10
- data/lib/sequent/core/helpers/equal_support.rb +8 -6
- data/lib/sequent/core/helpers/helpers.rb +2 -0
- data/lib/sequent/core/helpers/mergable.rb +6 -5
- data/lib/sequent/core/helpers/message_handler.rb +3 -1
- data/lib/sequent/core/helpers/param_support.rb +19 -15
- data/lib/sequent/core/helpers/secret.rb +14 -12
- data/lib/sequent/core/helpers/string_support.rb +5 -4
- data/lib/sequent/core/helpers/string_to_value_parsers.rb +7 -2
- data/lib/sequent/core/helpers/string_validator.rb +6 -1
- data/lib/sequent/core/helpers/type_conversion_support.rb +5 -3
- data/lib/sequent/core/helpers/uuid_helper.rb +5 -2
- data/lib/sequent/core/helpers/value_validators.rb +23 -9
- data/lib/sequent/core/persistors/active_record_persistor.rb +19 -9
- data/lib/sequent/core/persistors/persistor.rb +16 -14
- data/lib/sequent/core/persistors/persistors.rb +2 -0
- data/lib/sequent/core/persistors/replay_optimized_postgres_persistor.rb +70 -47
- data/lib/sequent/core/projector.rb +53 -22
- data/lib/sequent/core/random_uuid_generator.rb +2 -0
- data/lib/sequent/core/sequent_oj.rb +2 -0
- data/lib/sequent/core/stream_record.rb +9 -3
- data/lib/sequent/core/transactions/active_record_transaction_provider.rb +30 -9
- data/lib/sequent/core/transactions/no_transactions.rb +2 -1
- data/lib/sequent/core/transactions/read_only_active_record_transaction_provider.rb +46 -0
- data/lib/sequent/core/transactions/transactions.rb +3 -0
- data/lib/sequent/core/value_object.rb +8 -10
- data/lib/sequent/core/workflow.rb +35 -5
- data/lib/sequent/generator/aggregate.rb +16 -10
- data/lib/sequent/generator/command.rb +26 -19
- data/lib/sequent/generator/event.rb +19 -17
- data/lib/sequent/generator/generator.rb +2 -0
- data/lib/sequent/generator/project.rb +9 -0
- data/lib/sequent/generator/template_project/Gemfile +1 -1
- data/lib/sequent/generator/template_project/ruby-version +1 -0
- data/lib/sequent/generator.rb +2 -0
- data/lib/sequent/migrations/executor.rb +22 -13
- data/lib/sequent/migrations/functions.rb +5 -6
- data/lib/sequent/migrations/migrate_events.rb +12 -9
- data/lib/sequent/migrations/migrations.rb +2 -1
- data/lib/sequent/migrations/planner.rb +33 -23
- data/lib/sequent/migrations/projectors.rb +4 -3
- data/lib/sequent/migrations/sql.rb +2 -0
- data/lib/sequent/migrations/view_schema.rb +84 -45
- data/lib/sequent/rake/migration_tasks.rb +58 -22
- data/lib/sequent/rake/tasks.rb +5 -2
- data/lib/sequent/sequent.rb +2 -0
- data/lib/sequent/support/database.rb +30 -15
- data/lib/sequent/support/view_projection.rb +6 -3
- data/lib/sequent/support/view_schema.rb +2 -0
- data/lib/sequent/support.rb +2 -0
- data/lib/sequent/test/command_handler_helpers.rb +35 -17
- data/lib/sequent/test/event_handler_helpers.rb +10 -4
- data/lib/sequent/test/event_stream_helpers.rb +7 -3
- data/lib/sequent/test/time_comparison.rb +12 -5
- data/lib/sequent/test.rb +2 -0
- data/lib/sequent/util/dry_run.rb +28 -20
- data/lib/sequent/util/printer.rb +6 -5
- data/lib/sequent/util/skip_if_already_processing.rb +3 -1
- data/lib/sequent/util/timer.rb +2 -0
- data/lib/sequent/util/util.rb +2 -0
- data/lib/sequent.rb +2 -0
- data/lib/version.rb +3 -1
- metadata +84 -67
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support'
|
|
2
4
|
require_relative '../ext/ext'
|
|
3
5
|
require_relative 'array_with_type'
|
|
@@ -38,18 +40,15 @@ module Sequent
|
|
|
38
40
|
|
|
39
41
|
# module containing class methods to be added
|
|
40
42
|
module ClassMethods
|
|
41
|
-
|
|
42
43
|
def types
|
|
43
44
|
@types ||= {}
|
|
44
|
-
if @merged_types
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
@merged_types.merge!(mod.types)
|
|
50
|
-
end
|
|
51
|
-
@merged_types
|
|
45
|
+
return @merged_types if @merged_types
|
|
46
|
+
|
|
47
|
+
@merged_types = is_a?(Class) && superclass.respond_to?(:types) ? @types.merge(superclass.types) : @types
|
|
48
|
+
included_modules.select { |m| m.include? Sequent::Core::Helpers::AttributeSupport }.each do |mod|
|
|
49
|
+
@merged_types.merge!(mod.types)
|
|
52
50
|
end
|
|
51
|
+
@merged_types
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
def attrs(args)
|
|
@@ -58,14 +57,15 @@ module Sequent
|
|
|
58
57
|
associations = []
|
|
59
58
|
args.each do |attribute, type|
|
|
60
59
|
attr_accessor attribute
|
|
60
|
+
|
|
61
61
|
if included_modules.include?(Sequent::Core::Helpers::TypeConversionSupport)
|
|
62
62
|
Sequent::Core::Helpers::DefaultValidators.for(type).add_validations_for(self, attribute)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
if type.
|
|
65
|
+
if type.instance_of?(Sequent::Core::Helpers::ArrayWithType)
|
|
66
66
|
associations << attribute
|
|
67
67
|
elsif included_modules.include?(ActiveModel::Validations) &&
|
|
68
|
-
|
|
68
|
+
type.included_modules.include?(Sequent::Core::Helpers::AttributeSupport)
|
|
69
69
|
associations << attribute
|
|
70
70
|
end
|
|
71
71
|
end
|
|
@@ -77,9 +77,9 @@ module Sequent
|
|
|
77
77
|
def update_all_attributes(attrs)
|
|
78
78
|
super if defined?(super)
|
|
79
79
|
ensure_known_attributes(attrs)
|
|
80
|
-
#{@types.map
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
#{@types.map do |attribute, _|
|
|
81
|
+
"@#{attribute} = attrs[:#{attribute}]"
|
|
82
|
+
end.join("\n ")}
|
|
83
83
|
self
|
|
84
84
|
end
|
|
85
85
|
EOS
|
|
@@ -87,9 +87,9 @@ EOS
|
|
|
87
87
|
class_eval <<EOS
|
|
88
88
|
def update_all_attributes_from_json(attrs)
|
|
89
89
|
super if defined?(super)
|
|
90
|
-
#{@types.map
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
#{@types.map do |attribute, type|
|
|
91
|
+
"@#{attribute} = #{type}.deserialize_from_json(attrs['#{attribute}'])"
|
|
92
|
+
end.join("\n ")}
|
|
93
93
|
end
|
|
94
94
|
EOS
|
|
95
95
|
end
|
|
@@ -106,17 +106,33 @@ EOS
|
|
|
106
106
|
|
|
107
107
|
def deserialize_from_json(args)
|
|
108
108
|
unless args.nil?
|
|
109
|
-
obj = allocate
|
|
109
|
+
obj = allocate
|
|
110
|
+
|
|
111
|
+
upcast!(args)
|
|
112
|
+
|
|
110
113
|
obj.update_all_attributes_from_json(args)
|
|
111
114
|
obj
|
|
112
115
|
end
|
|
113
116
|
end
|
|
114
117
|
|
|
115
|
-
|
|
116
118
|
def numeric?(object)
|
|
117
|
-
true if Float(object)
|
|
119
|
+
true if Float(object)
|
|
120
|
+
rescue StandardError
|
|
121
|
+
false
|
|
118
122
|
end
|
|
119
123
|
|
|
124
|
+
def upcast(&block)
|
|
125
|
+
@upcasters ||= []
|
|
126
|
+
@upcasters.push(block)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def upcast!(hash)
|
|
130
|
+
return if @upcasters.nil?
|
|
131
|
+
|
|
132
|
+
@upcasters.each do |upcaster|
|
|
133
|
+
upcaster.call(hash)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
120
136
|
end
|
|
121
137
|
|
|
122
138
|
# extend host class with class methods when we're included
|
|
@@ -124,11 +140,10 @@ EOS
|
|
|
124
140
|
host_class.extend(ClassMethods)
|
|
125
141
|
end
|
|
126
142
|
|
|
127
|
-
|
|
128
143
|
def attributes
|
|
129
144
|
hash = HashWithIndifferentAccess.new
|
|
130
145
|
self.class.types.each do |name, _|
|
|
131
|
-
value =
|
|
146
|
+
value = instance_variable_get("@#{name}")
|
|
132
147
|
hash[name] = if value.respond_to?(:attributes)
|
|
133
148
|
value.attributes
|
|
134
149
|
else
|
|
@@ -141,7 +156,7 @@ EOS
|
|
|
141
156
|
def as_json(opts = {})
|
|
142
157
|
hash = HashWithIndifferentAccess.new
|
|
143
158
|
self.class.types.each do |name, _|
|
|
144
|
-
value =
|
|
159
|
+
value = instance_variable_get("@#{name}")
|
|
145
160
|
hash[name] = if value.respond_to?(:as_json)
|
|
146
161
|
value.as_json(opts)
|
|
147
162
|
else
|
|
@@ -158,15 +173,15 @@ EOS
|
|
|
158
173
|
def validation_errors(prefix = nil)
|
|
159
174
|
result = errors.to_hash
|
|
160
175
|
self.class.types.each do |field|
|
|
161
|
-
value =
|
|
176
|
+
value = instance_variable_get("@#{field[0]}")
|
|
162
177
|
if value.respond_to? :validation_errors
|
|
163
|
-
value.validation_errors.each { |k, v| result["#{field[0]
|
|
164
|
-
elsif field[1].
|
|
178
|
+
value.validation_errors.each { |k, v| result["#{field[0]}_#{k}".to_sym] = v }
|
|
179
|
+
elsif field[1].instance_of?(ArrayWithType) && value.present?
|
|
165
180
|
value
|
|
166
181
|
.select { |val| val.respond_to?(:validation_errors) }
|
|
167
182
|
.each_with_index do |val, index|
|
|
168
183
|
val.validation_errors.each do |k, v|
|
|
169
|
-
result["#{field[0]
|
|
184
|
+
result["#{field[0]}_#{index}_#{k}".to_sym] = v
|
|
170
185
|
end
|
|
171
186
|
end
|
|
172
187
|
end
|
|
@@ -178,7 +193,9 @@ EOS
|
|
|
178
193
|
return unless Sequent.configuration.strict_check_attributes_on_apply_events
|
|
179
194
|
|
|
180
195
|
unknowns = attrs.keys.map(&:to_s) - self.class.types.keys.map(&:to_s)
|
|
181
|
-
|
|
196
|
+
if unknowns.any?
|
|
197
|
+
fail UnknownAttributeError, "#{self.class.name} does not specify attrs: #{unknowns.join(', ')}"
|
|
198
|
+
end
|
|
182
199
|
end
|
|
183
200
|
end
|
|
184
201
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Core
|
|
3
5
|
module Helpers
|
|
@@ -24,8 +26,7 @@ module Sequent
|
|
|
24
26
|
#
|
|
25
27
|
module AutosetAttributes
|
|
26
28
|
module ClassMethods
|
|
27
|
-
|
|
28
|
-
@@autoset_ignore_attributes = %w{aggregate_id sequence_number created_at}
|
|
29
|
+
@@autoset_ignore_attributes = %w[aggregate_id sequence_number created_at]
|
|
29
30
|
|
|
30
31
|
def set_autoset_ignore_attributes(attribute_names)
|
|
31
32
|
@@autoset_ignore_attributes = attribute_names
|
|
@@ -39,7 +40,7 @@ module Sequent
|
|
|
39
40
|
event_classes.each do |event_class|
|
|
40
41
|
on event_class do |event|
|
|
41
42
|
self.class.event_attribute_keys(event_class).each do |attribute_name|
|
|
42
|
-
instance_variable_set(:"@#{attribute_name
|
|
43
|
+
instance_variable_set(:"@#{attribute_name}", event.send(attribute_name.to_sym))
|
|
43
44
|
end
|
|
44
45
|
end
|
|
45
46
|
end
|
|
@@ -53,4 +54,3 @@ module Sequent
|
|
|
53
54
|
end
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
|
-
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_model'
|
|
2
4
|
require_relative 'value_validators'
|
|
3
5
|
|
|
@@ -18,7 +20,10 @@ module Sequent
|
|
|
18
20
|
# They will be converted to `true`, `false` or `nil`
|
|
19
21
|
class BooleanValidator < ActiveModel::EachValidator
|
|
20
22
|
def validate_each(subject, attribute, value)
|
|
21
|
-
|
|
23
|
+
unless Sequent::Core::Helpers::ValueValidators.for(Boolean).valid_value?(value)
|
|
24
|
+
subject.errors.add attribute,
|
|
25
|
+
:invalid_boolean
|
|
26
|
+
end
|
|
22
27
|
end
|
|
23
28
|
end
|
|
24
29
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_model'
|
|
2
4
|
|
|
3
5
|
module Sequent
|
|
@@ -10,8 +12,9 @@ module Sequent
|
|
|
10
12
|
class DateTimeValidator < ActiveModel::EachValidator
|
|
11
13
|
def validate_each(subject, attribute, value)
|
|
12
14
|
return if value.is_a?(DateTime)
|
|
15
|
+
|
|
13
16
|
DateTime.deserialize_from_json(value)
|
|
14
|
-
rescue
|
|
17
|
+
rescue StandardError
|
|
15
18
|
subject.errors.add attribute, :invalid_date_time
|
|
16
19
|
end
|
|
17
20
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_model'
|
|
2
4
|
require_relative 'value_validators'
|
|
3
5
|
|
|
@@ -10,7 +12,10 @@ module Sequent
|
|
|
10
12
|
# attrs value: Date
|
|
11
13
|
class DateValidator < ActiveModel::EachValidator
|
|
12
14
|
def validate_each(subject, attribute, value)
|
|
13
|
-
|
|
15
|
+
unless Sequent::Core::Helpers::ValueValidators.for(Date).valid_value?(value)
|
|
16
|
+
subject.errors.add attribute,
|
|
17
|
+
:invalid_date
|
|
18
|
+
end
|
|
14
19
|
end
|
|
15
20
|
end
|
|
16
21
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'string_validator'
|
|
2
4
|
require_relative 'boolean_validator'
|
|
3
5
|
require_relative 'date_time_validator'
|
|
@@ -13,18 +15,18 @@ module Sequent
|
|
|
13
15
|
klass.validates_numericality_of field, only_integer: true, allow_nil: true, allow_blank: true
|
|
14
16
|
end,
|
|
15
17
|
Date => ->(klass, field) do
|
|
16
|
-
klass.validates field,
|
|
18
|
+
klass.validates field, 'sequent::Core::Helpers::Date' => true
|
|
17
19
|
end,
|
|
18
20
|
DateTime => ->(klass, field) do
|
|
19
|
-
klass.validates field,
|
|
21
|
+
klass.validates field, 'sequent::Core::Helpers::DateTime' => true
|
|
20
22
|
end,
|
|
21
|
-
Boolean => ->
|
|
22
|
-
klass.validates field,
|
|
23
|
+
Boolean => ->(klass, field) do
|
|
24
|
+
klass.validates field, 'sequent::Core::Helpers::Boolean' => true
|
|
23
25
|
end,
|
|
24
|
-
String => ->
|
|
25
|
-
klass.validates field,
|
|
26
|
+
String => ->(klass, field) do
|
|
27
|
+
klass.validates field, 'sequent::Core::Helpers::String' => true
|
|
26
28
|
end,
|
|
27
|
-
Sequent::Core::Helpers::Secret => ->
|
|
29
|
+
Sequent::Core::Helpers::Secret => ->(klass, field) do
|
|
28
30
|
klass.after_validation do |object|
|
|
29
31
|
if object.errors&.any?
|
|
30
32
|
object.send("#{field}=", nil)
|
|
@@ -33,8 +35,8 @@ module Sequent
|
|
|
33
35
|
object.send("#{field}=", Sequent::Secret.new(raw_value)) if raw_value
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
|
-
end
|
|
37
|
-
}
|
|
38
|
+
end,
|
|
39
|
+
}.freeze
|
|
38
40
|
|
|
39
41
|
def self.for(type)
|
|
40
42
|
new(type)
|
|
@@ -46,7 +48,7 @@ module Sequent
|
|
|
46
48
|
|
|
47
49
|
def add_validations_for(klass, field)
|
|
48
50
|
validator = VALIDATORS[@type]
|
|
49
|
-
validator
|
|
51
|
+
validator&.call(klass, field)
|
|
50
52
|
end
|
|
51
53
|
end
|
|
52
54
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Core
|
|
3
5
|
module Helpers
|
|
@@ -8,16 +10,17 @@ module Sequent
|
|
|
8
10
|
#
|
|
9
11
|
module EqualSupport
|
|
10
12
|
def ==(other)
|
|
11
|
-
return false if other
|
|
13
|
+
return false if other.nil?
|
|
12
14
|
return false if self.class != other.class
|
|
15
|
+
|
|
13
16
|
self.class.types.each do |name, _|
|
|
14
|
-
self_value =
|
|
17
|
+
self_value = send(name)
|
|
15
18
|
other_value = other.send(name)
|
|
16
19
|
if self_value.class == DateTime && other_value.class == DateTime
|
|
17
20
|
# we don't care about milliseconds. If you know a better way of checking for equality please improve.
|
|
18
|
-
return false unless
|
|
21
|
+
return false unless self_value.iso8601 == other_value.iso8601
|
|
19
22
|
else
|
|
20
|
-
return false unless
|
|
23
|
+
return false unless self_value == other_value
|
|
21
24
|
end
|
|
22
25
|
end
|
|
23
26
|
true
|
|
@@ -26,7 +29,7 @@ module Sequent
|
|
|
26
29
|
def hash
|
|
27
30
|
hash = 17
|
|
28
31
|
self.class.types.each do |name, _|
|
|
29
|
-
hash = hash * 31 +
|
|
32
|
+
hash = hash * 31 + send(name).hash
|
|
30
33
|
end
|
|
31
34
|
hash
|
|
32
35
|
end
|
|
@@ -35,7 +38,6 @@ module Sequent
|
|
|
35
38
|
self == other
|
|
36
39
|
end
|
|
37
40
|
end
|
|
38
|
-
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Core
|
|
3
5
|
module Helpers
|
|
@@ -6,17 +8,16 @@ module Sequent
|
|
|
6
8
|
# ben = Person.new(name: 'Ben').merge!(name: 'Ben Vonk')
|
|
7
9
|
#
|
|
8
10
|
module Mergable
|
|
9
|
-
|
|
10
11
|
def merge!(attrs = {})
|
|
11
|
-
warn
|
|
12
|
+
warn <<~EOS
|
|
13
|
+
[DEPRECATION] `merge!` is deprecated. Please use `copy` instead. This method will no longer be included in the next version of Sequent. You can still use it but you will have to include the module `Sequent::Core::Helpers::Mergable` yourself.
|
|
14
|
+
EOS
|
|
12
15
|
attrs.each do |name, value|
|
|
13
|
-
|
|
16
|
+
send("#{name}=", value)
|
|
14
17
|
end
|
|
15
18
|
self
|
|
16
19
|
end
|
|
17
|
-
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
|
-
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Core
|
|
3
5
|
module Helpers
|
|
@@ -56,7 +58,7 @@ module Sequent
|
|
|
56
58
|
|
|
57
59
|
def handle_message(message)
|
|
58
60
|
handlers = self.class.message_mapping[message.class]
|
|
59
|
-
handlers
|
|
61
|
+
handlers&.each { |handler| instance_exec(message, &handler) }
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support'
|
|
2
4
|
|
|
3
5
|
module Sequent
|
|
@@ -21,7 +23,6 @@ module Sequent
|
|
|
21
23
|
def from_form_data(params = {})
|
|
22
24
|
from_params(params, false)
|
|
23
25
|
end
|
|
24
|
-
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
# extend host class with class methods when we're included
|
|
@@ -35,7 +36,8 @@ module Sequent
|
|
|
35
36
|
value = params[attribute]
|
|
36
37
|
|
|
37
38
|
next if strict_nil_check && value.nil?
|
|
38
|
-
next if
|
|
39
|
+
next if !strict_nil_check && value.blank?
|
|
40
|
+
|
|
39
41
|
if type.respond_to? :from_params
|
|
40
42
|
value = type.from_params(value)
|
|
41
43
|
elsif value.is_a?(Array)
|
|
@@ -58,10 +60,12 @@ module Sequent
|
|
|
58
60
|
def as_params
|
|
59
61
|
hash = HashWithIndifferentAccess.new
|
|
60
62
|
self.class.types.each do |field|
|
|
61
|
-
value =
|
|
62
|
-
next if field[0] ==
|
|
63
|
-
|
|
63
|
+
value = instance_variable_get("@#{field[0]}")
|
|
64
|
+
next if field[0] == 'errors'
|
|
65
|
+
|
|
66
|
+
hash[field[0]] = if value.is_a?(Array)
|
|
64
67
|
next if value.blank?
|
|
68
|
+
|
|
65
69
|
value.map { |v| value_to_string(v) }
|
|
66
70
|
else
|
|
67
71
|
value_to_string(value)
|
|
@@ -86,16 +90,16 @@ module Sequent
|
|
|
86
90
|
|
|
87
91
|
def make_params(key, enumerable, memo = {})
|
|
88
92
|
case enumerable
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
when Array
|
|
94
|
+
enumerable.each_with_index do |object, index|
|
|
95
|
+
make_params("#{key}[#{index}]", object, memo)
|
|
96
|
+
end
|
|
97
|
+
when Hash
|
|
98
|
+
enumerable.each do |hash_key, object|
|
|
99
|
+
make_params("#{key}[#{hash_key}]", object, memo)
|
|
100
|
+
end
|
|
101
|
+
else
|
|
102
|
+
memo[key] = enumerable
|
|
99
103
|
end
|
|
100
104
|
memo
|
|
101
105
|
end
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'bcrypt'
|
|
2
4
|
|
|
3
5
|
module Sequent
|
|
4
6
|
module Core
|
|
5
7
|
module Helpers
|
|
6
|
-
|
|
7
8
|
#
|
|
8
9
|
# You can use this in Commands to handle for instance passwords
|
|
9
10
|
# safely. It uses BCrypt to encrypt the Secret.
|
|
@@ -55,7 +56,6 @@ module Sequent
|
|
|
55
56
|
# See +re_encrypt_secret+
|
|
56
57
|
# See +verify_secret+
|
|
57
58
|
class Secret
|
|
58
|
-
|
|
59
59
|
class << self
|
|
60
60
|
def deserialize_from_json(value)
|
|
61
61
|
new(value)
|
|
@@ -65,7 +65,8 @@ module Sequent
|
|
|
65
65
|
# Creates a hash for the given clear text password.
|
|
66
66
|
#
|
|
67
67
|
def encrypt_secret(clear_text_secret)
|
|
68
|
-
fail ArgumentError
|
|
68
|
+
fail ArgumentError, 'clear_text_secret can not be blank' if clear_text_secret.blank?
|
|
69
|
+
|
|
69
70
|
BCrypt::Password.create(clear_text_secret)
|
|
70
71
|
end
|
|
71
72
|
|
|
@@ -74,8 +75,8 @@ module Sequent
|
|
|
74
75
|
# (essentially re-creating the secret hash).
|
|
75
76
|
#
|
|
76
77
|
def re_encrypt_secret(clear_text_secret, hashed_secret)
|
|
77
|
-
fail ArgumentError
|
|
78
|
-
fail ArgumentError
|
|
78
|
+
fail ArgumentError, 'clear_text_secret can not be blank' if clear_text_secret.blank?
|
|
79
|
+
fail ArgumentError, 'hashed_secret can not be blank' if hashed_secret.blank?
|
|
79
80
|
|
|
80
81
|
BCrypt::Engine.hash_secret(clear_text_secret, hashed_secret)
|
|
81
82
|
end
|
|
@@ -84,7 +85,7 @@ module Sequent
|
|
|
84
85
|
# Verifies that the hashed and clear text secret are equal.
|
|
85
86
|
#
|
|
86
87
|
def verify_secret(hashed_secret, clear_text_secret)
|
|
87
|
-
return false if
|
|
88
|
+
return false if hashed_secret.blank? || clear_text_secret.blank?
|
|
88
89
|
|
|
89
90
|
BCrypt::Password.new(hashed_secret) == clear_text_secret
|
|
90
91
|
end
|
|
@@ -93,12 +94,13 @@ module Sequent
|
|
|
93
94
|
attr_reader :value
|
|
94
95
|
|
|
95
96
|
def initialize(value)
|
|
96
|
-
fail ArgumentError
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
fail ArgumentError, 'value can not be blank' if value.blank?
|
|
98
|
+
|
|
99
|
+
@value = if value.is_a?(Secret)
|
|
100
|
+
value.value
|
|
101
|
+
else
|
|
102
|
+
value
|
|
103
|
+
end
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
def encrypt
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Sequent
|
|
2
4
|
module Core
|
|
3
5
|
module Helpers
|
|
@@ -9,14 +11,13 @@ module Sequent
|
|
|
9
11
|
module StringSupport
|
|
10
12
|
def to_s
|
|
11
13
|
s = "#{self.class.name}: "
|
|
12
|
-
|
|
13
|
-
value =
|
|
14
|
+
instance_variables.each do |name|
|
|
15
|
+
value = instance_variable_get(name.to_s)
|
|
14
16
|
s += "#{name}=[#{value}], "
|
|
15
17
|
end
|
|
16
|
-
|
|
18
|
+
'{' + s.chomp(', ') + '}'
|
|
17
19
|
end
|
|
18
20
|
end
|
|
19
|
-
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative '../ext/ext'
|
|
2
4
|
require_relative 'array_with_type'
|
|
3
5
|
|
|
@@ -16,10 +18,11 @@ module Sequent
|
|
|
16
18
|
::DateTime => ->(value) { parse_to_date_time(value) },
|
|
17
19
|
::Sequent::Core::Helpers::ArrayWithType => ->(values, type_in_array) { parse_array(values, type_in_array) },
|
|
18
20
|
::Sequent::Core::Helpers::Secret => ->(value) { Sequent::Core::Helpers::Secret.new(value).encrypt },
|
|
19
|
-
}
|
|
21
|
+
}.freeze
|
|
20
22
|
|
|
21
23
|
def self.parse_to_integer(value)
|
|
22
24
|
return value if value.is_a?(Integer)
|
|
25
|
+
|
|
23
26
|
Integer(value, 10) unless value.blank?
|
|
24
27
|
end
|
|
25
28
|
|
|
@@ -35,12 +38,13 @@ module Sequent
|
|
|
35
38
|
if value.blank? && !(value.is_a?(TrueClass) || value.is_a?(FalseClass))
|
|
36
39
|
nil
|
|
37
40
|
else
|
|
38
|
-
(value.is_a?(TrueClass) || value ==
|
|
41
|
+
(value.is_a?(TrueClass) || value == 'true')
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
def self.parse_to_date(value)
|
|
43
46
|
return if value.blank?
|
|
47
|
+
|
|
44
48
|
value.is_a?(Date) ? value : Date.iso8601(value.dup)
|
|
45
49
|
end
|
|
46
50
|
|
|
@@ -50,6 +54,7 @@ module Sequent
|
|
|
50
54
|
|
|
51
55
|
def self.parse_array(values, type_in_array)
|
|
52
56
|
fail "invalid value for array(): \"#{values}\"" unless values.is_a?(Array)
|
|
57
|
+
|
|
53
58
|
values.map do |item|
|
|
54
59
|
if item.respond_to?(:parse_attrs_to_correct_types)
|
|
55
60
|
item.parse_attrs_to_correct_types
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_model'
|
|
2
4
|
require_relative 'value_validators'
|
|
3
5
|
|
|
@@ -16,7 +18,10 @@ module Sequent
|
|
|
16
18
|
#
|
|
17
19
|
class StringValidator < ActiveModel::EachValidator
|
|
18
20
|
def validate_each(subject, attribute, value)
|
|
19
|
-
|
|
21
|
+
unless Sequent::Core::Helpers::ValueValidators.for(String).valid_value?(value)
|
|
22
|
+
subject.errors.add attribute,
|
|
23
|
+
:invalid_string
|
|
24
|
+
end
|
|
20
25
|
end
|
|
21
26
|
end
|
|
22
27
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_model'
|
|
2
4
|
|
|
3
5
|
module Sequent
|
|
@@ -5,7 +7,6 @@ module Sequent
|
|
|
5
7
|
TypeConversionError = Class.new(RuntimeError)
|
|
6
8
|
|
|
7
9
|
module Helpers
|
|
8
|
-
|
|
9
10
|
# Will parse all values to the correct types.
|
|
10
11
|
# The raw values are typically posted from the web and are therefor mostly strings.
|
|
11
12
|
# To parse a raw value your class must have a parse_from_string method that returns the parsed values.
|
|
@@ -14,8 +15,9 @@ module Sequent
|
|
|
14
15
|
def parse_attrs_to_correct_types
|
|
15
16
|
the_copy = dup
|
|
16
17
|
the_copy.class.types.each do |name, type|
|
|
17
|
-
raw_value = the_copy.send(
|
|
18
|
+
raw_value = the_copy.send(name.to_s)
|
|
18
19
|
next if raw_value.nil?
|
|
20
|
+
|
|
19
21
|
if raw_value.respond_to?(:parse_attrs_to_correct_types)
|
|
20
22
|
the_copy.send("#{name}=", raw_value.parse_attrs_to_correct_types)
|
|
21
23
|
else
|
|
@@ -24,7 +26,7 @@ module Sequent
|
|
|
24
26
|
end
|
|
25
27
|
end
|
|
26
28
|
the_copy
|
|
27
|
-
rescue => e
|
|
29
|
+
rescue StandardError => e
|
|
28
30
|
raise TypeConversionError, e.message
|
|
29
31
|
end
|
|
30
32
|
end
|