activerecord 6.0.0.beta1 → 6.0.0.beta2
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 +99 -2
- data/lib/active_record.rb +7 -0
- data/lib/active_record/associations/association.rb +17 -0
- data/lib/active_record/associations/collection_association.rb +5 -6
- data/lib/active_record/associations/collection_proxy.rb +12 -41
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +11 -6
- data/lib/active_record/associations/preloader/association.rb +3 -4
- data/lib/active_record/associations/preloader/through_association.rb +9 -20
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +25 -12
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +17 -9
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +47 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +16 -8
- data/lib/active_record/connection_adapters/abstract/transaction.rb +5 -2
- data/lib/active_record/connection_adapters/abstract_adapter.rb +6 -4
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +28 -65
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +59 -1
- 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 +1 -1
- 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 +15 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +30 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +27 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +8 -5
- data/lib/active_record/connection_handling.rb +9 -4
- data/lib/active_record/core.rb +13 -1
- data/lib/active_record/database_configurations.rb +30 -10
- data/lib/active_record/database_configurations/hash_config.rb +1 -1
- data/lib/active_record/database_configurations/url_config.rb +9 -4
- data/lib/active_record/errors.rb +17 -12
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +90 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +62 -63
- data/lib/active_record/persistence.rb +6 -6
- data/lib/active_record/querying.rb +2 -3
- data/lib/active_record/railtie.rb +9 -0
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/reflection.rb +15 -29
- data/lib/active_record/relation.rb +86 -15
- data/lib/active_record/relation/calculations.rb +2 -4
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +8 -4
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +28 -8
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +1 -5
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/scoping/default.rb +1 -8
- data/lib/active_record/scoping/named.rb +9 -1
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +3 -1
- data/lib/arel.rb +7 -0
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- metadata +11 -8
@@ -1323,7 +1323,7 @@ module ActiveRecord
|
|
1323
1323
|
def record_version_state_after_migrating(version)
|
1324
1324
|
if down?
|
1325
1325
|
migrated.delete(version)
|
1326
|
-
ActiveRecord::SchemaMigration.
|
1326
|
+
ActiveRecord::SchemaMigration.delete_by(version: version.to_s)
|
1327
1327
|
else
|
1328
1328
|
migrated << version
|
1329
1329
|
ActiveRecord::SchemaMigration.create!(version: version.to_s)
|
@@ -16,13 +16,55 @@ module ActiveRecord
|
|
16
16
|
V6_0 = Current
|
17
17
|
|
18
18
|
class V5_2 < V6_0
|
19
|
+
module TableDefinition
|
20
|
+
def timestamps(**options)
|
21
|
+
options[:precision] ||= nil
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
19
26
|
module CommandRecorder
|
20
27
|
def invert_transaction(args, &block)
|
21
28
|
[:transaction, args, block]
|
22
29
|
end
|
23
30
|
end
|
24
31
|
|
32
|
+
def create_table(table_name, **options)
|
33
|
+
if block_given?
|
34
|
+
super { |t| yield compatible_table_definition(t) }
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def change_table(table_name, **options)
|
41
|
+
if block_given?
|
42
|
+
super { |t| yield compatible_table_definition(t) }
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_join_table(table_1, table_2, **options)
|
49
|
+
if block_given?
|
50
|
+
super { |t| yield compatible_table_definition(t) }
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_timestamps(table_name, **options)
|
57
|
+
options[:precision] ||= nil
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
25
61
|
private
|
62
|
+
def compatible_table_definition(t)
|
63
|
+
class << t
|
64
|
+
prepend TableDefinition
|
65
|
+
end
|
66
|
+
t
|
67
|
+
end
|
26
68
|
|
27
69
|
def command_recorder
|
28
70
|
recorder = super
|
@@ -35,20 +77,18 @@ module ActiveRecord
|
|
35
77
|
|
36
78
|
class V5_1 < V5_2
|
37
79
|
def change_column(table_name, column_name, type, options = {})
|
38
|
-
if adapter_name == "PostgreSQL"
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
44
|
-
change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
80
|
+
if connection.adapter_name == "PostgreSQL"
|
81
|
+
super(table_name, column_name, type, options.except(:default, :null, :comment))
|
82
|
+
connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
|
83
|
+
connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
|
84
|
+
connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
|
45
85
|
else
|
46
86
|
super
|
47
87
|
end
|
48
88
|
end
|
49
89
|
|
50
90
|
def create_table(table_name, options = {})
|
51
|
-
if adapter_name == "Mysql2"
|
91
|
+
if connection.adapter_name == "Mysql2"
|
52
92
|
super(table_name, options: "ENGINE=InnoDB", **options)
|
53
93
|
else
|
54
94
|
super
|
@@ -70,13 +110,13 @@ module ActiveRecord
|
|
70
110
|
end
|
71
111
|
|
72
112
|
def create_table(table_name, options = {})
|
73
|
-
if adapter_name == "PostgreSQL"
|
113
|
+
if connection.adapter_name == "PostgreSQL"
|
74
114
|
if options[:id] == :uuid && !options.key?(:default)
|
75
115
|
options[:default] = "uuid_generate_v4()"
|
76
116
|
end
|
77
117
|
end
|
78
118
|
|
79
|
-
unless adapter_name == "Mysql2" && options[:id] == :bigint
|
119
|
+
unless connection.adapter_name == "Mysql2" && options[:id] == :bigint
|
80
120
|
if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
|
81
121
|
options[:default] = nil
|
82
122
|
end
|
@@ -89,35 +129,12 @@ module ActiveRecord
|
|
89
129
|
options[:id] = :integer
|
90
130
|
end
|
91
131
|
|
92
|
-
|
93
|
-
super do |t|
|
94
|
-
yield compatible_table_definition(t)
|
95
|
-
end
|
96
|
-
else
|
97
|
-
super
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def change_table(table_name, options = {})
|
102
|
-
if block_given?
|
103
|
-
super do |t|
|
104
|
-
yield compatible_table_definition(t)
|
105
|
-
end
|
106
|
-
else
|
107
|
-
super
|
108
|
-
end
|
132
|
+
super
|
109
133
|
end
|
110
134
|
|
111
135
|
def create_join_table(table_1, table_2, column_options: {}, **options)
|
112
136
|
column_options.reverse_merge!(type: :integer)
|
113
|
-
|
114
|
-
if block_given?
|
115
|
-
super do |t|
|
116
|
-
yield compatible_table_definition(t)
|
117
|
-
end
|
118
|
-
else
|
119
|
-
super
|
120
|
-
end
|
137
|
+
super
|
121
138
|
end
|
122
139
|
|
123
140
|
def add_column(table_name, column_name, type, options = {})
|
@@ -138,7 +155,7 @@ module ActiveRecord
|
|
138
155
|
class << t
|
139
156
|
prepend TableDefinition
|
140
157
|
end
|
141
|
-
|
158
|
+
super
|
142
159
|
end
|
143
160
|
end
|
144
161
|
|
@@ -156,33 +173,13 @@ module ActiveRecord
|
|
156
173
|
end
|
157
174
|
end
|
158
175
|
|
159
|
-
def
|
160
|
-
if block_given?
|
161
|
-
super do |t|
|
162
|
-
yield compatible_table_definition(t)
|
163
|
-
end
|
164
|
-
else
|
165
|
-
super
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def change_table(table_name, options = {})
|
170
|
-
if block_given?
|
171
|
-
super do |t|
|
172
|
-
yield compatible_table_definition(t)
|
173
|
-
end
|
174
|
-
else
|
175
|
-
super
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def add_reference(*, **options)
|
176
|
+
def add_reference(table_name, ref_name, **options)
|
180
177
|
options[:index] ||= false
|
181
178
|
super
|
182
179
|
end
|
183
180
|
alias :add_belongs_to :add_reference
|
184
181
|
|
185
|
-
def add_timestamps(
|
182
|
+
def add_timestamps(table_name, **options)
|
186
183
|
options[:null] = true if options[:null].nil?
|
187
184
|
super
|
188
185
|
end
|
@@ -193,7 +190,7 @@ module ActiveRecord
|
|
193
190
|
if options[:name].present?
|
194
191
|
options[:name].to_s
|
195
192
|
else
|
196
|
-
index_name(table_name, column: column_names)
|
193
|
+
connection.index_name(table_name, column: column_names)
|
197
194
|
end
|
198
195
|
super
|
199
196
|
end
|
@@ -213,15 +210,17 @@ module ActiveRecord
|
|
213
210
|
end
|
214
211
|
|
215
212
|
def index_name_for_remove(table_name, options = {})
|
216
|
-
index_name = index_name(table_name, options)
|
213
|
+
index_name = connection.index_name(table_name, options)
|
217
214
|
|
218
|
-
unless index_name_exists?(table_name, index_name)
|
215
|
+
unless connection.index_name_exists?(table_name, index_name)
|
219
216
|
if options.is_a?(Hash) && options.has_key?(:name)
|
220
217
|
options_without_column = options.dup
|
221
218
|
options_without_column.delete :column
|
222
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
219
|
+
index_name_without_column = connection.index_name(table_name, options_without_column)
|
223
220
|
|
224
|
-
|
221
|
+
if connection.index_name_exists?(table_name, index_name_without_column)
|
222
|
+
return index_name_without_column
|
223
|
+
end
|
225
224
|
end
|
226
225
|
|
227
226
|
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
@@ -142,7 +142,7 @@ module ActiveRecord
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
|
-
# Deletes the row with a primary key matching the +id+ argument, using
|
145
|
+
# Deletes the row with a primary key matching the +id+ argument, using an
|
146
146
|
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
|
147
147
|
# Record objects are not instantiated, so the object's callbacks are not
|
148
148
|
# executed, including any <tt>:dependent</tt> association options.
|
@@ -161,7 +161,7 @@ module ActiveRecord
|
|
161
161
|
# # Delete multiple rows
|
162
162
|
# Todo.delete([2,3,4])
|
163
163
|
def delete(id_or_array)
|
164
|
-
|
164
|
+
delete_by(primary_key => id_or_array)
|
165
165
|
end
|
166
166
|
|
167
167
|
def _insert_record(values) # :nodoc:
|
@@ -436,7 +436,7 @@ module ActiveRecord
|
|
436
436
|
end
|
437
437
|
|
438
438
|
alias update_attributes update
|
439
|
-
deprecate :
|
439
|
+
deprecate update_attributes: "please, use update instead"
|
440
440
|
|
441
441
|
# Updates its receiver just like #update but calls #save! instead
|
442
442
|
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
@@ -450,7 +450,7 @@ module ActiveRecord
|
|
450
450
|
end
|
451
451
|
|
452
452
|
alias update_attributes! update!
|
453
|
-
deprecate
|
453
|
+
deprecate update_attributes!: "please, use update! instead"
|
454
454
|
|
455
455
|
# Equivalent to <code>update_columns(name => value)</code>.
|
456
456
|
def update_column(name, value)
|
@@ -707,10 +707,10 @@ module ActiveRecord
|
|
707
707
|
)
|
708
708
|
end
|
709
709
|
|
710
|
-
def create_or_update(
|
710
|
+
def create_or_update(**, &block)
|
711
711
|
_raise_readonly_record_error if readonly?
|
712
712
|
return false if destroyed?
|
713
|
-
result = new_record? ? _create_record(&block) : _update_record(
|
713
|
+
result = new_record? ? _create_record(&block) : _update_record(&block)
|
714
714
|
result != false
|
715
715
|
end
|
716
716
|
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
7
7
|
delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all
|
8
8
|
delegate :find_or_create_by, :find_or_create_by!, :create_or_find_by, :create_or_find_by!, :find_or_initialize_by, to: :all
|
9
9
|
delegate :find_by, :find_by!, to: :all
|
10
|
-
delegate :destroy_all, :delete_all, :update_all, to: :all
|
10
|
+
delegate :destroy_all, :delete_all, :update_all, :destroy_by, :delete_by, to: :all
|
11
11
|
delegate :find_each, :find_in_batches, :in_batches, to: :all
|
12
12
|
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or,
|
13
13
|
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending,
|
@@ -40,8 +40,7 @@ module ActiveRecord
|
|
40
40
|
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
41
41
|
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
|
42
42
|
column_types = result_set.column_types.dup
|
43
|
-
|
44
|
-
cached_columns_hash.each_key { |k| column_types.delete k }
|
43
|
+
attribute_types.each_key { |k| column_types.delete k }
|
45
44
|
message_bus = ActiveSupport::Notifications.instrumenter
|
46
45
|
|
47
46
|
payload = {
|
@@ -88,6 +88,14 @@ module ActiveRecord
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
initializer "active_record.database_selector" do
|
92
|
+
if options = config.active_record.delete(:database_selector)
|
93
|
+
resolver = config.active_record.delete(:database_resolver)
|
94
|
+
operations = config.active_record.delete(:database_resolver_context)
|
95
|
+
config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations, options
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
91
99
|
initializer "Check for cache versioning support" do
|
92
100
|
config.after_initialize do |app|
|
93
101
|
ActiveSupport.on_load(:active_record) do
|
@@ -189,6 +197,7 @@ end_error
|
|
189
197
|
# and then establishes the connection.
|
190
198
|
initializer "active_record.initialize_database" do
|
191
199
|
ActiveSupport.on_load(:active_record) do
|
200
|
+
self.connection_handlers = { writing_role => ActiveRecord::Base.default_connection_handler }
|
192
201
|
self.configurations = Rails.application.config.database_configuration
|
193
202
|
establish_connection
|
194
203
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Railties # :nodoc:
|
5
5
|
module CollectionCacheAssociationLoading #:nodoc:
|
6
|
-
def setup(context, options, block)
|
6
|
+
def setup(context, options, as, block)
|
7
7
|
@relation = relation_from_options(options)
|
8
8
|
|
9
9
|
super
|
@@ -20,12 +20,12 @@ module ActiveRecord
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def collection_without_template
|
23
|
+
def collection_without_template(*)
|
24
24
|
@relation.preload_associations(@collection) if @relation
|
25
25
|
super
|
26
26
|
end
|
27
27
|
|
28
|
-
def collection_with_template
|
28
|
+
def collection_with_template(*)
|
29
29
|
@relation.preload_associations(@collection) if @relation
|
30
30
|
super
|
31
31
|
end
|
@@ -178,28 +178,24 @@ module ActiveRecord
|
|
178
178
|
scope ? [scope] : []
|
179
179
|
end
|
180
180
|
|
181
|
-
def
|
182
|
-
key = join_keys.key
|
183
|
-
foreign_key = join_keys.foreign_key
|
184
|
-
|
185
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
186
|
-
|
187
|
-
if klass.finder_needs_type_condition?
|
188
|
-
table.create_and([constraint, klass.send(:type_condition, table)])
|
189
|
-
else
|
190
|
-
constraint
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
def join_scope(table, foreign_klass)
|
181
|
+
def join_scope(table, foreign_table, foreign_klass)
|
195
182
|
predicate_builder = predicate_builder(table)
|
196
183
|
scope_chain_items = join_scopes(table, predicate_builder)
|
197
184
|
klass_scope = klass_join_scope(table, predicate_builder)
|
198
185
|
|
186
|
+
key = join_keys.key
|
187
|
+
foreign_key = join_keys.foreign_key
|
188
|
+
|
189
|
+
klass_scope.where!(table[key].eq(foreign_table[foreign_key]))
|
190
|
+
|
199
191
|
if type
|
200
192
|
klass_scope.where!(type => foreign_klass.polymorphic_name)
|
201
193
|
end
|
202
194
|
|
195
|
+
if klass.finder_needs_type_condition?
|
196
|
+
klass_scope.where!(klass.send(:type_condition, table))
|
197
|
+
end
|
198
|
+
|
203
199
|
scope_chain_items.inject(klass_scope, &:merge!)
|
204
200
|
end
|
205
201
|
|
@@ -612,21 +608,9 @@ module ActiveRecord
|
|
612
608
|
|
613
609
|
# returns either +nil+ or the inverse association name that it finds.
|
614
610
|
def automatic_inverse_of
|
615
|
-
|
611
|
+
if can_find_inverse_of_automatically?(self)
|
612
|
+
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
616
613
|
|
617
|
-
inverse_name_candidates =
|
618
|
-
if options[:as]
|
619
|
-
[options[:as]]
|
620
|
-
else
|
621
|
-
active_record_name = active_record.name.demodulize
|
622
|
-
[active_record_name, ActiveSupport::Inflector.pluralize(active_record_name)]
|
623
|
-
end
|
624
|
-
|
625
|
-
inverse_name_candidates.map! do |candidate|
|
626
|
-
ActiveSupport::Inflector.underscore(candidate).to_sym
|
627
|
-
end
|
628
|
-
|
629
|
-
inverse_name_candidates.detect do |inverse_name|
|
630
614
|
begin
|
631
615
|
reflection = klass._reflect_on_association(inverse_name)
|
632
616
|
rescue NameError
|
@@ -635,7 +619,9 @@ module ActiveRecord
|
|
635
619
|
reflection = false
|
636
620
|
end
|
637
621
|
|
638
|
-
valid_inverse_reflection?(reflection)
|
622
|
+
if valid_inverse_reflection?(reflection)
|
623
|
+
return inverse_name
|
624
|
+
end
|
639
625
|
end
|
640
626
|
end
|
641
627
|
|
@@ -67,6 +67,7 @@ module ActiveRecord
|
|
67
67
|
# user = users.new { |user| user.name = 'Oscar' }
|
68
68
|
# user.name # => Oscar
|
69
69
|
def new(attributes = nil, &block)
|
70
|
+
block = _deprecated_scope_block("new", &block)
|
70
71
|
scoping { klass.new(attributes, &block) }
|
71
72
|
end
|
72
73
|
|
@@ -92,7 +93,12 @@ module ActiveRecord
|
|
92
93
|
# users.create(name: nil) # validation on name
|
93
94
|
# # => #<User id: nil, name: nil, ...>
|
94
95
|
def create(attributes = nil, &block)
|
95
|
-
|
96
|
+
if attributes.is_a?(Array)
|
97
|
+
attributes.collect { |attr| create(attr, &block) }
|
98
|
+
else
|
99
|
+
block = _deprecated_scope_block("create", &block)
|
100
|
+
scoping { klass.create(attributes, &block) }
|
101
|
+
end
|
96
102
|
end
|
97
103
|
|
98
104
|
# Similar to #create, but calls
|
@@ -102,7 +108,12 @@ module ActiveRecord
|
|
102
108
|
# Expects arguments in the same format as
|
103
109
|
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
|
104
110
|
def create!(attributes = nil, &block)
|
105
|
-
|
111
|
+
if attributes.is_a?(Array)
|
112
|
+
attributes.collect { |attr| create!(attr, &block) }
|
113
|
+
else
|
114
|
+
block = _deprecated_scope_block("create!", &block)
|
115
|
+
scoping { klass.create!(attributes, &block) }
|
116
|
+
end
|
106
117
|
end
|
107
118
|
|
108
119
|
def first_or_create(attributes = nil, &block) # :nodoc:
|
@@ -312,12 +323,12 @@ module ActiveRecord
|
|
312
323
|
# Please check unscoped if you want to remove all previous scopes (including
|
313
324
|
# the default_scope) during the execution of a block.
|
314
325
|
def scoping
|
315
|
-
|
326
|
+
already_in_scope? ? yield : _scoping(self) { yield }
|
316
327
|
end
|
317
328
|
|
318
|
-
def _exec_scope(*args, &block) # :nodoc:
|
329
|
+
def _exec_scope(name, *args, &block) # :nodoc:
|
319
330
|
@delegate_to_klass = true
|
320
|
-
instance_exec(*args, &block) || self
|
331
|
+
_scoping(_deprecated_spawn(name)) { instance_exec(*args, &block) || self }
|
321
332
|
ensure
|
322
333
|
@delegate_to_klass = false
|
323
334
|
end
|
@@ -361,6 +372,12 @@ module ActiveRecord
|
|
361
372
|
stmt.wheres = arel.constraints
|
362
373
|
|
363
374
|
if updates.is_a?(Hash)
|
375
|
+
if klass.locking_enabled? &&
|
376
|
+
!updates.key?(klass.locking_column) &&
|
377
|
+
!updates.key?(klass.locking_column.to_sym)
|
378
|
+
attr = arel_attribute(klass.locking_column)
|
379
|
+
updates[attr.name] = _increment_attribute(attr)
|
380
|
+
end
|
364
381
|
stmt.set _substitute_values(updates)
|
365
382
|
else
|
366
383
|
stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
@@ -383,10 +400,7 @@ module ActiveRecord
|
|
383
400
|
updates = {}
|
384
401
|
counters.each do |counter_name, value|
|
385
402
|
attr = arel_attribute(counter_name)
|
386
|
-
|
387
|
-
expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attr), 0)
|
388
|
-
expr = value < 0 ? expr - bind : expr + bind
|
389
|
-
updates[counter_name] = expr.expr
|
403
|
+
updates[attr.name] = _increment_attribute(attr, value)
|
390
404
|
end
|
391
405
|
|
392
406
|
if touch
|
@@ -422,12 +436,7 @@ module ActiveRecord
|
|
422
436
|
# Person.where(name: 'David').touch_all
|
423
437
|
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
424
438
|
def touch_all(*names, time: nil)
|
425
|
-
|
426
|
-
names << { time: time }
|
427
|
-
update_counters(klass.locking_column => 1, touch: names)
|
428
|
-
else
|
429
|
-
update_all klass.touch_attributes_with_time(*names, time: time)
|
430
|
-
end
|
439
|
+
update_all klass.touch_attributes_with_time(*names, time: time)
|
431
440
|
end
|
432
441
|
|
433
442
|
# Destroys the records by instantiating each
|
@@ -496,6 +505,32 @@ module ActiveRecord
|
|
496
505
|
affected
|
497
506
|
end
|
498
507
|
|
508
|
+
# Finds and destroys all records matching the specified conditions.
|
509
|
+
# This is short-hand for <tt>relation.where(condition).destroy_all</tt>.
|
510
|
+
# Returns the collection of objects that were destroyed.
|
511
|
+
#
|
512
|
+
# If no record is found, returns empty array.
|
513
|
+
#
|
514
|
+
# Person.destroy_by(id: 13)
|
515
|
+
# Person.destroy_by(name: 'Spartacus', rating: 4)
|
516
|
+
# Person.destroy_by("published_at < ?", 2.weeks.ago)
|
517
|
+
def destroy_by(*args)
|
518
|
+
where(*args).destroy_all
|
519
|
+
end
|
520
|
+
|
521
|
+
# Finds and deletes all records matching the specified conditions.
|
522
|
+
# This is short-hand for <tt>relation.where(condition).delete_all</tt>.
|
523
|
+
# Returns the number of rows affected.
|
524
|
+
#
|
525
|
+
# If no record is found, returns <tt>0</tt> as zero rows were affected.
|
526
|
+
#
|
527
|
+
# Person.delete_by(id: 13)
|
528
|
+
# Person.delete_by(name: 'Spartacus', rating: 4)
|
529
|
+
# Person.delete_by("published_at < ?", 2.weeks.ago)
|
530
|
+
def delete_by(*args)
|
531
|
+
where(*args).delete_all
|
532
|
+
end
|
533
|
+
|
499
534
|
# Causes the records to be loaded from the database if they have not
|
500
535
|
# been loaded already. You can use this if for some reason you need
|
501
536
|
# to explicitly load some records before actually using them. The
|
@@ -516,6 +551,7 @@ module ActiveRecord
|
|
516
551
|
|
517
552
|
def reset
|
518
553
|
@delegate_to_klass = false
|
554
|
+
@_deprecated_scope_source = nil
|
519
555
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
520
556
|
@records = [].freeze
|
521
557
|
@offsets = {}
|
@@ -624,7 +660,10 @@ module ActiveRecord
|
|
624
660
|
end
|
625
661
|
end
|
626
662
|
|
663
|
+
attr_reader :_deprecated_scope_source # :nodoc:
|
664
|
+
|
627
665
|
protected
|
666
|
+
attr_writer :_deprecated_scope_source # :nodoc:
|
628
667
|
|
629
668
|
def load_records(records)
|
630
669
|
@records = records.freeze
|
@@ -632,6 +671,31 @@ module ActiveRecord
|
|
632
671
|
end
|
633
672
|
|
634
673
|
private
|
674
|
+
def already_in_scope?
|
675
|
+
@delegate_to_klass && begin
|
676
|
+
scope = klass.current_scope(true)
|
677
|
+
scope && !scope._deprecated_scope_source
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
def _deprecated_spawn(name)
|
682
|
+
spawn.tap { |scope| scope._deprecated_scope_source = name }
|
683
|
+
end
|
684
|
+
|
685
|
+
def _deprecated_scope_block(name, &block)
|
686
|
+
-> record do
|
687
|
+
klass.current_scope = _deprecated_spawn(name)
|
688
|
+
yield record if block_given?
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
def _scoping(scope)
|
693
|
+
previous, klass.current_scope = klass.current_scope(true), scope
|
694
|
+
yield
|
695
|
+
ensure
|
696
|
+
klass.current_scope = previous
|
697
|
+
end
|
698
|
+
|
635
699
|
def _substitute_values(values)
|
636
700
|
values.map do |name, value|
|
637
701
|
attr = arel_attribute(name)
|
@@ -643,6 +707,13 @@ module ActiveRecord
|
|
643
707
|
end
|
644
708
|
end
|
645
709
|
|
710
|
+
def _increment_attribute(attribute, value = 1)
|
711
|
+
bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
|
712
|
+
expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
|
713
|
+
expr = value < 0 ? expr - bind : expr + bind
|
714
|
+
expr.expr
|
715
|
+
end
|
716
|
+
|
646
717
|
def exec_queries(&block)
|
647
718
|
skip_query_cache_if_necessary do
|
648
719
|
@records =
|