activerecord 6.0.0.beta3 → 6.0.0.rc1
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 +286 -6
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/associations.rb +3 -2
- data/lib/active_record/associations/association.rb +1 -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 +3 -13
- 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_proxy.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/preloader.rb +11 -6
- data/lib/active_record/associations/preloader/association.rb +32 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +4 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +42 -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/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +13 -3
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +84 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +10 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +70 -14
- data/lib/active_record/connection_adapters/abstract_adapter.rb +56 -11
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -69
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +9 -6
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +6 -2
- 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/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -27
- 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/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +50 -112
- data/lib/active_record/connection_handling.rb +17 -10
- data/lib/active_record/core.rb +15 -20
- data/lib/active_record/database_configurations.rb +14 -14
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +6 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +180 -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/migration.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railties/databases.rake +68 -6
- data/lib/active_record/reflection.rb +2 -2
- data/lib/active_record/relation.rb +98 -20
- data/lib/active_record/relation/calculations.rb +39 -39
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +3 -9
- data/lib/active_record/relation/merger.rb +7 -16
- data/lib/active_record/relation/query_methods.rb +153 -38
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +3 -2
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +3 -3
- data/lib/active_record/tasks/database_tasks.rb +36 -1
- data/lib/active_record/touch_later.rb +2 -2
- data/lib/active_record/transactions.rb +52 -41
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -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/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +6 -1
- 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 +87 -108
- 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 +12 -11
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -70,7 +70,15 @@ module ActiveRecord
|
|
70
70
|
predicates == other.predicates
|
71
71
|
end
|
72
72
|
|
73
|
-
def invert
|
73
|
+
def invert(as = :nand)
|
74
|
+
if predicates.size == 1
|
75
|
+
inverted_predicates = [ invert_predicate(predicates.first) ]
|
76
|
+
elsif as == :nor
|
77
|
+
inverted_predicates = predicates.map { |node| invert_predicate(node) }
|
78
|
+
else
|
79
|
+
inverted_predicates = [ Arel::Nodes::Not.new(ast) ]
|
80
|
+
end
|
81
|
+
|
74
82
|
WhereClause.new(inverted_predicates)
|
75
83
|
end
|
76
84
|
|
@@ -115,10 +123,6 @@ module ActiveRecord
|
|
115
123
|
node.respond_to?(:operator) && node.operator == :==
|
116
124
|
end
|
117
125
|
|
118
|
-
def inverted_predicates
|
119
|
-
predicates.map { |node| invert_predicate(node) }
|
120
|
-
end
|
121
|
-
|
122
126
|
def invert_predicate(node)
|
123
127
|
case node
|
124
128
|
when NilClass
|
@@ -165,10 +165,11 @@ module ActiveRecord
|
|
165
165
|
|
166
166
|
def quote_bound_value(value, c = connection)
|
167
167
|
if value.respond_to?(:map) && !value.acts_like?(:string)
|
168
|
-
|
168
|
+
quoted = value.map { |v| c.quote(v) }
|
169
|
+
if quoted.empty?
|
169
170
|
c.quote(nil)
|
170
171
|
else
|
171
|
-
|
172
|
+
quoted.join(",")
|
172
173
|
end
|
173
174
|
else
|
174
175
|
c.quote(value)
|
@@ -47,6 +47,7 @@ module ActiveRecord
|
|
47
47
|
end
|
48
48
|
|
49
49
|
private
|
50
|
+
attr_accessor :table_name
|
50
51
|
|
51
52
|
def initialize(connection, options = {})
|
52
53
|
@connection = connection
|
@@ -110,6 +111,8 @@ HEADER
|
|
110
111
|
def table(table, stream)
|
111
112
|
columns = @connection.columns(table)
|
112
113
|
begin
|
114
|
+
self.table_name = table
|
115
|
+
|
113
116
|
tbl = StringIO.new
|
114
117
|
|
115
118
|
# first dump primary key column
|
@@ -159,6 +162,8 @@ HEADER
|
|
159
162
|
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
160
163
|
stream.puts "# #{e.message}"
|
161
164
|
stream.puts
|
165
|
+
ensure
|
166
|
+
self.table_name = nil
|
162
167
|
end
|
163
168
|
end
|
164
169
|
|
@@ -86,8 +86,8 @@ module ActiveRecord
|
|
86
86
|
# # Should return a scope, you can call 'super' here etc.
|
87
87
|
# end
|
88
88
|
# end
|
89
|
-
def default_scope(scope = nil) # :doc:
|
90
|
-
scope =
|
89
|
+
def default_scope(scope = nil, &block) # :doc:
|
90
|
+
scope = block if block_given?
|
91
91
|
|
92
92
|
if scope.is_a?(Relation) || !scope.respond_to?(:call)
|
93
93
|
raise ArgumentError,
|
@@ -100,7 +100,7 @@ module ActiveRecord
|
|
100
100
|
self.default_scopes += [scope]
|
101
101
|
end
|
102
102
|
|
103
|
-
def build_default_scope(
|
103
|
+
def build_default_scope(relation = relation())
|
104
104
|
return if abstract_class?
|
105
105
|
|
106
106
|
if default_scope_override.nil?
|
@@ -111,15 +111,14 @@ module ActiveRecord
|
|
111
111
|
# The user has defined their own default scope method, so call that
|
112
112
|
evaluate_default_scope do
|
113
113
|
if scope = default_scope
|
114
|
-
|
114
|
+
relation.merge!(scope)
|
115
115
|
end
|
116
116
|
end
|
117
117
|
elsif default_scopes.any?
|
118
|
-
base_rel ||= relation
|
119
118
|
evaluate_default_scope do
|
120
|
-
default_scopes.inject(
|
119
|
+
default_scopes.inject(relation) do |default_scope, scope|
|
121
120
|
scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
|
122
|
-
default_scope.
|
121
|
+
default_scope.instance_exec(&scope) || default_scope
|
123
122
|
end
|
124
123
|
end
|
125
124
|
end
|
@@ -113,8 +113,8 @@ module ActiveRecord
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
-
def self.create(connection,
|
117
|
-
relation = block.call Params.new
|
116
|
+
def self.create(connection, callable = nil, &block)
|
117
|
+
relation = (callable || block).call Params.new
|
118
118
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
119
119
|
bind_map = BindMap.new(binds)
|
120
120
|
new(query_builder, bind_map, relation.klass)
|
data/lib/active_record/store.rb
CHANGED
@@ -11,6 +11,12 @@ module ActiveRecord
|
|
11
11
|
# of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
|
12
12
|
# already built around just accessing attributes on the model.
|
13
13
|
#
|
14
|
+
# Every accessor comes with dirty tracking methods (+key_changed?+, +key_was+ and +key_change+) and
|
15
|
+
# methods to access the changes made during the last save (+saved_change_to_key?+, +saved_change_to_key+ and
|
16
|
+
# +key_before_last_save+).
|
17
|
+
#
|
18
|
+
# NOTE: There is no +key_will_change!+ method for accessors, use +store_will_change!+ instead.
|
19
|
+
#
|
14
20
|
# Make sure that you declare the database column used for the serialized store as a text, so there's
|
15
21
|
# plenty of room.
|
16
22
|
#
|
@@ -49,6 +55,12 @@ module ActiveRecord
|
|
49
55
|
# u.settings[:country] # => 'Denmark'
|
50
56
|
# u.settings['country'] # => 'Denmark'
|
51
57
|
#
|
58
|
+
# # Dirty tracking
|
59
|
+
# u.color = 'green'
|
60
|
+
# u.color_changed? # => true
|
61
|
+
# u.color_was # => 'black'
|
62
|
+
# u.color_change # => ['black', 'red']
|
63
|
+
#
|
52
64
|
# # Add additional accessors to an existing store through store_accessor
|
53
65
|
# class SuperUser < User
|
54
66
|
# store_accessor :settings, :privileges, :servants
|
@@ -127,6 +139,42 @@ module ActiveRecord
|
|
127
139
|
define_method(accessor_key) do
|
128
140
|
read_store_attribute(store_attribute, key)
|
129
141
|
end
|
142
|
+
|
143
|
+
define_method("#{accessor_key}_changed?") do
|
144
|
+
return false unless attribute_changed?(store_attribute)
|
145
|
+
prev_store, new_store = changes[store_attribute]
|
146
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
147
|
+
end
|
148
|
+
|
149
|
+
define_method("#{accessor_key}_change") do
|
150
|
+
return unless attribute_changed?(store_attribute)
|
151
|
+
prev_store, new_store = changes[store_attribute]
|
152
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
153
|
+
end
|
154
|
+
|
155
|
+
define_method("#{accessor_key}_was") do
|
156
|
+
return unless attribute_changed?(store_attribute)
|
157
|
+
prev_store, _new_store = changes[store_attribute]
|
158
|
+
prev_store&.dig(key)
|
159
|
+
end
|
160
|
+
|
161
|
+
define_method("saved_change_to_#{accessor_key}?") do
|
162
|
+
return false unless saved_change_to_attribute?(store_attribute)
|
163
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
164
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
165
|
+
end
|
166
|
+
|
167
|
+
define_method("saved_change_to_#{accessor_key}") do
|
168
|
+
return unless saved_change_to_attribute?(store_attribute)
|
169
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
170
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
171
|
+
end
|
172
|
+
|
173
|
+
define_method("#{accessor_key}_before_last_save") do
|
174
|
+
return unless saved_change_to_attribute?(store_attribute)
|
175
|
+
prev_store, _new_store = saved_change_to_attribute(store_attribute)
|
176
|
+
prev_store&.dig(key)
|
177
|
+
end
|
130
178
|
end
|
131
179
|
end
|
132
180
|
|
@@ -12,9 +12,9 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
def resolve_column_aliases(hash)
|
14
14
|
new_hash = hash.dup
|
15
|
-
hash.
|
16
|
-
if
|
17
|
-
new_hash[
|
15
|
+
hash.each_key do |key|
|
16
|
+
if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
|
17
|
+
new_hash[new_key] = new_hash.delete(key)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
new_hash
|
@@ -142,7 +142,9 @@ module ActiveRecord
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def for_each
|
145
|
-
|
145
|
+
return {} unless defined?(Rails)
|
146
|
+
|
147
|
+
databases = Rails.application.config.load_database_yaml
|
146
148
|
database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
|
147
149
|
|
148
150
|
# if this is a single database application we don't want tasks for each primary database
|
@@ -153,6 +155,20 @@ module ActiveRecord
|
|
153
155
|
end
|
154
156
|
end
|
155
157
|
|
158
|
+
def raise_for_multi_db(environment = env, command:)
|
159
|
+
db_configs = ActiveRecord::Base.configurations.configs_for(env_name: environment)
|
160
|
+
|
161
|
+
if db_configs.count > 1
|
162
|
+
dbs_list = []
|
163
|
+
|
164
|
+
db_configs.each do |db|
|
165
|
+
dbs_list << "#{command}:#{db.spec_name}"
|
166
|
+
end
|
167
|
+
|
168
|
+
raise "You're using a multiple database application. To use `#{command}` you must run the namespaced task with a VERSION. Available tasks are #{dbs_list.to_sentence}."
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
156
172
|
def create_current(environment = env)
|
157
173
|
each_current_configuration(environment) { |configuration|
|
158
174
|
create configuration
|
@@ -182,6 +198,25 @@ module ActiveRecord
|
|
182
198
|
}
|
183
199
|
end
|
184
200
|
|
201
|
+
def truncate_tables(configuration)
|
202
|
+
ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
|
203
|
+
table_names = ActiveRecord::Base.connection.tables
|
204
|
+
table_names -= [
|
205
|
+
SchemaMigration.table_name,
|
206
|
+
InternalMetadata.table_name
|
207
|
+
]
|
208
|
+
|
209
|
+
ActiveRecord::Base.connection.truncate_tables(*table_names)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
private :truncate_tables
|
213
|
+
|
214
|
+
def truncate_all(environment = env)
|
215
|
+
ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
|
216
|
+
truncate_tables db_config.config
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
185
220
|
def migrate
|
186
221
|
check_target_version
|
187
222
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
# = Active Record Touch Later
|
5
|
-
module TouchLater
|
5
|
+
module TouchLater # :nodoc:
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
@_touch_time = current_time_from_proper_timezone
|
23
23
|
|
24
24
|
surreptitiously_touch @_defer_touch_attrs
|
25
|
-
|
25
|
+
add_to_transaction
|
26
26
|
|
27
27
|
# touch the parents as we are not calling the after_save callbacks
|
28
28
|
self.class.reflect_on_all_associations(:belongs_to).each do |r|
|
@@ -234,6 +234,12 @@ module ActiveRecord
|
|
234
234
|
set_callback(:commit, :after, *args, &block)
|
235
235
|
end
|
236
236
|
|
237
|
+
# Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
|
238
|
+
def after_save_commit(*args, &block)
|
239
|
+
set_options_for_callbacks!(args, on: [ :create, :update ])
|
240
|
+
set_callback(:commit, :after, *args, &block)
|
241
|
+
end
|
242
|
+
|
237
243
|
# Shortcut for <tt>after_commit :hook, on: :create</tt>.
|
238
244
|
def after_create_commit(*args, &block)
|
239
245
|
set_options_for_callbacks!(args, on: :create)
|
@@ -349,18 +355,6 @@ module ActiveRecord
|
|
349
355
|
clear_transaction_record_state
|
350
356
|
end
|
351
357
|
|
352
|
-
# Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
|
353
|
-
# can be called.
|
354
|
-
def add_to_transaction
|
355
|
-
if has_transactional_callbacks?
|
356
|
-
self.class.connection.add_transaction_record(self)
|
357
|
-
else
|
358
|
-
sync_with_transaction_state
|
359
|
-
set_transaction_state(self.class.connection.transaction_state)
|
360
|
-
end
|
361
|
-
remember_transaction_record_state
|
362
|
-
end
|
363
|
-
|
364
358
|
# Executes +method+ within a transaction and captures its return value as a
|
365
359
|
# status flag. If the status is true the transaction is committed, otherwise
|
366
360
|
# a ROLLBACK is issued. In any case the status flag is returned.
|
@@ -370,9 +364,19 @@ module ActiveRecord
|
|
370
364
|
def with_transaction_returning_status
|
371
365
|
status = nil
|
372
366
|
self.class.transaction do
|
373
|
-
|
367
|
+
unless has_transactional_callbacks?
|
368
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
369
|
+
@transaction_state = self.class.connection.transaction_state
|
370
|
+
end
|
371
|
+
remember_transaction_record_state
|
372
|
+
|
374
373
|
status = yield
|
375
374
|
raise ActiveRecord::Rollback unless status
|
375
|
+
ensure
|
376
|
+
if has_transactional_callbacks? &&
|
377
|
+
(@_new_record_before_last_commit && !new_record? || _trigger_update_callback || _trigger_destroy_callback)
|
378
|
+
add_to_transaction
|
379
|
+
end
|
376
380
|
end
|
377
381
|
status
|
378
382
|
end
|
@@ -382,13 +386,15 @@ module ActiveRecord
|
|
382
386
|
|
383
387
|
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
384
388
|
def remember_transaction_record_state
|
385
|
-
@_start_transaction_state
|
389
|
+
@_start_transaction_state ||= {
|
386
390
|
id: id,
|
387
391
|
new_record: @new_record,
|
388
392
|
destroyed: @destroyed,
|
393
|
+
attributes: @attributes,
|
389
394
|
frozen?: frozen?,
|
390
|
-
|
391
|
-
|
395
|
+
level: 0
|
396
|
+
}
|
397
|
+
@_start_transaction_state[:level] += 1
|
392
398
|
remember_new_record_before_last_commit
|
393
399
|
end
|
394
400
|
|
@@ -402,27 +408,32 @@ module ActiveRecord
|
|
402
408
|
|
403
409
|
# Clear the new record state and id of a record.
|
404
410
|
def clear_transaction_record_state
|
405
|
-
|
411
|
+
return unless @_start_transaction_state
|
412
|
+
@_start_transaction_state[:level] -= 1
|
406
413
|
force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
|
407
414
|
end
|
408
415
|
|
409
416
|
# Force to clear the transaction record state.
|
410
417
|
def force_clear_transaction_record_state
|
411
|
-
@_start_transaction_state
|
418
|
+
@_start_transaction_state = nil
|
419
|
+
@transaction_state = nil
|
412
420
|
end
|
413
421
|
|
414
422
|
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
|
415
|
-
def restore_transaction_record_state(
|
416
|
-
|
417
|
-
|
418
|
-
if transaction_level < 1 || force
|
419
|
-
restore_state = @_start_transaction_state
|
420
|
-
thaw
|
423
|
+
def restore_transaction_record_state(force_restore_state = false)
|
424
|
+
if restore_state = @_start_transaction_state
|
425
|
+
if force_restore_state || restore_state[:level] <= 1
|
421
426
|
@new_record = restore_state[:new_record]
|
422
427
|
@destroyed = restore_state[:destroyed]
|
423
|
-
|
424
|
-
|
425
|
-
|
428
|
+
@attributes = restore_state[:attributes].map do |attr|
|
429
|
+
value = @attributes.fetch_value(attr.name)
|
430
|
+
attr = attr.with_value_from_user(value) if attr.value != value
|
431
|
+
attr
|
432
|
+
end
|
433
|
+
@mutations_from_database = nil
|
434
|
+
@mutations_before_last_save = nil
|
435
|
+
if @attributes.fetch_value(@primary_key) != restore_state[:id]
|
436
|
+
@attributes.write_from_user(@primary_key, restore_state[:id])
|
426
437
|
end
|
427
438
|
freeze if restore_state[:frozen?]
|
428
439
|
end
|
@@ -443,8 +454,10 @@ module ActiveRecord
|
|
443
454
|
end
|
444
455
|
end
|
445
456
|
|
446
|
-
|
447
|
-
|
457
|
+
# Add the record to the current transaction so that the #after_rollback and #after_commit
|
458
|
+
# callbacks can be called.
|
459
|
+
def add_to_transaction
|
460
|
+
self.class.connection.add_transaction_record(self)
|
448
461
|
end
|
449
462
|
|
450
463
|
def has_transactional_callbacks?
|
@@ -464,19 +477,17 @@ module ActiveRecord
|
|
464
477
|
# This method checks to see if the ActiveRecord object's state reflects
|
465
478
|
# the TransactionState, and rolls back or commits the Active Record object
|
466
479
|
# as appropriate.
|
467
|
-
#
|
468
|
-
# Since Active Record objects can be inside multiple transactions, this
|
469
|
-
# method recursively goes through the parent of the TransactionState and
|
470
|
-
# checks if the Active Record object reflects the state of the object.
|
471
480
|
def sync_with_transaction_state
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
481
|
+
if transaction_state = @transaction_state
|
482
|
+
if transaction_state.fully_committed?
|
483
|
+
force_clear_transaction_record_state
|
484
|
+
elsif transaction_state.committed?
|
485
|
+
clear_transaction_record_state
|
486
|
+
elsif transaction_state.rolledback?
|
487
|
+
force_restore_state = transaction_state.fully_rolledback?
|
488
|
+
restore_transaction_record_state(force_restore_state)
|
489
|
+
clear_transaction_record_state
|
490
|
+
end
|
480
491
|
end
|
481
492
|
end
|
482
493
|
end
|