active_record_compose 1.1.0 → 1.1.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 +4 -4
- data/.yardopts +2 -0
- data/CHANGELOG.md +5 -0
- data/lib/active_record_compose/attributes.rb +63 -55
- data/lib/active_record_compose/callbacks.rb +30 -2
- data/lib/active_record_compose/inspectable.rb +58 -16
- data/lib/active_record_compose/model.rb +0 -313
- data/lib/active_record_compose/persistence.rb +61 -13
- data/lib/active_record_compose/transaction_support.rb +36 -12
- data/lib/active_record_compose/validations.rb +59 -1
- data/lib/active_record_compose/version.rb +1 -1
- data/sig/_internal/package_private.rbs +11 -36
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 491f46737a3744c5c5d3e0319de3ee9f64ae21a9adfab39f36cedc478bfa0e7c
|
|
4
|
+
data.tar.gz: 406f4c471e0d7c40ec7b3a0d492f8e7e6c96d93fd4b43204a128bf52c1f7ed4c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9ff0e91cfb0ca22322d1b9ca47c32f09372afa206e9fa6690c1dfe5ca30d221269ccc55fe17556627234ea8a7019118ddec8a46ad24a52c47239e694dbcac323
|
|
7
|
+
data.tar.gz: 925001a566ee81600ce91862c0c95f241dba2cd735f6ecbc3e987e83e6cd6f59d217e67afa4bed0a981f35c16645661ef7eeb00adb18df92999f9127e22116e0
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -5,8 +5,6 @@ require_relative "attributes/delegation"
|
|
|
5
5
|
require_relative "attributes/querying"
|
|
6
6
|
|
|
7
7
|
module ActiveRecordCompose
|
|
8
|
-
# @private
|
|
9
|
-
#
|
|
10
8
|
# Provides attribute-related functionality for use within ActiveRecordCompose::Model.
|
|
11
9
|
#
|
|
12
10
|
# This module allows you to define attributes on your composed model, including support
|
|
@@ -56,45 +54,32 @@ module ActiveRecordCompose
|
|
|
56
54
|
include ActiveModel::Attributes
|
|
57
55
|
|
|
58
56
|
included do
|
|
57
|
+
# @type self: Class
|
|
58
|
+
|
|
59
59
|
include Querying
|
|
60
60
|
|
|
61
|
-
# @type self: Class
|
|
62
61
|
class_attribute :delegated_attributes, instance_writer: false
|
|
63
62
|
end
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
#
|
|
69
|
-
# class AccountRegistration < ActiveRecordCompose::Model
|
|
70
|
-
# def initialize(account, attributes = {})
|
|
71
|
-
# @account = account
|
|
72
|
-
# super(attributes)
|
|
73
|
-
# models.push(account)
|
|
74
|
-
# end
|
|
75
|
-
#
|
|
76
|
-
# attribute :original_attribute, :string, default: "qux"
|
|
77
|
-
# delegate_attribute :name, to: :account
|
|
78
|
-
#
|
|
79
|
-
# private
|
|
80
|
-
#
|
|
81
|
-
# attr_reader :account
|
|
82
|
-
# end
|
|
83
|
-
#
|
|
84
|
-
# account = Account.new
|
|
85
|
-
# account.name = "foo"
|
|
86
|
-
#
|
|
87
|
-
# registration = AccountRegistration.new(account)
|
|
88
|
-
# registration.name # => "foo" (delegated)
|
|
89
|
-
# registration.name? # => true (delegated attribute method + `?`)
|
|
90
|
-
#
|
|
91
|
-
# registration.name = "bar" # => updates account.name
|
|
92
|
-
# account.name # => "bar"
|
|
93
|
-
# account.name? # => true
|
|
64
|
+
# steep:ignore:start
|
|
65
|
+
|
|
66
|
+
class_methods do
|
|
67
|
+
# Provides a method of attribute access to the encapsulated model.
|
|
94
68
|
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
69
|
+
# It provides a way to access the attributes of the model it encompasses,
|
|
70
|
+
# allowing transparent access as if it had those attributes itself.
|
|
97
71
|
#
|
|
72
|
+
# @param [Array<Symbol, String>] attributes
|
|
73
|
+
# attributes A variable-length list of attribute names to delegate.
|
|
74
|
+
# @param [Symbol, String] to
|
|
75
|
+
# The target object to which attributes are delegated (keyword argument).
|
|
76
|
+
# @param [Boolean] allow_nil
|
|
77
|
+
# allow_nil Whether to allow nil values. Defaults to false.
|
|
78
|
+
# @example Basic usage
|
|
79
|
+
# delegate_attribute :name, :email, to: :profile
|
|
80
|
+
# @example Allowing nil
|
|
81
|
+
# delegate_attribute :bio, to: :profile, allow_nil: true
|
|
82
|
+
# @see Module#delegate for similar behavior in ActiveSupport
|
|
98
83
|
def delegate_attribute(*attributes, to:, allow_nil: false)
|
|
99
84
|
if to.start_with?("@")
|
|
100
85
|
raise ArgumentError, "Instance variables cannot be specified in delegate to. (#{to})"
|
|
@@ -107,45 +92,68 @@ module ActiveRecordCompose
|
|
|
107
92
|
end
|
|
108
93
|
|
|
109
94
|
# Returns a array of attribute name.
|
|
110
|
-
# Attributes declared with
|
|
95
|
+
# Attributes declared with {.delegate_attribute} are also merged.
|
|
111
96
|
#
|
|
97
|
+
# @see #attribute_names
|
|
112
98
|
# @return [Array<String>] array of attribute name.
|
|
113
99
|
def attribute_names = super + delegated_attributes.to_a.map { _1.attribute_name }
|
|
114
100
|
end
|
|
115
101
|
|
|
102
|
+
# steep:ignore:end
|
|
103
|
+
|
|
116
104
|
# Returns a array of attribute name.
|
|
117
|
-
# Attributes declared with
|
|
105
|
+
# Attributes declared with {.delegate_attribute} are also merged.
|
|
106
|
+
#
|
|
107
|
+
# class Foo < ActiveRecordCompose::Base
|
|
108
|
+
# def initialize(attributes = {})
|
|
109
|
+
# @account = Account.new
|
|
110
|
+
# super
|
|
111
|
+
# end
|
|
112
|
+
#
|
|
113
|
+
# attribute :confirmation, :boolean, default: false # plain attribute
|
|
114
|
+
# delegate_attribute :name, to: :account # delegated attribute
|
|
115
|
+
#
|
|
116
|
+
# private
|
|
117
|
+
#
|
|
118
|
+
# attr_reader :account
|
|
119
|
+
# end
|
|
118
120
|
#
|
|
121
|
+
# Foo.attribute_names # Returns the merged state of plain and delegated attributes
|
|
122
|
+
# # => ["confirmation" ,"name"]
|
|
123
|
+
#
|
|
124
|
+
# foo = Foo.new
|
|
125
|
+
# foo.attribute_names # Similar behavior for instance method version
|
|
126
|
+
# # => ["confirmation", "name"]
|
|
127
|
+
#
|
|
128
|
+
# @see #attributes
|
|
119
129
|
# @return [Array<String>] array of attribute name.
|
|
120
130
|
def attribute_names = super + delegated_attributes.to_a.map { _1.attribute_name }
|
|
121
131
|
|
|
122
132
|
# Returns a hash with the attribute name as key and the attribute value as value.
|
|
123
|
-
# Attributes declared with
|
|
133
|
+
# Attributes declared with {.delegate_attribute} are also merged.
|
|
124
134
|
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# super(attributes)
|
|
131
|
-
# models.push(account)
|
|
132
|
-
# end
|
|
135
|
+
# class Foo < ActiveRecordCompose::Base
|
|
136
|
+
# def initialize(attributes = {})
|
|
137
|
+
# @account = Account.new
|
|
138
|
+
# super
|
|
139
|
+
# end
|
|
133
140
|
#
|
|
134
|
-
#
|
|
135
|
-
#
|
|
141
|
+
# attribute :confirmation, :boolean, default: false # plain attribute
|
|
142
|
+
# delegate_attribute :name, to: :account # delegated attribute
|
|
136
143
|
#
|
|
137
|
-
#
|
|
144
|
+
# private
|
|
138
145
|
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
# account = Account.new
|
|
143
|
-
# account.name = "foo"
|
|
146
|
+
# attr_reader :account
|
|
147
|
+
# end
|
|
144
148
|
#
|
|
145
|
-
#
|
|
149
|
+
# foo = Foo.new
|
|
150
|
+
# foo.name = "Alice"
|
|
151
|
+
# foo.confirmation = true
|
|
146
152
|
#
|
|
147
|
-
#
|
|
153
|
+
# foo.attributes # Returns the merged state of plain and delegated attributes
|
|
154
|
+
# # => { "confirmation" => true, "name" => "Alice" }
|
|
148
155
|
#
|
|
156
|
+
# @return [Hash<String, Object>] hash with the attribute name as key and the attribute value as value.
|
|
149
157
|
def attributes
|
|
150
158
|
super.merge(*delegated_attributes.to_a.map { _1.attribute_hash(self) })
|
|
151
159
|
end
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ActiveRecordCompose
|
|
4
|
-
# @private
|
|
5
|
-
#
|
|
6
4
|
# Provides hooks into the life cycle of an ActiveRecordCompose model,
|
|
7
5
|
# allowing you to insert custom logic before or after changes to the object's state.
|
|
8
6
|
#
|
|
@@ -21,18 +19,48 @@ module ActiveRecordCompose
|
|
|
21
19
|
include ActiveModel::Validations::Callbacks
|
|
22
20
|
|
|
23
21
|
included do
|
|
22
|
+
# @!method self.before_save(*args, &block)
|
|
23
|
+
# Registers a callback to be called before a model is saved.
|
|
24
|
+
|
|
25
|
+
# @!method self.around_save(*args, &block)
|
|
26
|
+
# Registers a callback to be called around the save of a model.
|
|
27
|
+
|
|
28
|
+
# @!method self.after_save(*args, &block)
|
|
29
|
+
# Registers a callback to be called after a model is saved.
|
|
30
|
+
|
|
24
31
|
define_model_callbacks :save
|
|
32
|
+
|
|
33
|
+
# @!method self.before_create(*args, &block)
|
|
34
|
+
# Registers a callback to be called before a model is created.
|
|
35
|
+
|
|
36
|
+
# @!method self.around_create(*args, &block)
|
|
37
|
+
# Registers a callback to be called around the creation of a model.
|
|
38
|
+
|
|
39
|
+
# @!method self.after_create(*args, &block)
|
|
40
|
+
# Registers a callback to be called after a model is created.
|
|
41
|
+
|
|
25
42
|
define_model_callbacks :create
|
|
43
|
+
|
|
44
|
+
# @!method self.before_update(*args, &block)
|
|
45
|
+
# Registers a callback to be called before a model is updated.
|
|
46
|
+
|
|
47
|
+
# @!method self.around_update(*args, &block)
|
|
48
|
+
# Registers a callback to be called around the update of a model.
|
|
49
|
+
|
|
50
|
+
# @!method self.after_update(*args, &block)
|
|
51
|
+
# Registers a callback to be called after a update is updated.
|
|
26
52
|
define_model_callbacks :update
|
|
27
53
|
end
|
|
28
54
|
|
|
29
55
|
private
|
|
30
56
|
|
|
57
|
+
# @private
|
|
31
58
|
# Evaluate while firing callbacks such as `before_save` `after_save`
|
|
32
59
|
# before and after block evaluation.
|
|
33
60
|
#
|
|
34
61
|
def with_callbacks(&block) = run_callbacks(:save) { run_callbacks(callback_context, &block) }
|
|
35
62
|
|
|
63
|
+
# @private
|
|
36
64
|
# Returns the symbol representing the callback context, which is `:create` if the record
|
|
37
65
|
# is new, or `:update` if it has been persisted.
|
|
38
66
|
#
|
|
@@ -4,8 +4,6 @@ require "active_support/parameter_filter"
|
|
|
4
4
|
require_relative "attributes"
|
|
5
5
|
|
|
6
6
|
module ActiveRecordCompose
|
|
7
|
-
# @private
|
|
8
|
-
#
|
|
9
7
|
# It provides #inspect behavior.
|
|
10
8
|
# It tries to replicate the inspect format provided by ActiveRecord as closely as possible.
|
|
11
9
|
#
|
|
@@ -39,26 +37,48 @@ module ActiveRecordCompose
|
|
|
39
37
|
extend ActiveSupport::Concern
|
|
40
38
|
include ActiveRecordCompose::Attributes
|
|
41
39
|
|
|
40
|
+
# steep:ignore:start
|
|
41
|
+
|
|
42
|
+
# @private
|
|
43
|
+
FILTERED_MASK =
|
|
44
|
+
Class.new(DelegateClass(::String)) do
|
|
45
|
+
def pretty_print(pp)
|
|
46
|
+
pp.text __getobj__
|
|
47
|
+
end
|
|
48
|
+
end.new(ActiveSupport::ParameterFilter::FILTERED).freeze
|
|
49
|
+
private_constant :FILTERED_MASK
|
|
50
|
+
|
|
51
|
+
# steep:ignore:end
|
|
52
|
+
|
|
42
53
|
included do
|
|
43
54
|
self.filter_attributes = []
|
|
44
55
|
end
|
|
45
56
|
|
|
46
|
-
|
|
57
|
+
# steep:ignore:start
|
|
58
|
+
|
|
59
|
+
class_methods do
|
|
60
|
+
# Returns columns not to expose when invoking {#inspect}.
|
|
61
|
+
#
|
|
62
|
+
# @return [Array<Symbol>]
|
|
63
|
+
# @see #inspect
|
|
47
64
|
def filter_attributes
|
|
48
65
|
if @filter_attributes.nil?
|
|
49
|
-
superclass.filter_attributes
|
|
66
|
+
superclass.filter_attributes
|
|
50
67
|
else
|
|
51
68
|
@filter_attributes
|
|
52
69
|
end
|
|
53
70
|
end
|
|
54
71
|
|
|
72
|
+
# Specify columns not to expose when invoking {#inspect}.
|
|
73
|
+
#
|
|
74
|
+
# @param [Array<Symbol>] value
|
|
75
|
+
# @see #inspect
|
|
55
76
|
def filter_attributes=(value)
|
|
56
77
|
@inspection_filter = nil
|
|
57
78
|
@filter_attributes = value
|
|
58
79
|
end
|
|
59
80
|
|
|
60
|
-
#
|
|
61
|
-
|
|
81
|
+
# @private
|
|
62
82
|
def inspection_filter
|
|
63
83
|
if @filter_attributes.nil?
|
|
64
84
|
superclass.inspection_filter
|
|
@@ -77,20 +97,41 @@ module ActiveRecordCompose
|
|
|
77
97
|
@filter_attributes ||= nil
|
|
78
98
|
end
|
|
79
99
|
end
|
|
80
|
-
|
|
81
|
-
FILTERED_MASK =
|
|
82
|
-
Class.new(DelegateClass(::String)) do
|
|
83
|
-
def pretty_print(pp)
|
|
84
|
-
pp.text __getobj__
|
|
85
|
-
end
|
|
86
|
-
end.new(ActiveSupport::ParameterFilter::FILTERED).freeze
|
|
87
|
-
private_constant :FILTERED_MASK
|
|
88
|
-
|
|
89
|
-
# steep:ignore:end
|
|
90
100
|
end
|
|
91
101
|
|
|
102
|
+
# steep:ignore:end
|
|
103
|
+
|
|
92
104
|
# Returns a formatted string representation of the record's attributes.
|
|
105
|
+
# It tries to replicate the inspect format provided by ActiveRecord as closely as possible.
|
|
106
|
+
#
|
|
107
|
+
# @example
|
|
108
|
+
# class Model < ActiveRecordCompose::Model
|
|
109
|
+
# def initialize(ar_model)
|
|
110
|
+
# @ar_model = ar_model
|
|
111
|
+
# super
|
|
112
|
+
# end
|
|
113
|
+
#
|
|
114
|
+
# attribute :foo, :date, default: -> { Date.today }
|
|
115
|
+
# delegate_attribute :bar, to: :ar_model
|
|
116
|
+
#
|
|
117
|
+
# private attr_reader :ar_model
|
|
118
|
+
# end
|
|
119
|
+
#
|
|
120
|
+
# m = Model.new(ar_model)
|
|
121
|
+
# m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: "2025-11-14", bar: "bar">
|
|
122
|
+
#
|
|
123
|
+
# @example use {.filter_attributes}
|
|
124
|
+
# class Model < ActiveRecordCompose::Model
|
|
125
|
+
# self.filter_attributes += %i[foo]
|
|
126
|
+
#
|
|
127
|
+
# # ...
|
|
128
|
+
# end
|
|
129
|
+
#
|
|
130
|
+
# m = Model.new(ar_model)
|
|
131
|
+
# m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: [FILTERED], bar: "bar">
|
|
93
132
|
#
|
|
133
|
+
# @return [String] formatted string representation of the record's attributes.
|
|
134
|
+
# @see .filter_attributes
|
|
94
135
|
def inspect
|
|
95
136
|
inspection =
|
|
96
137
|
if @attributes
|
|
@@ -126,6 +167,7 @@ module ActiveRecordCompose
|
|
|
126
167
|
|
|
127
168
|
private
|
|
128
169
|
|
|
170
|
+
# @private
|
|
129
171
|
def format_for_inspect(name, value)
|
|
130
172
|
return value.inspect if value.nil?
|
|
131
173
|
|
|
@@ -89,315 +89,6 @@ module ActiveRecordCompose
|
|
|
89
89
|
include ActiveRecordCompose::TransactionSupport
|
|
90
90
|
include ActiveRecordCompose::Inspectable
|
|
91
91
|
|
|
92
|
-
begin
|
|
93
|
-
# @group Model Core
|
|
94
|
-
|
|
95
|
-
# @!method self.delegate_attribute(*attributes, to:, allow_nil: false)
|
|
96
|
-
# Provides a method of attribute access to the encapsulated model.
|
|
97
|
-
#
|
|
98
|
-
# It provides a way to access the attributes of the model it encompasses,
|
|
99
|
-
# allowing transparent access as if it had those attributes itself.
|
|
100
|
-
#
|
|
101
|
-
# @param [Array<Symbol, String>] attributes
|
|
102
|
-
# attributes A variable-length list of attribute names to delegate.
|
|
103
|
-
# @param [Symbol, String] to
|
|
104
|
-
# The target object to which attributes are delegated (keyword argument).
|
|
105
|
-
# @param [Boolean] allow_nil
|
|
106
|
-
# allow_nil Whether to allow nil values. Defaults to false.
|
|
107
|
-
# @example Basic usage
|
|
108
|
-
# delegate_attribute :name, :email, to: :profile
|
|
109
|
-
# @example Allowing nil
|
|
110
|
-
# delegate_attribute :bio, to: :profile, allow_nil: true
|
|
111
|
-
# @see Module#delegate for similar behavior in ActiveSupport
|
|
112
|
-
|
|
113
|
-
# @!method self.attribute_names
|
|
114
|
-
# Returns a array of attribute name.
|
|
115
|
-
# Attributes declared with {.delegate_attribute} are also merged.
|
|
116
|
-
#
|
|
117
|
-
# @see #attribute_names
|
|
118
|
-
# @return [Array<String>] array of attribute name.
|
|
119
|
-
|
|
120
|
-
# @!method attribute_names
|
|
121
|
-
# Returns a array of attribute name.
|
|
122
|
-
# Attributes declared with {.delegate_attribute} are also merged.
|
|
123
|
-
#
|
|
124
|
-
# class Foo < ActiveRecordCompose::Base
|
|
125
|
-
# def initialize(attributes = {})
|
|
126
|
-
# @account = Account.new
|
|
127
|
-
# super
|
|
128
|
-
# end
|
|
129
|
-
#
|
|
130
|
-
# attribute :confirmation, :boolean, default: false # plain attribute
|
|
131
|
-
# delegate_attribute :name, to: :account # delegated attribute
|
|
132
|
-
#
|
|
133
|
-
# private
|
|
134
|
-
#
|
|
135
|
-
# attr_reader :account
|
|
136
|
-
# end
|
|
137
|
-
#
|
|
138
|
-
# Foo.attribute_names # Returns the merged state of plain and delegated attributes
|
|
139
|
-
# # => ["confirmation" ,"name"]
|
|
140
|
-
#
|
|
141
|
-
# foo = Foo.new
|
|
142
|
-
# foo.attribute_names # Similar behavior for instance method version
|
|
143
|
-
# # => ["confirmation", "name"]
|
|
144
|
-
#
|
|
145
|
-
# @see #attributes
|
|
146
|
-
# @return [Array<String>] array of attribute name.
|
|
147
|
-
|
|
148
|
-
# @!method attributes
|
|
149
|
-
# Returns a hash with the attribute name as key and the attribute value as value.
|
|
150
|
-
# Attributes declared with {.delegate_attribute} are also merged.
|
|
151
|
-
#
|
|
152
|
-
# class Foo < ActiveRecordCompose::Base
|
|
153
|
-
# def initialize(attributes = {})
|
|
154
|
-
# @account = Account.new
|
|
155
|
-
# super
|
|
156
|
-
# end
|
|
157
|
-
#
|
|
158
|
-
# attribute :confirmation, :boolean, default: false # plain attribute
|
|
159
|
-
# delegate_attribute :name, to: :account # delegated attribute
|
|
160
|
-
#
|
|
161
|
-
# private
|
|
162
|
-
#
|
|
163
|
-
# attr_reader :account
|
|
164
|
-
# end
|
|
165
|
-
#
|
|
166
|
-
# foo = Foo.new
|
|
167
|
-
# foo.name = "Alice"
|
|
168
|
-
# foo.confirmation = true
|
|
169
|
-
#
|
|
170
|
-
# foo.attributes # Returns the merged state of plain and delegated attributes
|
|
171
|
-
# # => { "confirmation" => true, "name" => "Alice" }
|
|
172
|
-
#
|
|
173
|
-
# @return [Hash<String, Object>] hash with the attribute name as key and the attribute value as value.
|
|
174
|
-
|
|
175
|
-
# @!method persisted?
|
|
176
|
-
# Returns true if model is persisted.
|
|
177
|
-
#
|
|
178
|
-
# By overriding this definition, you can control the callbacks that are triggered when a save is made.
|
|
179
|
-
# For example, returning false will trigger before_create, around_create and after_create,
|
|
180
|
-
# and returning true will trigger {.before_update}, {.around_update} and {.after_update}.
|
|
181
|
-
#
|
|
182
|
-
# @return [Boolean] returns true if model is persisted.
|
|
183
|
-
# @example
|
|
184
|
-
# # A model where persistence is always false
|
|
185
|
-
# class Foo < ActiveRecordCompose::Model
|
|
186
|
-
# before_save { puts "before_save called" }
|
|
187
|
-
# before_create { puts "before_create called" }
|
|
188
|
-
# before_update { puts "before_update called" }
|
|
189
|
-
# after_update { puts "after_update called" }
|
|
190
|
-
# after_create { puts "after_create called" }
|
|
191
|
-
# after_save { puts "after_save called" }
|
|
192
|
-
#
|
|
193
|
-
# def persisted? = false
|
|
194
|
-
# end
|
|
195
|
-
#
|
|
196
|
-
# # A model where persistence is always true
|
|
197
|
-
# class Bar < Foo
|
|
198
|
-
# def persisted? = true
|
|
199
|
-
# end
|
|
200
|
-
#
|
|
201
|
-
# Foo.new.save!
|
|
202
|
-
# # before_save called
|
|
203
|
-
# # before_create called
|
|
204
|
-
# # after_create called
|
|
205
|
-
# # after_save called
|
|
206
|
-
#
|
|
207
|
-
# Bar.new.save!
|
|
208
|
-
# # before_save called
|
|
209
|
-
# # before_update called
|
|
210
|
-
# # after_update called
|
|
211
|
-
# # after_save called
|
|
212
|
-
|
|
213
|
-
# @endgroup
|
|
214
|
-
|
|
215
|
-
# @group Validations
|
|
216
|
-
|
|
217
|
-
# @!method valid?(context = nil)
|
|
218
|
-
# Runs all the validations and returns the result as true or false.
|
|
219
|
-
# @param context Validation context.
|
|
220
|
-
# @return [Boolean] true on success, false on failure.
|
|
221
|
-
|
|
222
|
-
# @!method validate(context = nil)
|
|
223
|
-
# Alias for {#valid?}
|
|
224
|
-
# @see #valid? Validation context.
|
|
225
|
-
# @param context
|
|
226
|
-
# @return [Boolean] true on success, false on failure.
|
|
227
|
-
|
|
228
|
-
# @!method validate!(context = nil)
|
|
229
|
-
# @see #valid?
|
|
230
|
-
# Runs all the validations within the specified context.
|
|
231
|
-
# no errors are found, raises `ActiveRecord::RecordInvalid` otherwise.
|
|
232
|
-
# @param context Validation context.
|
|
233
|
-
# @raise ActiveRecord::RecordInvalid
|
|
234
|
-
|
|
235
|
-
# @!method errors
|
|
236
|
-
# Returns the `ActiveModel::Errors` object that holds all information about attribute error messages.
|
|
237
|
-
#
|
|
238
|
-
# The `ActiveModel::Base` implementation itself,
|
|
239
|
-
# but also aggregates error information for objects stored in {#models} when validation is performed.
|
|
240
|
-
#
|
|
241
|
-
# class Account < ActiveRecord::Base
|
|
242
|
-
# validates :name, :email, presence: true
|
|
243
|
-
# end
|
|
244
|
-
#
|
|
245
|
-
# class AccountRegistration < ActiveRecordCompose::Model
|
|
246
|
-
# def initialize(attributes = {})
|
|
247
|
-
# @account = Account.new
|
|
248
|
-
# super(attributes)
|
|
249
|
-
# models << account
|
|
250
|
-
# end
|
|
251
|
-
#
|
|
252
|
-
# attribute :confirmation, :boolean, default: false
|
|
253
|
-
# validates :confirmation, presence: true
|
|
254
|
-
#
|
|
255
|
-
# private
|
|
256
|
-
#
|
|
257
|
-
# attr_reader :account
|
|
258
|
-
# end
|
|
259
|
-
#
|
|
260
|
-
# registration = AccountRegistration
|
|
261
|
-
# registration.valid?
|
|
262
|
-
# #=> false
|
|
263
|
-
#
|
|
264
|
-
# # In addition to the model's own validation error information (`confirmation`), also aggregates
|
|
265
|
-
# # error information for objects stored in `account` (`name`, `email`) when validation is performed.
|
|
266
|
-
#
|
|
267
|
-
# registration.errors.map { _1.attribute } #=> [:name, :email, :confirmation]
|
|
268
|
-
#
|
|
269
|
-
# @return [ActiveModel::Errors]
|
|
270
|
-
|
|
271
|
-
# @endgroup
|
|
272
|
-
|
|
273
|
-
# @group Persistences
|
|
274
|
-
|
|
275
|
-
# @!method save(**options)
|
|
276
|
-
# Save the models that exist in models.
|
|
277
|
-
# Returns false if any of the targets fail, true if all succeed.
|
|
278
|
-
#
|
|
279
|
-
# The save is performed within a single transaction.
|
|
280
|
-
#
|
|
281
|
-
# Only the `:validate` option takes effect as it is required internally.
|
|
282
|
-
# However, we do not recommend explicitly specifying `validate: false` to skip validation.
|
|
283
|
-
# Additionally, the `:context` option is not accepted.
|
|
284
|
-
# The need for such a value indicates that operations from multiple contexts are being processed.
|
|
285
|
-
# If the contexts differ, we recommend separating them into different model definitions.
|
|
286
|
-
#
|
|
287
|
-
# @param options [Hash] parameters.
|
|
288
|
-
# @option options [Boolean] :validate Whether to run validations.
|
|
289
|
-
# This option is intended for internal use only.
|
|
290
|
-
# Users should avoid explicitly passing <tt>validate: false</tt>,
|
|
291
|
-
# as skipping validations can lead to unexpected behavior.
|
|
292
|
-
# @return [Boolean] returns true on success, false on failure.
|
|
293
|
-
|
|
294
|
-
# @!method save!(**options)
|
|
295
|
-
# Behavior is same to {#save}, but raises an exception prematurely on failure.
|
|
296
|
-
# @see #save
|
|
297
|
-
# @raise ActiveRecord::RecordInvalid
|
|
298
|
-
# @raise ActiveRecord::RecordNotSaved
|
|
299
|
-
|
|
300
|
-
# @!method update(attributes)
|
|
301
|
-
# Assign attributes and {#save}.
|
|
302
|
-
#
|
|
303
|
-
# @param [Hash<String, Object>] attributes
|
|
304
|
-
# new attributes.
|
|
305
|
-
# @see #save
|
|
306
|
-
# @return [Boolean] returns true on success, false on failure.
|
|
307
|
-
|
|
308
|
-
# @!method update!(attributes)
|
|
309
|
-
# Behavior is same to {#update}, but raises an exception prematurely on failure.
|
|
310
|
-
#
|
|
311
|
-
# @param [Hash<String, Object>] attributes
|
|
312
|
-
# new attributes.
|
|
313
|
-
# @see #save
|
|
314
|
-
# @see #update
|
|
315
|
-
# @raise ActiveRecord::RecordInvalid
|
|
316
|
-
# @raise ActiveRecord::RecordNotSaved
|
|
317
|
-
|
|
318
|
-
# @endgroup
|
|
319
|
-
|
|
320
|
-
# @group Callbacks
|
|
321
|
-
|
|
322
|
-
# @!method self.before_save(*args, &block)
|
|
323
|
-
# Registers a callback to be called before a model is saved.
|
|
324
|
-
|
|
325
|
-
# @!method self.around_save(*args, &block)
|
|
326
|
-
# Registers a callback to be called around the save of a model.
|
|
327
|
-
|
|
328
|
-
# @!method self.after_save(*args, &block)
|
|
329
|
-
# Registers a callback to be called after a model is saved.
|
|
330
|
-
|
|
331
|
-
# @!method self.before_create(*args, &block)
|
|
332
|
-
# Registers a callback to be called before a model is created.
|
|
333
|
-
|
|
334
|
-
# @!method self.around_create(*args, &block)
|
|
335
|
-
# Registers a callback to be called around the creation of a model.
|
|
336
|
-
|
|
337
|
-
# @!method self.after_create(*args, &block)
|
|
338
|
-
# Registers a callback to be called after a model is created.
|
|
339
|
-
|
|
340
|
-
# @!method self.before_update(*args, &block)
|
|
341
|
-
# Registers a callback to be called before a model is updated.
|
|
342
|
-
|
|
343
|
-
# @!method self.around_update(*args, &block)
|
|
344
|
-
# Registers a callback to be called around the update of a model.
|
|
345
|
-
|
|
346
|
-
# @!method self.after_update(*args, &block)
|
|
347
|
-
# Registers a callback to be called after a update is updated.
|
|
348
|
-
|
|
349
|
-
# @!method self.after_commit(*args, &block)
|
|
350
|
-
# Registers a block to be called after the transaction is fully committed.
|
|
351
|
-
|
|
352
|
-
# @!method self.after_rollback(*args, &block)
|
|
353
|
-
# Registers a block to be called after the transaction is rolled back.
|
|
354
|
-
|
|
355
|
-
# @endgroup
|
|
356
|
-
|
|
357
|
-
# @!method inspect
|
|
358
|
-
# Returns a formatted string representation of the record's attributes.
|
|
359
|
-
# It tries to replicate the inspect format provided by ActiveRecord as closely as possible.
|
|
360
|
-
#
|
|
361
|
-
# @example
|
|
362
|
-
# class Model < ActiveRecordCompose::Model
|
|
363
|
-
# def initialize(ar_model)
|
|
364
|
-
# @ar_model = ar_model
|
|
365
|
-
# super
|
|
366
|
-
# end
|
|
367
|
-
#
|
|
368
|
-
# attribute :foo, :date, default: -> { Date.today }
|
|
369
|
-
# delegate_attribute :bar, to: :ar_model
|
|
370
|
-
#
|
|
371
|
-
# private attr_reader :ar_model
|
|
372
|
-
# end
|
|
373
|
-
#
|
|
374
|
-
# m = Model.new(ar_model)
|
|
375
|
-
# m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: "2025-11-14", bar: "bar">
|
|
376
|
-
#
|
|
377
|
-
# @example use {.filter_attributes}
|
|
378
|
-
# class Model < ActiveRecordCompose::Model
|
|
379
|
-
# self.filter_attributes += %i[foo]
|
|
380
|
-
#
|
|
381
|
-
# # ...
|
|
382
|
-
# end
|
|
383
|
-
#
|
|
384
|
-
# m = Model.new(ar_model)
|
|
385
|
-
# m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: [FILTERED], bar: "bar">
|
|
386
|
-
#
|
|
387
|
-
# @return [String] formatted string representation of the record's attributes.
|
|
388
|
-
# @see .filter_attributes
|
|
389
|
-
|
|
390
|
-
# @!method self.filter_attributes
|
|
391
|
-
# Returns columns not to expose when invoking {#inspect}.
|
|
392
|
-
# @return [Array<Symbol>]
|
|
393
|
-
# @see #inspect
|
|
394
|
-
|
|
395
|
-
# @!method self.filter_attributes=(value)
|
|
396
|
-
# Specify columns not to expose when invoking {#inspect}.
|
|
397
|
-
# @param [Array<Symbol>] value
|
|
398
|
-
# @see #inspect
|
|
399
|
-
end
|
|
400
|
-
|
|
401
92
|
def initialize(attributes = {})
|
|
402
93
|
super
|
|
403
94
|
end
|
|
@@ -425,8 +116,6 @@ module ActiveRecordCompose
|
|
|
425
116
|
#
|
|
426
117
|
def id = nil
|
|
427
118
|
|
|
428
|
-
# @group Model Core
|
|
429
|
-
|
|
430
119
|
private
|
|
431
120
|
|
|
432
121
|
# Returns a collection of model elements to encapsulate.
|
|
@@ -439,7 +128,5 @@ module ActiveRecordCompose
|
|
|
439
128
|
# @return [ActiveRecordCompose::ComposedCollection]
|
|
440
129
|
#
|
|
441
130
|
def models = @__models ||= ActiveRecordCompose::ComposedCollection.new(self)
|
|
442
|
-
|
|
443
|
-
# @endgroup
|
|
444
131
|
end
|
|
445
132
|
end
|
|
@@ -6,7 +6,6 @@ require_relative "composed_collection"
|
|
|
6
6
|
module ActiveRecordCompose
|
|
7
7
|
using ComposedCollection::PackagePrivate
|
|
8
8
|
|
|
9
|
-
# @private
|
|
10
9
|
module Persistence
|
|
11
10
|
extend ActiveSupport::Concern
|
|
12
11
|
include ActiveRecordCompose::Callbacks
|
|
@@ -22,6 +21,11 @@ module ActiveRecordCompose
|
|
|
22
21
|
# The need for such a value indicates that operations from multiple contexts are being processed.
|
|
23
22
|
# If the contexts differ, we recommend separating them into different model definitions.
|
|
24
23
|
#
|
|
24
|
+
# @param options [Hash] parameters.
|
|
25
|
+
# @option options [Boolean] :validate Whether to run validations.
|
|
26
|
+
# This option is intended for internal use only.
|
|
27
|
+
# Users should avoid explicitly passing <tt>validate: false</tt>,
|
|
28
|
+
# as skipping validations can lead to unexpected behavior.
|
|
25
29
|
# @return [Boolean] returns true on success, false on failure.
|
|
26
30
|
def save(**options)
|
|
27
31
|
with_callbacks { save_models(**options, bang: false) }
|
|
@@ -29,38 +33,80 @@ module ActiveRecordCompose
|
|
|
29
33
|
false
|
|
30
34
|
end
|
|
31
35
|
|
|
32
|
-
#
|
|
33
|
-
# Unlike #save, an exception is raises on failure.
|
|
34
|
-
#
|
|
35
|
-
# Saving, like `#save`, is performed within a single transaction.
|
|
36
|
-
#
|
|
37
|
-
# Only the `:validate` option takes effect as it is required internally.
|
|
38
|
-
# However, we do not recommend explicitly specifying `validate: false` to skip validation.
|
|
39
|
-
# Additionally, the `:context` option is not accepted.
|
|
40
|
-
# The need for such a value indicates that operations from multiple contexts are being processed.
|
|
41
|
-
# If the contexts differ, we recommend separating them into different model definitions.
|
|
36
|
+
# Behavior is same to {#save}, but raises an exception prematurely on failure.
|
|
42
37
|
#
|
|
38
|
+
# @see #save
|
|
39
|
+
# @raise ActiveRecord::RecordInvalid
|
|
40
|
+
# @raise ActiveRecord::RecordNotSaved
|
|
43
41
|
def save!(**options)
|
|
44
42
|
with_callbacks { save_models(**options, bang: true) } || raise_on_save_error
|
|
45
43
|
end
|
|
46
44
|
|
|
47
|
-
# Assign attributes and save.
|
|
45
|
+
# Assign attributes and {#save}.
|
|
48
46
|
#
|
|
47
|
+
# @param [Hash<String, Object>] attributes
|
|
48
|
+
# new attributes.
|
|
49
|
+
# @see #save
|
|
49
50
|
# @return [Boolean] returns true on success, false on failure.
|
|
50
51
|
def update(attributes)
|
|
51
52
|
assign_attributes(attributes)
|
|
52
53
|
save
|
|
53
54
|
end
|
|
54
55
|
|
|
55
|
-
# Behavior is same to
|
|
56
|
+
# Behavior is same to {#update}, but raises an exception prematurely on failure.
|
|
56
57
|
#
|
|
58
|
+
# @param [Hash<String, Object>] attributes
|
|
59
|
+
# new attributes.
|
|
60
|
+
# @see #save
|
|
61
|
+
# @see #update
|
|
62
|
+
# @raise ActiveRecord::RecordInvalid
|
|
63
|
+
# @raise ActiveRecord::RecordNotSaved
|
|
57
64
|
def update!(attributes)
|
|
58
65
|
assign_attributes(attributes)
|
|
59
66
|
save!
|
|
60
67
|
end
|
|
61
68
|
|
|
69
|
+
# @!method persisted?
|
|
70
|
+
# Returns true if model is persisted.
|
|
71
|
+
#
|
|
72
|
+
# By overriding this definition, you can control the callbacks that are triggered when a save is made.
|
|
73
|
+
# For example, returning false will trigger before_create, around_create and after_create,
|
|
74
|
+
# and returning true will trigger {.before_update}, {.around_update} and {.after_update}.
|
|
75
|
+
#
|
|
76
|
+
# @return [Boolean] returns true if model is persisted.
|
|
77
|
+
# @example
|
|
78
|
+
# # A model where persistence is always false
|
|
79
|
+
# class Foo < ActiveRecordCompose::Model
|
|
80
|
+
# before_save { puts "before_save called" }
|
|
81
|
+
# before_create { puts "before_create called" }
|
|
82
|
+
# before_update { puts "before_update called" }
|
|
83
|
+
# after_update { puts "after_update called" }
|
|
84
|
+
# after_create { puts "after_create called" }
|
|
85
|
+
# after_save { puts "after_save called" }
|
|
86
|
+
#
|
|
87
|
+
# def persisted? = false
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# # A model where persistence is always true
|
|
91
|
+
# class Bar < Foo
|
|
92
|
+
# def persisted? = true
|
|
93
|
+
# end
|
|
94
|
+
#
|
|
95
|
+
# Foo.new.save!
|
|
96
|
+
# # before_save called
|
|
97
|
+
# # before_create called
|
|
98
|
+
# # after_create called
|
|
99
|
+
# # after_save called
|
|
100
|
+
#
|
|
101
|
+
# Bar.new.save!
|
|
102
|
+
# # before_save called
|
|
103
|
+
# # before_update called
|
|
104
|
+
# # after_update called
|
|
105
|
+
# # after_save called
|
|
106
|
+
|
|
62
107
|
private
|
|
63
108
|
|
|
109
|
+
# @private
|
|
64
110
|
def save_models(bang:, **options)
|
|
65
111
|
models.__wrapped_models.all? do |model|
|
|
66
112
|
if bang
|
|
@@ -71,8 +117,10 @@ module ActiveRecordCompose
|
|
|
71
117
|
end
|
|
72
118
|
end
|
|
73
119
|
|
|
120
|
+
# @private
|
|
74
121
|
def raise_on_save_error = raise ActiveRecord::RecordNotSaved.new(raise_on_save_error_message, self)
|
|
75
122
|
|
|
123
|
+
# @private
|
|
76
124
|
def raise_on_save_error_message = "Failed to save the model."
|
|
77
125
|
end
|
|
78
126
|
end
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "active_support/core_ext/module"
|
|
4
4
|
|
|
5
5
|
module ActiveRecordCompose
|
|
6
|
-
# @private
|
|
7
6
|
module TransactionSupport
|
|
8
7
|
extend ActiveSupport::Concern
|
|
9
8
|
|
|
@@ -11,82 +10,103 @@ module ActiveRecordCompose
|
|
|
11
10
|
define_callbacks :commit, :rollback, :before_commit, scope: [ :kind, :name ]
|
|
12
11
|
end
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
# steep:ignore:start
|
|
13
|
+
# steep:ignore:start
|
|
16
14
|
|
|
15
|
+
class_methods do
|
|
16
|
+
# @private
|
|
17
17
|
# @deprecated
|
|
18
18
|
def with_connection(...)
|
|
19
19
|
ActiveRecord.deprecator.warn("`with_connection` is deprecated. Use `ActiveRecord::Base.with_connection` instead.")
|
|
20
20
|
ActiveRecord::Base.with_connection(...)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# @private
|
|
23
24
|
# @deprecated
|
|
24
25
|
def lease_connection(...)
|
|
25
26
|
ActiveRecord.deprecator.warn("`lease_connection` is deprecated. Use `ActiveRecord::Base.lease_connection` instead.")
|
|
26
27
|
ActiveRecord::Base.lease_connection(...)
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
# @private
|
|
29
31
|
# @deprecated
|
|
30
32
|
def connection(...)
|
|
31
33
|
ActiveRecord.deprecator.warn("`connection` is deprecated. Use `ActiveRecord::Base.connection` instead.")
|
|
32
34
|
ActiveRecord::Base.connection(...)
|
|
33
35
|
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# steep:ignore:end
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
# steep:ignore:start
|
|
36
41
|
|
|
42
|
+
class_methods do
|
|
43
|
+
# @private
|
|
37
44
|
def before_commit(*args, &block)
|
|
38
45
|
set_options_for_callbacks!(args)
|
|
39
|
-
set_callback(:before_commit, :before, *args, &block)
|
|
46
|
+
set_callback(:before_commit, :before, *args, &block)
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
# Registers a block to be called after the transaction is fully committed.
|
|
50
|
+
#
|
|
42
51
|
def after_commit(*args, &block)
|
|
43
52
|
set_options_for_callbacks!(args, prepend_option)
|
|
44
|
-
set_callback(:commit, :after, *args, &block)
|
|
53
|
+
set_callback(:commit, :after, *args, &block)
|
|
45
54
|
end
|
|
46
55
|
|
|
56
|
+
# Registers a block to be called after the transaction is rolled back.
|
|
57
|
+
#
|
|
47
58
|
def after_rollback(*args, &block)
|
|
48
59
|
set_options_for_callbacks!(args, prepend_option)
|
|
49
|
-
set_callback(:rollback, :after, *args, &block)
|
|
60
|
+
set_callback(:rollback, :after, *args, &block)
|
|
50
61
|
end
|
|
51
62
|
|
|
52
63
|
private
|
|
53
64
|
|
|
65
|
+
# @private
|
|
54
66
|
def prepend_option
|
|
55
|
-
if ActiveRecord.run_after_transaction_callbacks_in_order_defined
|
|
67
|
+
if ActiveRecord.run_after_transaction_callbacks_in_order_defined
|
|
56
68
|
{ prepend: true }
|
|
57
69
|
else
|
|
58
70
|
{}
|
|
59
71
|
end
|
|
60
72
|
end
|
|
61
73
|
|
|
74
|
+
# @private
|
|
62
75
|
def set_options_for_callbacks!(args, enforced_options = {})
|
|
63
76
|
options = args.extract_options!.merge!(enforced_options)
|
|
64
77
|
args << options
|
|
65
78
|
end
|
|
66
79
|
end
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def save!(**options) = with_transaction_returning_status { super }
|
|
81
|
+
# steep:ignore:end
|
|
71
82
|
|
|
72
83
|
concerning :SupportForActiveRecordConnectionAdaptersTransaction do
|
|
84
|
+
# @private
|
|
73
85
|
def trigger_transactional_callbacks? = true
|
|
74
86
|
|
|
87
|
+
# @private
|
|
75
88
|
def before_committed!
|
|
76
89
|
_run_before_commit_callbacks
|
|
77
90
|
end
|
|
78
91
|
|
|
92
|
+
# @private
|
|
79
93
|
def committed!(should_run_callbacks: true)
|
|
80
94
|
_run_commit_callbacks if should_run_callbacks
|
|
81
95
|
end
|
|
82
96
|
|
|
97
|
+
# @private
|
|
83
98
|
def rolledback!(force_restore_state: false, should_run_callbacks: true)
|
|
84
99
|
_run_rollback_callbacks if should_run_callbacks
|
|
85
100
|
end
|
|
86
101
|
end
|
|
87
102
|
|
|
103
|
+
def save(**options) = with_transaction_returning_status { super }
|
|
104
|
+
|
|
105
|
+
def save!(**options) = with_transaction_returning_status { super }
|
|
106
|
+
|
|
88
107
|
private
|
|
89
108
|
|
|
109
|
+
# @private
|
|
90
110
|
def with_transaction_returning_status
|
|
91
111
|
connection_pool.with_connection do |connection|
|
|
92
112
|
with_pool_transaction_isolation_level(connection) do
|
|
@@ -96,13 +116,15 @@ module ActiveRecordCompose
|
|
|
96
116
|
connection.add_transaction_record(self, ensure_finalize || has_transactional_callbacks?) # steep:ignore
|
|
97
117
|
|
|
98
118
|
yield.tap { raise ActiveRecord::Rollback unless _1 }
|
|
99
|
-
end
|
|
119
|
+
end || false
|
|
100
120
|
end
|
|
101
121
|
end
|
|
102
122
|
end
|
|
103
123
|
|
|
124
|
+
# @private
|
|
104
125
|
def default_ar_class = ActiveRecord::Base
|
|
105
126
|
|
|
127
|
+
# @private
|
|
106
128
|
def connection_pool(ar_class: default_ar_class)
|
|
107
129
|
connection_specification_name = ar_class.connection_specification_name
|
|
108
130
|
role = ar_class.current_role
|
|
@@ -114,6 +136,7 @@ module ActiveRecordCompose
|
|
|
114
136
|
connection_handler.retrieve_connection_pool(connection_specification_name, **retrieve_options)
|
|
115
137
|
end
|
|
116
138
|
|
|
139
|
+
# @private
|
|
117
140
|
def with_pool_transaction_isolation_level(connection, &block)
|
|
118
141
|
if ActiveRecord.gem_version.release >= Gem::Version.new("8.1.0")
|
|
119
142
|
isolation_level = ActiveRecord.default_transaction_isolation_level # steep:ignore
|
|
@@ -123,6 +146,7 @@ module ActiveRecordCompose
|
|
|
123
146
|
end
|
|
124
147
|
end
|
|
125
148
|
|
|
149
|
+
# @private
|
|
126
150
|
def has_transactional_callbacks?
|
|
127
151
|
_rollback_callbacks.present? || _commit_callbacks.present? || _before_commit_callbacks.present? # steep:ignore
|
|
128
152
|
end
|
|
@@ -5,7 +5,6 @@ require_relative "composed_collection"
|
|
|
5
5
|
module ActiveRecordCompose
|
|
6
6
|
using ComposedCollection::PackagePrivate
|
|
7
7
|
|
|
8
|
-
# @private
|
|
9
8
|
module Validations
|
|
10
9
|
extend ActiveSupport::Concern
|
|
11
10
|
include ActiveModel::Validations::Callbacks
|
|
@@ -22,27 +21,86 @@ module ActiveRecordCompose
|
|
|
22
21
|
perform_validations(options) ? super : raise_validation_error
|
|
23
22
|
end
|
|
24
23
|
|
|
24
|
+
# Runs all the validations and returns the result as true or false.
|
|
25
|
+
#
|
|
26
|
+
# @param context Validation context.
|
|
27
|
+
# @return [Boolean] true on success, false on failure.
|
|
25
28
|
def valid?(context = nil) = context_for_override_validation.with_override(context) { super }
|
|
26
29
|
|
|
30
|
+
# @!method validate(context = nil)
|
|
31
|
+
# Alias for {#valid?}
|
|
32
|
+
# @see #valid? Validation context.
|
|
33
|
+
# @param context
|
|
34
|
+
# @return [Boolean] true on success, false on failure.
|
|
35
|
+
|
|
36
|
+
# @!method validate!(context = nil)
|
|
37
|
+
# @see #valid?
|
|
38
|
+
# Runs all the validations within the specified context.
|
|
39
|
+
# no errors are found, raises `ActiveRecord::RecordInvalid` otherwise.
|
|
40
|
+
# @param context Validation context.
|
|
41
|
+
# @raise ActiveRecord::RecordInvalid
|
|
42
|
+
|
|
43
|
+
# @!method errors
|
|
44
|
+
# Returns the `ActiveModel::Errors` object that holds all information about attribute error messages.
|
|
45
|
+
#
|
|
46
|
+
# The `ActiveModel::Base` implementation itself,
|
|
47
|
+
# but also aggregates error information for objects stored in {#models} when validation is performed.
|
|
48
|
+
#
|
|
49
|
+
# class Account < ActiveRecord::Base
|
|
50
|
+
# validates :name, :email, presence: true
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# class AccountRegistration < ActiveRecordCompose::Model
|
|
54
|
+
# def initialize(attributes = {})
|
|
55
|
+
# @account = Account.new
|
|
56
|
+
# super(attributes)
|
|
57
|
+
# models << account
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# attribute :confirmation, :boolean, default: false
|
|
61
|
+
# validates :confirmation, presence: true
|
|
62
|
+
#
|
|
63
|
+
# private
|
|
64
|
+
#
|
|
65
|
+
# attr_reader :account
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# registration = AccountRegistration
|
|
69
|
+
# registration.valid?
|
|
70
|
+
# #=> false
|
|
71
|
+
#
|
|
72
|
+
# # In addition to the model's own validation error information (`confirmation`), also aggregates
|
|
73
|
+
# # error information for objects stored in `account` (`name`, `email`) when validation is performed.
|
|
74
|
+
#
|
|
75
|
+
# registration.errors.map { _1.attribute } #=> [:name, :email, :confirmation]
|
|
76
|
+
#
|
|
77
|
+
# @return [ActiveModel::Errors]
|
|
78
|
+
|
|
27
79
|
private
|
|
28
80
|
|
|
81
|
+
# @private
|
|
29
82
|
def validate_models
|
|
30
83
|
context = override_validation_context
|
|
31
84
|
models.__wrapped_models.lazy.select { _1.invalid?(context) }.each { errors.merge!(_1) }
|
|
32
85
|
end
|
|
33
86
|
|
|
87
|
+
# @private
|
|
34
88
|
def perform_validations(options)
|
|
35
89
|
options[:validate] == false || valid?(options[:context])
|
|
36
90
|
end
|
|
37
91
|
|
|
92
|
+
# @private
|
|
38
93
|
def raise_validation_error = raise ActiveRecord::RecordInvalid, self
|
|
39
94
|
|
|
95
|
+
# @private
|
|
40
96
|
def context_for_override_validation
|
|
41
97
|
@context_for_override_validation ||= OverrideValidationContext.new
|
|
42
98
|
end
|
|
43
99
|
|
|
100
|
+
# @private
|
|
44
101
|
def override_validation_context = context_for_override_validation.context
|
|
45
102
|
|
|
103
|
+
# @private
|
|
46
104
|
class OverrideValidationContext
|
|
47
105
|
attr_reader :context
|
|
48
106
|
|
|
@@ -4,19 +4,13 @@ module ActiveRecordCompose
|
|
|
4
4
|
include ActiveModel::Attributes
|
|
5
5
|
include Querying
|
|
6
6
|
|
|
7
|
+
def self.delegate_attribute: (*untyped methods, to: untyped, ?allow_nil: bool) -> untyped
|
|
8
|
+
def self.delegated_attributes: () -> Array[Delegation]
|
|
9
|
+
def self.delegated_attributes=: (Array[Delegation]) -> untyped
|
|
7
10
|
def delegated_attributes: () -> Array[Delegation]
|
|
8
11
|
|
|
9
12
|
@attributes: untyped
|
|
10
13
|
|
|
11
|
-
module ClassMethods : Module
|
|
12
|
-
include ActiveModel::Attributes::ClassMethods
|
|
13
|
-
include ActiveModel::AttributeMethods::ClassMethods
|
|
14
|
-
|
|
15
|
-
def delegate_attribute: (*untyped methods, to: untyped, ?allow_nil: bool) -> untyped
|
|
16
|
-
def delegated_attributes: () -> Array[Delegation]
|
|
17
|
-
def delegated_attributes=: (Array[Delegation]) -> untyped
|
|
18
|
-
end
|
|
19
|
-
|
|
20
14
|
class AttributePredicate
|
|
21
15
|
def initialize: (untyped value) -> void
|
|
22
16
|
def call: -> bool
|
|
@@ -88,31 +82,20 @@ module ActiveRecordCompose
|
|
|
88
82
|
module Inspectable
|
|
89
83
|
extend ActiveSupport::Concern
|
|
90
84
|
include Attributes
|
|
91
|
-
extend Inspectable::ClassMethods
|
|
92
85
|
|
|
86
|
+
def self.inspection_filter: () -> ActiveSupport::ParameterFilter
|
|
87
|
+
def self.filter_attributes: () -> Array[untyped]
|
|
88
|
+
def self.filter_attributes=: (Array[untyped]) -> void
|
|
93
89
|
def inspect: () -> String
|
|
94
90
|
def pretty_print: (untyped q) -> void
|
|
95
91
|
|
|
96
92
|
private
|
|
97
93
|
def format_for_inspect: (String name, untyped value) -> String
|
|
98
|
-
|
|
99
|
-
module ClassMethods
|
|
100
|
-
FILTERED_MASK: String
|
|
101
|
-
|
|
102
|
-
def inspection_filter: () -> ActiveSupport::ParameterFilter
|
|
103
|
-
def filter_attributes: () -> Array[untyped]
|
|
104
|
-
def filter_attributes=: (Array[untyped]) -> void
|
|
105
|
-
|
|
106
|
-
@inspection_filter: ActiveSupport::ParameterFilter?
|
|
107
|
-
@filter_attributes: untyped
|
|
108
|
-
end
|
|
109
94
|
end
|
|
110
95
|
|
|
111
96
|
class Model
|
|
112
97
|
include Attributes
|
|
113
|
-
extend Attributes::ClassMethods
|
|
114
98
|
include TransactionSupport
|
|
115
|
-
extend TransactionSupport::ClassMethods
|
|
116
99
|
include Callbacks
|
|
117
100
|
|
|
118
101
|
@__models: ComposedCollection
|
|
@@ -125,19 +108,12 @@ module ActiveRecordCompose
|
|
|
125
108
|
module TransactionSupport
|
|
126
109
|
extend ActiveSupport::Concern
|
|
127
110
|
include ActiveRecord::Transactions
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
module ClassMethods
|
|
131
|
-
include ActiveSupport::Callbacks::ClassMethods
|
|
132
|
-
|
|
133
|
-
def before_commit: (*untyped) -> untyped
|
|
134
|
-
def after_commit: (*untyped) -> untyped
|
|
135
|
-
def after_rollback: (*untyped) -> untyped
|
|
111
|
+
include ActiveSupport::Callbacks
|
|
112
|
+
extend ActiveSupport::Callbacks::ClassMethods
|
|
136
113
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
end
|
|
114
|
+
def self.before_commit: (*untyped) -> untyped
|
|
115
|
+
def self.after_commit: (*untyped) -> untyped
|
|
116
|
+
def self.after_rollback: (*untyped) -> untyped
|
|
141
117
|
|
|
142
118
|
def save: (**untyped options) -> bool
|
|
143
119
|
def save!: (**untyped options) -> untyped
|
|
@@ -154,7 +130,6 @@ module ActiveRecordCompose
|
|
|
154
130
|
module Persistence
|
|
155
131
|
include Callbacks
|
|
156
132
|
include TransactionSupport
|
|
157
|
-
extend TransactionSupport::ClassMethods
|
|
158
133
|
|
|
159
134
|
def save: (**untyped options) -> bool
|
|
160
135
|
def save!: (**untyped options) -> untyped
|