activerecord 6.0.0.beta1 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +455 -9
- data/README.rdoc +3 -1
- data/lib/active_record/associations/association.rb +18 -1
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/join_dependency.rb +10 -9
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/preloader.rb +11 -6
- data/lib/active_record/associations.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +15 -5
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +124 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +108 -39
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +91 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +69 -118
- data/lib/active_record/connection_handling.rb +32 -16
- data/lib/active_record/core.rb +27 -20
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +13 -13
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +217 -68
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/relation.rb +184 -35
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +55 -45
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -1
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/arel.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +17 -13
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -143,16 +143,13 @@ module ActiveRecord
|
|
143
143
|
|
144
144
|
def preloaders_for_reflection(reflection, records, scope)
|
145
145
|
records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
|
146
|
-
|
147
|
-
loader.run self
|
148
|
-
loader
|
146
|
+
preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope).run
|
149
147
|
end
|
150
148
|
end
|
151
149
|
|
152
150
|
def grouped_records(association, records, polymorphic_parent)
|
153
151
|
h = {}
|
154
152
|
records.each do |record|
|
155
|
-
next unless record
|
156
153
|
reflection = record.class._reflect_on_association(association)
|
157
154
|
next if polymorphic_parent && !reflection || !record.association(association).klass
|
158
155
|
(h[reflection] ||= []) << record
|
@@ -166,10 +163,18 @@ module ActiveRecord
|
|
166
163
|
@reflection = reflection
|
167
164
|
end
|
168
165
|
|
169
|
-
def run
|
166
|
+
def run
|
167
|
+
self
|
168
|
+
end
|
170
169
|
|
171
170
|
def preloaded_records
|
172
|
-
|
171
|
+
@preloaded_records ||= records_by_owner.flat_map(&:last)
|
172
|
+
end
|
173
|
+
|
174
|
+
def records_by_owner
|
175
|
+
@records_by_owner ||= owners.each_with_object({}) do |owner, result|
|
176
|
+
result[owner] = Array(owner.association(reflection.name).target)
|
177
|
+
end
|
173
178
|
end
|
174
179
|
|
175
180
|
private
|
@@ -703,8 +703,9 @@ module ActiveRecord
|
|
703
703
|
# #belongs_to associations.
|
704
704
|
#
|
705
705
|
# Extra options on the associations, as defined in the
|
706
|
-
# <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt>
|
707
|
-
# also prevent the association's inverse
|
706
|
+
# <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt>
|
707
|
+
# constant, or a custom scope, will also prevent the association's inverse
|
708
|
+
# from being found automatically.
|
708
709
|
#
|
709
710
|
# The automatic guessing of the inverse association uses a heuristic based
|
710
711
|
# on the name of the class, so it may not work for all associations,
|
@@ -46,6 +46,7 @@ module ActiveRecord
|
|
46
46
|
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
47
47
|
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
48
48
|
def read_attribute_before_type_cast(attr_name)
|
49
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
49
50
|
@attributes[attr_name.to_s].value_before_type_cast
|
50
51
|
end
|
51
52
|
|
@@ -60,17 +61,19 @@ module ActiveRecord
|
|
60
61
|
# task.attributes_before_type_cast
|
61
62
|
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
62
63
|
def attributes_before_type_cast
|
64
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
63
65
|
@attributes.values_before_type_cast
|
64
66
|
end
|
65
67
|
|
66
68
|
private
|
67
69
|
|
68
|
-
#
|
70
|
+
# Dispatch target for <tt>*_before_type_cast</tt> attribute methods.
|
69
71
|
def attribute_before_type_cast(attribute_name)
|
70
72
|
read_attribute_before_type_cast(attribute_name)
|
71
73
|
end
|
72
74
|
|
73
75
|
def attribute_came_from_user?(attribute_name)
|
76
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
74
77
|
@attributes[attribute_name].came_from_user?
|
75
78
|
end
|
76
79
|
end
|
@@ -29,9 +29,7 @@ module ActiveRecord
|
|
29
29
|
# <tt>reload</tt> the record and clears changed attributes.
|
30
30
|
def reload(*)
|
31
31
|
super.tap do
|
32
|
-
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
|
33
32
|
@mutations_before_last_save = nil
|
34
|
-
@attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
|
35
33
|
@mutations_from_database = nil
|
36
34
|
end
|
37
35
|
end
|
@@ -51,7 +49,7 @@ module ActiveRecord
|
|
51
49
|
# +to+ When passed, this method will return false unless the value was
|
52
50
|
# changed to the given value
|
53
51
|
def saved_change_to_attribute?(attr_name, **options)
|
54
|
-
mutations_before_last_save.changed?(attr_name,
|
52
|
+
mutations_before_last_save.changed?(attr_name.to_s, options)
|
55
53
|
end
|
56
54
|
|
57
55
|
# Returns the change to an attribute during the last save. If the
|
@@ -63,7 +61,7 @@ module ActiveRecord
|
|
63
61
|
# invoked as +saved_change_to_name+ instead of
|
64
62
|
# <tt>saved_change_to_attribute("name")</tt>.
|
65
63
|
def saved_change_to_attribute(attr_name)
|
66
|
-
mutations_before_last_save.change_to_attribute(attr_name)
|
64
|
+
mutations_before_last_save.change_to_attribute(attr_name.to_s)
|
67
65
|
end
|
68
66
|
|
69
67
|
# Returns the original value of an attribute before the last save.
|
@@ -73,7 +71,7 @@ module ActiveRecord
|
|
73
71
|
# invoked as +name_before_last_save+ instead of
|
74
72
|
# <tt>attribute_before_last_save("name")</tt>.
|
75
73
|
def attribute_before_last_save(attr_name)
|
76
|
-
mutations_before_last_save.original_value(attr_name)
|
74
|
+
mutations_before_last_save.original_value(attr_name.to_s)
|
77
75
|
end
|
78
76
|
|
79
77
|
# Did the last call to +save+ have any changes to change?
|
@@ -101,7 +99,7 @@ module ActiveRecord
|
|
101
99
|
# +to+ When passed, this method will return false unless the value will be
|
102
100
|
# changed to the given value
|
103
101
|
def will_save_change_to_attribute?(attr_name, **options)
|
104
|
-
mutations_from_database.changed?(attr_name,
|
102
|
+
mutations_from_database.changed?(attr_name.to_s, options)
|
105
103
|
end
|
106
104
|
|
107
105
|
# Returns the change to an attribute that will be persisted during the
|
@@ -115,7 +113,7 @@ module ActiveRecord
|
|
115
113
|
# If the attribute will change, the result will be an array containing the
|
116
114
|
# original value and the new value about to be saved.
|
117
115
|
def attribute_change_to_be_saved(attr_name)
|
118
|
-
mutations_from_database.change_to_attribute(attr_name)
|
116
|
+
mutations_from_database.change_to_attribute(attr_name.to_s)
|
119
117
|
end
|
120
118
|
|
121
119
|
# Returns the value of an attribute in the database, as opposed to the
|
@@ -127,7 +125,7 @@ module ActiveRecord
|
|
127
125
|
# saved. It can be invoked as +name_in_database+ instead of
|
128
126
|
# <tt>attribute_in_database("name")</tt>.
|
129
127
|
def attribute_in_database(attr_name)
|
130
|
-
mutations_from_database.original_value(attr_name)
|
128
|
+
mutations_from_database.original_value(attr_name.to_s)
|
131
129
|
end
|
132
130
|
|
133
131
|
# Will the next call to +save+ have any changes to persist?
|
@@ -158,16 +156,51 @@ module ActiveRecord
|
|
158
156
|
end
|
159
157
|
|
160
158
|
private
|
159
|
+
def mutations_from_database
|
160
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
164
|
+
def mutations_before_last_save
|
165
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
166
|
+
super
|
167
|
+
end
|
168
|
+
|
161
169
|
def write_attribute_without_type_cast(attr_name, value)
|
162
|
-
|
163
|
-
|
164
|
-
name = self.class.attribute_alias(name)
|
165
|
-
end
|
166
|
-
result = super(name, value)
|
167
|
-
clear_attribute_change(name)
|
170
|
+
result = super
|
171
|
+
clear_attribute_change(attr_name)
|
168
172
|
result
|
169
173
|
end
|
170
174
|
|
175
|
+
def _touch_row(attribute_names, time)
|
176
|
+
@_touch_attr_names = Set.new(attribute_names)
|
177
|
+
|
178
|
+
affected_rows = super
|
179
|
+
|
180
|
+
if @_skip_dirty_tracking ||= false
|
181
|
+
clear_attribute_changes(@_touch_attr_names)
|
182
|
+
return affected_rows
|
183
|
+
end
|
184
|
+
|
185
|
+
changes = {}
|
186
|
+
@attributes.keys.each do |attr_name|
|
187
|
+
next if @_touch_attr_names.include?(attr_name)
|
188
|
+
|
189
|
+
if attribute_changed?(attr_name)
|
190
|
+
changes[attr_name] = _read_attribute(attr_name)
|
191
|
+
_write_attribute(attr_name, attribute_was(attr_name))
|
192
|
+
clear_attribute_change(attr_name)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
changes_applied
|
197
|
+
changes.each { |attr_name, value| _write_attribute(attr_name, value) }
|
198
|
+
|
199
|
+
affected_rows
|
200
|
+
ensure
|
201
|
+
@_touch_attr_names, @_skip_dirty_tracking = nil, nil
|
202
|
+
end
|
203
|
+
|
171
204
|
def _update_record(attribute_names = attribute_names_for_partial_writes)
|
172
205
|
affected_rows = super
|
173
206
|
changes_applied
|
@@ -16,40 +16,32 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
# Returns the primary key column's value.
|
18
18
|
def id
|
19
|
-
|
20
|
-
primary_key = self.class.primary_key
|
21
|
-
_read_attribute(primary_key) if primary_key
|
19
|
+
_read_attribute(@primary_key)
|
22
20
|
end
|
23
21
|
|
24
22
|
# Sets the primary key column's value.
|
25
23
|
def id=(value)
|
26
|
-
|
27
|
-
primary_key = self.class.primary_key
|
28
|
-
_write_attribute(primary_key, value) if primary_key
|
24
|
+
_write_attribute(@primary_key, value)
|
29
25
|
end
|
30
26
|
|
31
27
|
# Queries the primary key column's value.
|
32
28
|
def id?
|
33
|
-
|
34
|
-
query_attribute(self.class.primary_key)
|
29
|
+
query_attribute(@primary_key)
|
35
30
|
end
|
36
31
|
|
37
32
|
# Returns the primary key column's value before type cast.
|
38
33
|
def id_before_type_cast
|
39
|
-
|
40
|
-
read_attribute_before_type_cast(self.class.primary_key)
|
34
|
+
read_attribute_before_type_cast(@primary_key)
|
41
35
|
end
|
42
36
|
|
43
37
|
# Returns the primary key column's previous value.
|
44
38
|
def id_was
|
45
|
-
|
46
|
-
attribute_was(self.class.primary_key)
|
39
|
+
attribute_was(@primary_key)
|
47
40
|
end
|
48
41
|
|
49
42
|
# Returns the primary key column's value from the database.
|
50
43
|
def id_in_database
|
51
|
-
|
52
|
-
attribute_in_database(self.class.primary_key)
|
44
|
+
attribute_in_database(@primary_key)
|
53
45
|
end
|
54
46
|
|
55
47
|
private
|
@@ -122,7 +114,7 @@ module ActiveRecord
|
|
122
114
|
#
|
123
115
|
# Project.primary_key # => "foo_id"
|
124
116
|
def primary_key=(value)
|
125
|
-
@primary_key = value && value.to_s
|
117
|
+
@primary_key = value && -value.to_s
|
126
118
|
@quoted_primary_key = nil
|
127
119
|
@attributes_builder = nil
|
128
120
|
end
|
@@ -16,8 +16,7 @@ module ActiveRecord
|
|
16
16
|
when true then true
|
17
17
|
when false, nil then false
|
18
18
|
else
|
19
|
-
|
20
|
-
if column.nil?
|
19
|
+
if !type_for_attribute(attr_name) { false }
|
21
20
|
if Numeric === value || value !~ /[^0-9]/
|
22
21
|
!value.to_i.zero?
|
23
22
|
else
|
@@ -33,7 +32,7 @@ module ActiveRecord
|
|
33
32
|
end
|
34
33
|
|
35
34
|
private
|
36
|
-
#
|
35
|
+
# Dispatch target for <tt>*?</tt> attribute methods.
|
37
36
|
def attribute?(attribute_name)
|
38
37
|
query_attribute(attribute_name)
|
39
38
|
end
|
@@ -9,14 +9,11 @@ module ActiveRecord
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def define_method_attribute(name)
|
12
|
-
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
|
13
|
-
|
14
12
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
15
13
|
generated_attribute_methods, name
|
16
14
|
) do |temp_method_name, attr_name_expr|
|
17
15
|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
18
16
|
def #{temp_method_name}
|
19
|
-
#{sync_with_transaction_state}
|
20
17
|
name = #{attr_name_expr}
|
21
18
|
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
22
19
|
end
|
@@ -30,19 +27,16 @@ module ActiveRecord
|
|
30
27
|
# to a date object, like Date.new(2004, 12, 12)).
|
31
28
|
def read_attribute(attr_name, &block)
|
32
29
|
name = attr_name.to_s
|
33
|
-
|
34
|
-
name = self.class.attribute_alias(name)
|
35
|
-
end
|
30
|
+
name = self.class.attribute_aliases[name] || name
|
36
31
|
|
37
|
-
|
38
|
-
name = primary_key if name == "id" && primary_key
|
39
|
-
sync_with_transaction_state if name == primary_key
|
32
|
+
name = @primary_key if name == "id" && @primary_key
|
40
33
|
_read_attribute(name, &block)
|
41
34
|
end
|
42
35
|
|
43
36
|
# This method exists to avoid the expensive primary_key check internally, without
|
44
37
|
# breaking compatibility with the read_attribute API
|
45
38
|
def _read_attribute(attr_name, &block) # :nodoc
|
39
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
46
40
|
@attributes.fetch_value(attr_name.to_s, &block)
|
47
41
|
end
|
48
42
|
|
@@ -13,15 +13,12 @@ module ActiveRecord
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def define_method_attribute=(name)
|
16
|
-
sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
|
17
|
-
|
18
16
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
19
17
|
generated_attribute_methods, name, writer: true,
|
20
18
|
) do |temp_method_name, attr_name_expr|
|
21
19
|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
22
20
|
def #{temp_method_name}(value)
|
23
21
|
name = #{attr_name_expr}
|
24
|
-
#{sync_with_transaction_state}
|
25
22
|
_write_attribute(name, value)
|
26
23
|
end
|
27
24
|
RUBY
|
@@ -34,31 +31,28 @@ module ActiveRecord
|
|
34
31
|
# turned into +nil+.
|
35
32
|
def write_attribute(attr_name, value)
|
36
33
|
name = attr_name.to_s
|
37
|
-
|
38
|
-
name = self.class.attribute_alias(name)
|
39
|
-
end
|
34
|
+
name = self.class.attribute_aliases[name] || name
|
40
35
|
|
41
|
-
|
42
|
-
name = primary_key if name == "id" && primary_key
|
43
|
-
sync_with_transaction_state if name == primary_key
|
36
|
+
name = @primary_key if name == "id" && @primary_key
|
44
37
|
_write_attribute(name, value)
|
45
38
|
end
|
46
39
|
|
47
40
|
# This method exists to avoid the expensive primary_key check internally, without
|
48
41
|
# breaking compatibility with the write_attribute API
|
49
42
|
def _write_attribute(attr_name, value) # :nodoc:
|
43
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
50
44
|
@attributes.write_from_user(attr_name.to_s, value)
|
51
45
|
value
|
52
46
|
end
|
53
47
|
|
54
48
|
private
|
55
49
|
def write_attribute_without_type_cast(attr_name, value)
|
56
|
-
|
57
|
-
@attributes.write_cast_value(
|
50
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
51
|
+
@attributes.write_cast_value(attr_name.to_s, value)
|
58
52
|
value
|
59
53
|
end
|
60
54
|
|
61
|
-
#
|
55
|
+
# Dispatch target for <tt>*=</tt> attribute methods.
|
62
56
|
def attribute=(attribute_name, value)
|
63
57
|
_write_attribute(attribute_name, value)
|
64
58
|
end
|
@@ -35,7 +35,8 @@ module ActiveRecord
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def initialize_generated_modules # :nodoc:
|
38
|
-
@generated_attribute_methods = GeneratedAttributeMethods.new
|
38
|
+
@generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
|
39
|
+
private_constant :GeneratedAttributeMethods
|
39
40
|
@attribute_methods_generated = false
|
40
41
|
include @generated_attribute_methods
|
41
42
|
|
@@ -158,57 +159,6 @@ module ActiveRecord
|
|
158
159
|
end
|
159
160
|
end
|
160
161
|
|
161
|
-
# Regexp for column names (with or without a table name prefix). Matches
|
162
|
-
# the following:
|
163
|
-
# "#{table_name}.#{column_name}"
|
164
|
-
# "#{column_name}"
|
165
|
-
COLUMN_NAME = /\A(?:\w+\.)?\w+\z/i
|
166
|
-
|
167
|
-
# Regexp for column names with order (with or without a table name
|
168
|
-
# prefix, with or without various order modifiers). Matches the following:
|
169
|
-
# "#{table_name}.#{column_name}"
|
170
|
-
# "#{table_name}.#{column_name} #{direction}"
|
171
|
-
# "#{table_name}.#{column_name} #{direction} NULLS FIRST"
|
172
|
-
# "#{table_name}.#{column_name} NULLS LAST"
|
173
|
-
# "#{column_name}"
|
174
|
-
# "#{column_name} #{direction}"
|
175
|
-
# "#{column_name} #{direction} NULLS FIRST"
|
176
|
-
# "#{column_name} NULLS LAST"
|
177
|
-
COLUMN_NAME_WITH_ORDER = /
|
178
|
-
\A
|
179
|
-
(?:\w+\.)?
|
180
|
-
\w+
|
181
|
-
(?:\s+asc|\s+desc)?
|
182
|
-
(?:\s+nulls\s+(?:first|last))?
|
183
|
-
\z
|
184
|
-
/ix
|
185
|
-
|
186
|
-
def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc:
|
187
|
-
unexpected = args.reject do |arg|
|
188
|
-
Arel.arel_node?(arg) ||
|
189
|
-
arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) }
|
190
|
-
end
|
191
|
-
|
192
|
-
return if unexpected.none?
|
193
|
-
|
194
|
-
if allow_unsafe_raw_sql == :deprecated
|
195
|
-
ActiveSupport::Deprecation.warn(
|
196
|
-
"Dangerous query method (method whose arguments are used as raw " \
|
197
|
-
"SQL) called with non-attribute argument(s): " \
|
198
|
-
"#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
|
199
|
-
"arguments will be disallowed in Rails 6.0. This method should " \
|
200
|
-
"not be called with user-provided values, such as request " \
|
201
|
-
"parameters or model attributes. Known-safe values can be passed " \
|
202
|
-
"by wrapping them in Arel.sql()."
|
203
|
-
)
|
204
|
-
else
|
205
|
-
raise(ActiveRecord::UnknownAttributeReference,
|
206
|
-
"Query method called with non-attribute argument(s): " +
|
207
|
-
unexpected.map(&:inspect).join(", ")
|
208
|
-
)
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
162
|
# Returns true if the given attribute exists, otherwise false.
|
213
163
|
#
|
214
164
|
# class Person < ActiveRecord::Base
|
@@ -464,7 +414,7 @@ module ActiveRecord
|
|
464
414
|
end
|
465
415
|
|
466
416
|
def pk_attribute?(name)
|
467
|
-
name ==
|
417
|
+
name == @primary_key
|
468
418
|
end
|
469
419
|
end
|
470
420
|
end
|
@@ -41,6 +41,9 @@ module ActiveRecord
|
|
41
41
|
# +range+ (PostgreSQL only) specifies that the type should be a range (see the
|
42
42
|
# examples below).
|
43
43
|
#
|
44
|
+
# When using a symbol for +cast_type+, extra options are forwarded to the
|
45
|
+
# constructor of the type object.
|
46
|
+
#
|
44
47
|
# ==== Examples
|
45
48
|
#
|
46
49
|
# The type detected by Active Record can be overridden.
|
@@ -112,6 +115,16 @@ module ActiveRecord
|
|
112
115
|
# my_float_range: 1.0..3.5
|
113
116
|
# }
|
114
117
|
#
|
118
|
+
# Passing options to the type constructor
|
119
|
+
#
|
120
|
+
# # app/models/my_model.rb
|
121
|
+
# class MyModel < ActiveRecord::Base
|
122
|
+
# attribute :small_int, :integer, limit: 2
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# MyModel.create(small_int: 65537)
|
126
|
+
# # => Error: 65537 is out of range for the limit of two bytes
|
127
|
+
#
|
115
128
|
# ==== Creating Custom Types
|
116
129
|
#
|
117
130
|
# Users may also define their own custom types, as long as they respond
|
@@ -304,7 +304,7 @@ module ActiveRecord
|
|
304
304
|
def validate_single_association(reflection)
|
305
305
|
association = association_instance_get(reflection.name)
|
306
306
|
record = association && association.reader
|
307
|
-
association_valid?(reflection, record) if record
|
307
|
+
association_valid?(reflection, record) if record && record.changed_for_autosave?
|
308
308
|
end
|
309
309
|
|
310
310
|
# Validate the associated records if <tt>:validate</tt> or
|
@@ -382,10 +382,14 @@ module ActiveRecord
|
|
382
382
|
if association = association_instance_get(reflection.name)
|
383
383
|
autosave = reflection.options[:autosave]
|
384
384
|
|
385
|
+
# By saving the instance variable in a local variable,
|
386
|
+
# we make the whole callback re-entrant.
|
387
|
+
new_record_before_save = @new_record_before_save
|
388
|
+
|
385
389
|
# reconstruct the scope now that we know the owner's id
|
386
390
|
association.reset_scope
|
387
391
|
|
388
|
-
if records = associated_records_to_validate_or_save(association,
|
392
|
+
if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
|
389
393
|
if autosave
|
390
394
|
records_to_destroy = records.select(&:marked_for_destruction?)
|
391
395
|
records_to_destroy.each { |record| association.destroy(record) }
|
@@ -397,7 +401,7 @@ module ActiveRecord
|
|
397
401
|
|
398
402
|
saved = true
|
399
403
|
|
400
|
-
if autosave != false && (
|
404
|
+
if autosave != false && (new_record_before_save || record.new_record?)
|
401
405
|
if autosave
|
402
406
|
saved = association.insert_record(record, false)
|
403
407
|
elsif !reflection.nested?
|
@@ -412,7 +416,7 @@ module ActiveRecord
|
|
412
416
|
saved = record.save(validate: false)
|
413
417
|
end
|
414
418
|
|
415
|
-
raise
|
419
|
+
raise(RecordInvalid.new(association.owner)) unless saved
|
416
420
|
end
|
417
421
|
end
|
418
422
|
end
|
@@ -457,10 +461,16 @@ module ActiveRecord
|
|
457
461
|
# If the record is new or it has changed, returns true.
|
458
462
|
def record_changed?(reflection, record, key)
|
459
463
|
record.new_record? ||
|
460
|
-
|
464
|
+
association_foreign_key_changed?(reflection, record, key) ||
|
461
465
|
record.will_save_change_to_attribute?(reflection.foreign_key)
|
462
466
|
end
|
463
467
|
|
468
|
+
def association_foreign_key_changed?(reflection, record, key)
|
469
|
+
return false if reflection.through_reflection?
|
470
|
+
|
471
|
+
record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
|
472
|
+
end
|
473
|
+
|
464
474
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
465
475
|
#
|
466
476
|
# In addition, it will destroy the association if it was marked for destruction.
|
data/lib/active_record/base.rb
CHANGED
@@ -95,7 +95,7 @@ module ActiveRecord
|
|
95
95
|
#
|
96
96
|
# private
|
97
97
|
# def delete_parents
|
98
|
-
# self.class.
|
98
|
+
# self.class.delete_by(parent_id: id)
|
99
99
|
# end
|
100
100
|
# end
|
101
101
|
#
|
@@ -324,7 +324,7 @@ module ActiveRecord
|
|
324
324
|
|
325
325
|
private
|
326
326
|
|
327
|
-
def create_or_update(
|
327
|
+
def create_or_update(**)
|
328
328
|
_run_save_callbacks { super }
|
329
329
|
end
|
330
330
|
|
@@ -332,7 +332,7 @@ module ActiveRecord
|
|
332
332
|
_run_create_callbacks { super }
|
333
333
|
end
|
334
334
|
|
335
|
-
def _update_record
|
335
|
+
def _update_record
|
336
336
|
_run_update_callbacks { super }
|
337
337
|
end
|
338
338
|
end
|