activerecord 4.2.11.3 → 5.0.0.beta1
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 +5 -5
- data/CHANGELOG.md +1029 -1349
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/examples/performance.rb +2 -2
- data/lib/active_record.rb +7 -3
- data/lib/active_record/aggregations.rb +35 -25
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations.rb +305 -204
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +10 -8
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +20 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +41 -18
- data/lib/active_record/associations/builder/collection_association.rb +8 -24
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +10 -5
- data/lib/active_record/associations/builder/singular_association.rb +2 -9
- data/lib/active_record/associations/collection_association.rb +40 -43
- data/lib/active_record/associations/collection_proxy.rb +55 -29
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -52
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +28 -18
- data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
- data/lib/active_record/associations/preloader.rb +13 -4
- data/lib/active_record/associations/preloader/association.rb +45 -51
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +5 -4
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +61 -17
- data/lib/active_record/attribute/user_provided_default.rb +23 -0
- data/lib/active_record/attribute_assignment.rb +27 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +79 -26
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +26 -42
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
- data/lib/active_record/attribute_methods/write.rb +13 -24
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +194 -81
- data/lib/active_record/autosave_association.rb +33 -15
- data/lib/active_record/base.rb +30 -18
- data/lib/active_record/callbacks.rb +36 -40
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
- data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
- data/lib/active_record/connection_adapters/column.rb +27 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
- data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +23 -16
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -11
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
- data/lib/active_record/connection_handling.rb +5 -5
- data/lib/active_record/core.rb +72 -104
- data/lib/active_record/counter_cache.rb +9 -20
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +110 -76
- data/lib/active_record/errors.rb +72 -47
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +19 -4
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +27 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +10 -14
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +40 -22
- data/lib/active_record/migration.rb +304 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +90 -0
- data/lib/active_record/model_schema.rb +92 -40
- data/lib/active_record/nested_attributes.rb +45 -34
- data/lib/active_record/null_relation.rb +15 -7
- data/lib/active_record/persistence.rb +112 -72
- data/lib/active_record/querying.rb +6 -5
- data/lib/active_record/railtie.rb +20 -13
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +47 -38
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +182 -57
- data/lib/active_record/relation.rb +152 -100
- data/lib/active_record/relation/batches.rb +133 -33
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +80 -101
- data/lib/active_record/relation/delegation.rb +6 -19
- data/lib/active_record/relation/finder_methods.rb +58 -46
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +13 -42
- data/lib/active_record/relation/predicate_builder.rb +99 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +17 -0
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +274 -238
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -6
- data/lib/active_record/relation/where_clause.rb +173 -0
- data/lib/active_record/relation/where_clause_factory.rb +37 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +94 -65
- data/lib/active_record/schema.rb +23 -22
- data/lib/active_record/schema_dumper.rb +33 -22
- data/lib/active_record/schema_migration.rb +10 -4
- data/lib/active_record/scoping.rb +17 -6
- data/lib/active_record/scoping/default.rb +19 -6
- data/lib/active_record/scoping/named.rb +39 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +15 -13
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +54 -0
- data/lib/active_record/table_metadata.rb +64 -0
- data/lib/active_record/tasks/database_tasks.rb +30 -40
- data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +16 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +9 -14
- data/lib/active_record/type/time.rb +3 -21
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +24 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +36 -0
- data/lib/active_record/validations/presence.rb +12 -12
- data/lib/active_record/validations/uniqueness.rb +24 -21
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +4 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +50 -35
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -1,38 +1,39 @@
|
|
1
|
-
|
2
|
-
require 'arel/collectors/bind'
|
1
|
+
require "arel/collectors/bind"
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
|
-
# = Active Record Relation
|
4
|
+
# = Active Record \Relation
|
6
5
|
class Relation
|
7
6
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
8
|
-
:order, :joins, :
|
7
|
+
:order, :joins, :left_joins, :left_outer_joins, :references,
|
9
8
|
:extending, :unscope]
|
10
9
|
|
11
|
-
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :
|
12
|
-
:reverse_order, :distinct, :create_with
|
10
|
+
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
|
11
|
+
:reverse_order, :distinct, :create_with]
|
12
|
+
CLAUSE_METHODS = [:where, :having, :from]
|
13
13
|
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
|
14
14
|
|
15
|
-
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
|
15
|
+
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
|
16
16
|
|
17
|
+
include Enumerable
|
17
18
|
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
18
19
|
|
19
|
-
attr_reader :table, :klass, :loaded
|
20
|
+
attr_reader :table, :klass, :loaded, :predicate_builder
|
20
21
|
alias :model :klass
|
21
22
|
alias :loaded? :loaded
|
22
23
|
|
23
|
-
def initialize(klass, table, values = {})
|
24
|
+
def initialize(klass, table, predicate_builder, values = {})
|
24
25
|
@klass = klass
|
25
26
|
@table = table
|
26
27
|
@values = values
|
27
28
|
@offsets = {}
|
28
29
|
@loaded = false
|
30
|
+
@predicate_builder = predicate_builder
|
29
31
|
end
|
30
32
|
|
31
33
|
def initialize_copy(other)
|
32
34
|
# This method is a hot spot, so for now, use Hash[] to dup the hash.
|
33
35
|
# https://bugs.ruby-lang.org/issues/7166
|
34
36
|
@values = Hash[@values]
|
35
|
-
@values[:bind] = @values[:bind].dup if @values.key? :bind
|
36
37
|
reset
|
37
38
|
end
|
38
39
|
|
@@ -80,7 +81,7 @@ module ActiveRecord
|
|
80
81
|
end
|
81
82
|
|
82
83
|
relation = scope.where(@klass.primary_key => (id_was || id))
|
83
|
-
bvs = binds + relation.
|
84
|
+
bvs = binds + relation.bound_attributes
|
84
85
|
um = relation
|
85
86
|
.arel
|
86
87
|
.compile_update(substitutes, @klass.primary_key)
|
@@ -94,11 +95,11 @@ module ActiveRecord
|
|
94
95
|
|
95
96
|
def substitute_values(values) # :nodoc:
|
96
97
|
binds = values.map do |arel_attr, value|
|
97
|
-
|
98
|
+
QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name))
|
98
99
|
end
|
99
100
|
|
100
|
-
substitutes = values.
|
101
|
-
[arel_attr,
|
101
|
+
substitutes = values.map do |(arel_attr, _)|
|
102
|
+
[arel_attr, connection.substitute_at(klass.columns_hash[arel_attr.name])]
|
102
103
|
end
|
103
104
|
|
104
105
|
[substitutes, binds]
|
@@ -107,7 +108,7 @@ module ActiveRecord
|
|
107
108
|
# Initializes new record from relation while maintaining the current
|
108
109
|
# scope.
|
109
110
|
#
|
110
|
-
# Expects arguments in the same format as
|
111
|
+
# Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
|
111
112
|
#
|
112
113
|
# users = User.where(name: 'DHH')
|
113
114
|
# user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
|
@@ -125,28 +126,32 @@ module ActiveRecord
|
|
125
126
|
# Tries to create a new record with the same scoped attributes
|
126
127
|
# defined in the relation. Returns the initialized object if validation fails.
|
127
128
|
#
|
128
|
-
# Expects arguments in the same format as
|
129
|
+
# Expects arguments in the same format as
|
130
|
+
# {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
|
129
131
|
#
|
130
132
|
# ==== Examples
|
133
|
+
#
|
131
134
|
# users = User.where(name: 'Oscar')
|
132
|
-
# users.create # #<User id: 3, name: "oscar", ...>
|
135
|
+
# users.create # => #<User id: 3, name: "oscar", ...>
|
133
136
|
#
|
134
137
|
# users.create(name: 'fxn')
|
135
|
-
# users.create # #<User id: 4, name: "fxn", ...>
|
138
|
+
# users.create # => #<User id: 4, name: "fxn", ...>
|
136
139
|
#
|
137
140
|
# users.create { |user| user.name = 'tenderlove' }
|
138
|
-
# # #<User id: 5, name: "tenderlove", ...>
|
141
|
+
# # => #<User id: 5, name: "tenderlove", ...>
|
139
142
|
#
|
140
143
|
# users.create(name: nil) # validation on name
|
141
|
-
# # #<User id: nil, name: nil, ...>
|
144
|
+
# # => #<User id: nil, name: nil, ...>
|
142
145
|
def create(*args, &block)
|
143
146
|
scoping { @klass.create(*args, &block) }
|
144
147
|
end
|
145
148
|
|
146
|
-
# Similar to #create, but calls
|
147
|
-
#
|
149
|
+
# Similar to #create, but calls
|
150
|
+
# {create!}[rdoc-ref:Persistence::ClassMethods#create!]
|
151
|
+
# on the base class. Raises an exception if a validation error occurs.
|
148
152
|
#
|
149
|
-
# Expects arguments in the same format as
|
153
|
+
# Expects arguments in the same format as
|
154
|
+
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
|
150
155
|
def create!(*args, &block)
|
151
156
|
scoping { @klass.create!(*args, &block) }
|
152
157
|
end
|
@@ -180,7 +185,7 @@ module ActiveRecord
|
|
180
185
|
# User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
|
181
186
|
# # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
|
182
187
|
#
|
183
|
-
# This method accepts a block, which is passed down to
|
188
|
+
# This method accepts a block, which is passed down to #create. The last example
|
184
189
|
# above can be alternatively written this way:
|
185
190
|
#
|
186
191
|
# # Find the first user named "Scarlett" or create a new one with a
|
@@ -192,7 +197,7 @@ module ActiveRecord
|
|
192
197
|
#
|
193
198
|
# This method always returns a record, but if creation was attempted and
|
194
199
|
# failed due to validation errors it won't be persisted, you get what
|
195
|
-
#
|
200
|
+
# #create returns in such situation.
|
196
201
|
#
|
197
202
|
# Please note *this method is not atomic*, it runs first a SELECT, and if
|
198
203
|
# there are no results an INSERT is attempted. If there are other threads
|
@@ -204,7 +209,9 @@ module ActiveRecord
|
|
204
209
|
# constraint an exception may be raised, just retry:
|
205
210
|
#
|
206
211
|
# begin
|
207
|
-
# CreditAccount.
|
212
|
+
# CreditAccount.transaction(requires_new: true) do
|
213
|
+
# CreditAccount.find_or_create_by(user_id: user.id)
|
214
|
+
# end
|
208
215
|
# rescue ActiveRecord::RecordNotUnique
|
209
216
|
# retry
|
210
217
|
# end
|
@@ -213,13 +220,15 @@ module ActiveRecord
|
|
213
220
|
find_by(attributes) || create(attributes, &block)
|
214
221
|
end
|
215
222
|
|
216
|
-
# Like
|
223
|
+
# Like #find_or_create_by, but calls
|
224
|
+
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
217
225
|
# is raised if the created record is invalid.
|
218
226
|
def find_or_create_by!(attributes, &block)
|
219
227
|
find_by(attributes) || create!(attributes, &block)
|
220
228
|
end
|
221
229
|
|
222
|
-
# Like
|
230
|
+
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
|
231
|
+
# instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
|
223
232
|
def find_or_initialize_by(attributes, &block)
|
224
233
|
find_by(attributes) || new(attributes, &block)
|
225
234
|
end
|
@@ -270,22 +279,54 @@ module ActiveRecord
|
|
270
279
|
end
|
271
280
|
end
|
272
281
|
|
282
|
+
# Returns true if there are no records.
|
283
|
+
def none?
|
284
|
+
return super if block_given?
|
285
|
+
empty?
|
286
|
+
end
|
287
|
+
|
273
288
|
# Returns true if there are any records.
|
274
289
|
def any?
|
275
|
-
if block_given?
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
290
|
+
return super if block_given?
|
291
|
+
!empty?
|
292
|
+
end
|
293
|
+
|
294
|
+
# Returns true if there is exactly one record.
|
295
|
+
def one?
|
296
|
+
return super if block_given?
|
297
|
+
limit_value ? to_a.one? : size == 1
|
280
298
|
end
|
281
299
|
|
282
300
|
# Returns true if there is more than one record.
|
283
301
|
def many?
|
284
|
-
if block_given?
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
302
|
+
return super if block_given?
|
303
|
+
limit_value ? to_a.many? : size > 1
|
304
|
+
end
|
305
|
+
|
306
|
+
# Returns a cache key that can be used to identify the records fetched by
|
307
|
+
# this query. The cache key is built with a fingerprint of the sql query,
|
308
|
+
# the number of records matched by the query and a timestamp of the last
|
309
|
+
# updated record. When a new record comes to match the query, or any of
|
310
|
+
# the existing records is updated or deleted, the cache key changes.
|
311
|
+
#
|
312
|
+
# Product.where("name like ?", "%Cosmic Encounter%").cache_key
|
313
|
+
# # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
|
314
|
+
#
|
315
|
+
# If the collection is loaded, the method will iterate through the records
|
316
|
+
# to generate the timestamp, otherwise it will trigger one SQL query like:
|
317
|
+
#
|
318
|
+
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
319
|
+
#
|
320
|
+
# You can also pass a custom timestamp column to fetch the timestamp of the
|
321
|
+
# last updated record.
|
322
|
+
#
|
323
|
+
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
324
|
+
#
|
325
|
+
# You can customize the strategy to generate the key on a per model basis
|
326
|
+
# overriding ActiveRecord::Base#collection_cache_key.
|
327
|
+
def cache_key(timestamp_column = :updated_at)
|
328
|
+
@cache_keys ||= {}
|
329
|
+
@cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
|
289
330
|
end
|
290
331
|
|
291
332
|
# Scope all queries to the current scope.
|
@@ -306,9 +347,8 @@ module ActiveRecord
|
|
306
347
|
|
307
348
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
308
349
|
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
309
|
-
# trigger Active Record callbacks or validations.
|
310
|
-
#
|
311
|
-
# database.
|
350
|
+
# trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
|
351
|
+
# Active Record's normal type casting and serialization.
|
312
352
|
#
|
313
353
|
# ==== Parameters
|
314
354
|
#
|
@@ -327,7 +367,7 @@ module ActiveRecord
|
|
327
367
|
def update_all(updates)
|
328
368
|
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
329
369
|
|
330
|
-
stmt = Arel::UpdateManager.new
|
370
|
+
stmt = Arel::UpdateManager.new
|
331
371
|
|
332
372
|
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
|
333
373
|
stmt.table(table)
|
@@ -341,8 +381,7 @@ module ActiveRecord
|
|
341
381
|
stmt.wheres = arel.constraints
|
342
382
|
end
|
343
383
|
|
344
|
-
|
345
|
-
@klass.connection.update stmt, 'SQL', bvs
|
384
|
+
@klass.connection.update stmt, 'SQL', bound_attributes
|
346
385
|
end
|
347
386
|
|
348
387
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -361,20 +400,39 @@ module ActiveRecord
|
|
361
400
|
# # Updates multiple records
|
362
401
|
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
|
363
402
|
# Person.update(people.keys, people.values)
|
364
|
-
|
403
|
+
#
|
404
|
+
# # Updates multiple records from the result of a relation
|
405
|
+
# people = Person.where(group: 'expert')
|
406
|
+
# people.update(group: 'masters')
|
407
|
+
#
|
408
|
+
# Note: Updating a large number of records will run an
|
409
|
+
# UPDATE query for each record, which may cause a performance
|
410
|
+
# issue. So if it is not needed to run callbacks for each update, it is
|
411
|
+
# preferred to use #update_all for updating all records using
|
412
|
+
# a single query.
|
413
|
+
def update(id = :all, attributes)
|
365
414
|
if id.is_a?(Array)
|
366
415
|
id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
|
416
|
+
elsif id == :all
|
417
|
+
to_a.each { |record| record.update(attributes) }
|
367
418
|
else
|
419
|
+
if ActiveRecord::Base === id
|
420
|
+
id = id.id
|
421
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
422
|
+
You are passing an instance of ActiveRecord::Base to `update`.
|
423
|
+
Please pass the id of the object by calling `.id`
|
424
|
+
MSG
|
425
|
+
end
|
368
426
|
object = find(id)
|
369
427
|
object.update(attributes)
|
370
428
|
object
|
371
429
|
end
|
372
430
|
end
|
373
431
|
|
374
|
-
# Destroys the records
|
375
|
-
# record and calling its
|
376
|
-
# executed (including <tt>:dependent</tt> association options).
|
377
|
-
# collection of objects that were destroyed; each will be frozen, to
|
432
|
+
# Destroys the records by instantiating each
|
433
|
+
# record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
|
434
|
+
# Each object's callbacks are executed (including <tt>:dependent</tt> association options).
|
435
|
+
# Returns the collection of objects that were destroyed; each will be frozen, to
|
378
436
|
# reflect that no changes should be made (since they can't be persisted).
|
379
437
|
#
|
380
438
|
# Note: Instantiation, callback execution, and deletion of each
|
@@ -382,31 +440,26 @@ module ActiveRecord
|
|
382
440
|
# once. It generates at least one SQL +DELETE+ query per record (or
|
383
441
|
# possibly more, to enforce your callbacks). If you want to delete many
|
384
442
|
# rows quickly, without concern for their associations or callbacks, use
|
385
|
-
#
|
386
|
-
#
|
387
|
-
# ==== Parameters
|
388
|
-
#
|
389
|
-
# * +conditions+ - A string, array, or hash that specifies which records
|
390
|
-
# to destroy. If omitted, all records are destroyed. See the
|
391
|
-
# Conditions section in the introduction to ActiveRecord::Base for
|
392
|
-
# more information.
|
443
|
+
# #delete_all instead.
|
393
444
|
#
|
394
445
|
# ==== Examples
|
395
446
|
#
|
396
|
-
# Person.destroy_all("last_login < '2004-04-04'")
|
397
|
-
# Person.destroy_all(status: "inactive")
|
398
447
|
# Person.where(age: 0..18).destroy_all
|
399
448
|
def destroy_all(conditions = nil)
|
400
449
|
if conditions
|
450
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
451
|
+
Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1.
|
452
|
+
To achieve the same use where(conditions).destroy_all
|
453
|
+
MESSAGE
|
401
454
|
where(conditions).destroy_all
|
402
455
|
else
|
403
|
-
to_a.each
|
456
|
+
to_a.each(&:destroy).tap { reset }
|
404
457
|
end
|
405
458
|
end
|
406
459
|
|
407
460
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
408
461
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
409
|
-
# less efficient than
|
462
|
+
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
410
463
|
#
|
411
464
|
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
412
465
|
# from the attributes, and then calls destroy on it.
|
@@ -431,22 +484,21 @@ module ActiveRecord
|
|
431
484
|
end
|
432
485
|
end
|
433
486
|
|
434
|
-
# Deletes the records
|
435
|
-
# first, and hence not calling the
|
436
|
-
#
|
437
|
-
#
|
487
|
+
# Deletes the records without instantiating the records
|
488
|
+
# first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
|
489
|
+
# method nor invoking callbacks.
|
490
|
+
# This is a single SQL DELETE statement that goes straight to the database, much more
|
491
|
+
# efficient than #destroy_all. Be careful with relations though, in particular
|
438
492
|
# <tt>:dependent</tt> rules defined on associations are not honored. Returns the
|
439
493
|
# number of rows affected.
|
440
494
|
#
|
441
|
-
# Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
|
442
|
-
# Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
|
443
495
|
# Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
|
444
496
|
#
|
445
497
|
# Both calls delete the affected posts all at once with a single DELETE statement.
|
446
498
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
447
|
-
# +after_destroy+ callbacks, use the
|
499
|
+
# +after_destroy+ callbacks, use the #destroy_all method instead.
|
448
500
|
#
|
449
|
-
# If an invalid method is supplied,
|
501
|
+
# If an invalid method is supplied, #delete_all raises an ActiveRecordError:
|
450
502
|
#
|
451
503
|
# Post.limit(100).delete_all
|
452
504
|
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
@@ -454,8 +506,10 @@ module ActiveRecord
|
|
454
506
|
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
|
455
507
|
if MULTI_VALUE_METHODS.include?(method)
|
456
508
|
send("#{method}_values").any?
|
457
|
-
|
509
|
+
elsif SINGLE_VALUE_METHODS.include?(method)
|
458
510
|
send("#{method}_value")
|
511
|
+
elsif CLAUSE_METHODS.include?(method)
|
512
|
+
send("#{method}_clause").any?
|
459
513
|
end
|
460
514
|
}
|
461
515
|
if invalid_methods.any?
|
@@ -463,9 +517,13 @@ module ActiveRecord
|
|
463
517
|
end
|
464
518
|
|
465
519
|
if conditions
|
520
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
521
|
+
Passing conditions to delete_all is deprecated and will be removed in Rails 5.1.
|
522
|
+
To achieve the same use where(conditions).delete_all
|
523
|
+
MESSAGE
|
466
524
|
where(conditions).delete_all
|
467
525
|
else
|
468
|
-
stmt = Arel::DeleteManager.new
|
526
|
+
stmt = Arel::DeleteManager.new
|
469
527
|
stmt.from(table)
|
470
528
|
|
471
529
|
if joins_values.any?
|
@@ -474,8 +532,7 @@ module ActiveRecord
|
|
474
532
|
stmt.wheres = arel.constraints
|
475
533
|
end
|
476
534
|
|
477
|
-
|
478
|
-
affected = @klass.connection.delete(stmt, 'SQL', bvs)
|
535
|
+
affected = @klass.connection.delete(stmt, 'SQL', bound_attributes)
|
479
536
|
|
480
537
|
reset
|
481
538
|
affected
|
@@ -490,7 +547,7 @@ module ActiveRecord
|
|
490
547
|
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
491
548
|
#
|
492
549
|
# Note: Although it is often much faster than the alternative,
|
493
|
-
#
|
550
|
+
# #destroy, skipping callbacks might bypass business logic in
|
494
551
|
# your application that ensures referential integrity or performs other
|
495
552
|
# essential jobs.
|
496
553
|
#
|
@@ -545,10 +602,10 @@ module ActiveRecord
|
|
545
602
|
find_with_associations { |rel| relation = rel }
|
546
603
|
end
|
547
604
|
|
548
|
-
|
549
|
-
binds = (
|
550
|
-
binds.map! { |
|
551
|
-
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
605
|
+
binds = relation.bound_attributes
|
606
|
+
binds = connection.prepare_binds_for_database(binds)
|
607
|
+
binds.map! { |value| connection.quote(value) }
|
608
|
+
collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
|
552
609
|
collect.substitute_binds(binds).join
|
553
610
|
end
|
554
611
|
end
|
@@ -558,22 +615,7 @@ module ActiveRecord
|
|
558
615
|
# User.where(name: 'Oscar').where_values_hash
|
559
616
|
# # => {name: "Oscar"}
|
560
617
|
def where_values_hash(relation_table_name = table_name)
|
561
|
-
|
562
|
-
node.left.relation.name == relation_table_name
|
563
|
-
}
|
564
|
-
|
565
|
-
binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
|
566
|
-
|
567
|
-
Hash[equalities.map { |where|
|
568
|
-
name = where.left.name
|
569
|
-
[name, binds.fetch(name.to_s) {
|
570
|
-
case where.right
|
571
|
-
when Array then where.right.map(&:val)
|
572
|
-
when Arel::Nodes::Casted
|
573
|
-
where.right.val
|
574
|
-
end
|
575
|
-
}]
|
576
|
-
}]
|
618
|
+
where_clause.to_h(relation_table_name)
|
577
619
|
end
|
578
620
|
|
579
621
|
def scope_for_create
|
@@ -595,11 +637,14 @@ module ActiveRecord
|
|
595
637
|
includes_values & joins_values
|
596
638
|
end
|
597
639
|
|
598
|
-
#
|
599
|
-
#
|
640
|
+
# {#uniq}[rdoc-ref:QueryMethods#uniq] and
|
641
|
+
# {#uniq!}[rdoc-ref:QueryMethods#uniq!] are silently deprecated.
|
642
|
+
# #uniq_value delegates to #distinct_value to maintain backwards compatibility.
|
643
|
+
# Use #distinct_value instead.
|
600
644
|
def uniq_value
|
601
645
|
distinct_value
|
602
646
|
end
|
647
|
+
deprecate uniq_value: :distinct_value
|
603
648
|
|
604
649
|
# Compares two relations for equality.
|
605
650
|
def ==(other)
|
@@ -633,10 +678,17 @@ module ActiveRecord
|
|
633
678
|
"#<#{self.class.name} [#{entries.join(', ')}]>"
|
634
679
|
end
|
635
680
|
|
681
|
+
protected
|
682
|
+
|
683
|
+
def load_records(records)
|
684
|
+
@records = records
|
685
|
+
@loaded = true
|
686
|
+
end
|
687
|
+
|
636
688
|
private
|
637
689
|
|
638
690
|
def exec_queries
|
639
|
-
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel,
|
691
|
+
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bound_attributes)
|
640
692
|
|
641
693
|
preload = preload_values
|
642
694
|
preload += includes_values unless eager_loading?
|
@@ -645,7 +697,7 @@ module ActiveRecord
|
|
645
697
|
preloader.preload @records, associations
|
646
698
|
end
|
647
699
|
|
648
|
-
@records.each
|
700
|
+
@records.each(&:readonly!) if readonly_value
|
649
701
|
|
650
702
|
@loaded = true
|
651
703
|
@records
|
@@ -667,7 +719,7 @@ module ActiveRecord
|
|
667
719
|
joined_tables += [table.name, table.table_alias]
|
668
720
|
|
669
721
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
670
|
-
joined_tables = joined_tables.flatten.compact.map
|
722
|
+
joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
|
671
723
|
|
672
724
|
(references_values - joined_tables).any?
|
673
725
|
end
|
@@ -676,7 +728,7 @@ module ActiveRecord
|
|
676
728
|
return [] if string.blank?
|
677
729
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
678
730
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
679
|
-
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map
|
731
|
+
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_']
|
680
732
|
end
|
681
733
|
end
|
682
734
|
end
|