activerecord 4.2.11.3 → 5.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 +5 -5
- data/CHANGELOG.md +1281 -1204
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record/aggregations.rb +35 -24
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +11 -9
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +14 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +49 -41
- data/lib/active_record/associations/collection_proxy.rb +67 -27
- 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 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
- data/lib/active_record/associations/join_dependency.rb +29 -19
- data/lib/active_record/associations/preloader/association.rb +46 -52
- 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 +27 -14
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +68 -18
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- 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 +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_methods.rb +76 -47
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attribute_set.rb +30 -3
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +49 -16
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +29 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -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 +3 -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 -4
- 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 +31 -17
- 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/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- 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 +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -148
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
- 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/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +149 -192
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +89 -107
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +113 -76
- data/lib/active_record/errors.rb +87 -48
- 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 +26 -5
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- 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 +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +363 -133
- data/lib/active_record/model_schema.rb +129 -41
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +121 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +69 -46
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +79 -108
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +163 -81
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -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/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +176 -116
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +57 -43
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- 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 +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +138 -56
- 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 +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +30 -29
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +8 -4
- 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 +8 -1
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +59 -34
- 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
@@ -40,9 +40,9 @@ module ActiveRecord
|
|
40
40
|
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
|
41
41
|
end
|
42
42
|
|
43
|
-
# \Reflection enables
|
44
|
-
#
|
45
|
-
#
|
43
|
+
# \Reflection enables the ability to examine the associations and aggregations of
|
44
|
+
# Active Record classes and objects. This information, for example,
|
45
|
+
# can be used in a form builder that takes an Active Record object
|
46
46
|
# and creates input fields for all of the attributes depending on their type
|
47
47
|
# and displays the associations to other objects.
|
48
48
|
#
|
@@ -62,20 +62,20 @@ module ActiveRecord
|
|
62
62
|
aggregate_reflections[aggregation.to_s]
|
63
63
|
end
|
64
64
|
|
65
|
-
# Returns a Hash of name of the reflection as the key and
|
65
|
+
# Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
|
66
66
|
#
|
67
67
|
# Account.reflections # => {"balance" => AggregateReflection}
|
68
68
|
#
|
69
|
-
# @api public
|
70
69
|
def reflections
|
71
70
|
@__reflections ||= begin
|
72
71
|
ref = {}
|
73
72
|
|
74
73
|
_reflections.each do |name, reflection|
|
75
|
-
|
74
|
+
parent_reflection = reflection.parent_reflection
|
76
75
|
|
77
|
-
if
|
78
|
-
|
76
|
+
if parent_reflection
|
77
|
+
parent_name = parent_reflection.name
|
78
|
+
ref[parent_name.to_s] = parent_reflection
|
79
79
|
else
|
80
80
|
ref[name] = reflection
|
81
81
|
end
|
@@ -95,10 +95,10 @@ module ActiveRecord
|
|
95
95
|
# Account.reflect_on_all_associations # returns an array of all associations
|
96
96
|
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
97
97
|
#
|
98
|
-
# @api public
|
99
98
|
def reflect_on_all_associations(macro = nil)
|
100
99
|
association_reflections = reflections.values
|
101
|
-
|
100
|
+
association_reflections.select! { |reflection| reflection.macro == macro } if macro
|
101
|
+
association_reflections
|
102
102
|
end
|
103
103
|
|
104
104
|
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
@@ -106,31 +106,42 @@ module ActiveRecord
|
|
106
106
|
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
107
107
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
108
108
|
#
|
109
|
-
# @api public
|
110
109
|
def reflect_on_association(association)
|
111
110
|
reflections[association.to_s]
|
112
111
|
end
|
113
112
|
|
114
|
-
# @api private
|
115
113
|
def _reflect_on_association(association) #:nodoc:
|
116
114
|
_reflections[association.to_s]
|
117
115
|
end
|
118
116
|
|
119
117
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
120
|
-
#
|
121
|
-
# @api public
|
122
118
|
def reflect_on_all_autosave_associations
|
123
119
|
reflections.values.select { |reflection| reflection.options[:autosave] }
|
124
120
|
end
|
125
121
|
|
126
|
-
def clear_reflections_cache
|
122
|
+
def clear_reflections_cache # :nodoc:
|
127
123
|
@__reflections = nil
|
128
124
|
end
|
129
125
|
end
|
130
126
|
|
131
|
-
# Holds all the methods that are shared between MacroReflection
|
132
|
-
#
|
127
|
+
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
128
|
+
#
|
129
|
+
# AbstractReflection
|
130
|
+
# MacroReflection
|
131
|
+
# AggregateReflection
|
132
|
+
# AssociationReflection
|
133
|
+
# HasManyReflection
|
134
|
+
# HasOneReflection
|
135
|
+
# BelongsToReflection
|
136
|
+
# HasAndBelongsToManyReflection
|
137
|
+
# ThroughReflection
|
138
|
+
# PolymorphicReflection
|
139
|
+
# RuntimeReflection
|
133
140
|
class AbstractReflection # :nodoc:
|
141
|
+
def through_reflection?
|
142
|
+
false
|
143
|
+
end
|
144
|
+
|
134
145
|
def table_name
|
135
146
|
klass.table_name
|
136
147
|
end
|
@@ -159,17 +170,24 @@ module ActiveRecord
|
|
159
170
|
|
160
171
|
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
161
172
|
|
162
|
-
def join_keys(
|
173
|
+
def join_keys(association_klass)
|
163
174
|
JoinKeys.new(foreign_key, active_record_primary_key)
|
164
175
|
end
|
165
176
|
|
166
|
-
def
|
167
|
-
|
168
|
-
|
169
|
-
without replacement.
|
170
|
-
MSG
|
177
|
+
def constraints
|
178
|
+
scope_chain.flatten
|
179
|
+
end
|
171
180
|
|
172
|
-
|
181
|
+
def counter_cache_column
|
182
|
+
if belongs_to?
|
183
|
+
if options[:counter_cache] == true
|
184
|
+
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
185
|
+
elsif options[:counter_cache]
|
186
|
+
options[:counter_cache].to_s
|
187
|
+
end
|
188
|
+
else
|
189
|
+
options[:counter_cache] ? options[:counter_cache].to_s : "#{name}_count"
|
190
|
+
end
|
173
191
|
end
|
174
192
|
|
175
193
|
def inverse_of
|
@@ -185,17 +203,54 @@ module ActiveRecord
|
|
185
203
|
end
|
186
204
|
end
|
187
205
|
end
|
206
|
+
|
207
|
+
# This shit is nasty. We need to avoid the following situation:
|
208
|
+
#
|
209
|
+
# * An associated record is deleted via record.destroy
|
210
|
+
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
211
|
+
# :counter_cache options which points back at our owner. So they update the
|
212
|
+
# counter cache.
|
213
|
+
# * In which case, we must make sure to *not* update the counter cache, or else
|
214
|
+
# it will be decremented twice.
|
215
|
+
#
|
216
|
+
# Hence this method.
|
217
|
+
def inverse_which_updates_counter_cache
|
218
|
+
return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
|
219
|
+
@inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
|
220
|
+
inverse.counter_cache_column == counter_cache_column
|
221
|
+
end
|
222
|
+
end
|
223
|
+
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
224
|
+
|
225
|
+
def inverse_updates_counter_in_memory?
|
226
|
+
inverse_of && inverse_which_updates_counter_cache == inverse_of
|
227
|
+
end
|
228
|
+
|
229
|
+
# Returns whether a counter cache should be used for this association.
|
230
|
+
#
|
231
|
+
# The counter_cache option must be given on either the owner or inverse
|
232
|
+
# association, and the column must be present on the owner.
|
233
|
+
def has_cached_counter?
|
234
|
+
options[:counter_cache] ||
|
235
|
+
inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
|
236
|
+
!!active_record.columns_hash[counter_cache_column]
|
237
|
+
end
|
238
|
+
|
239
|
+
def counter_must_be_updated_by_has_many?
|
240
|
+
!inverse_updates_counter_in_memory? && has_cached_counter?
|
241
|
+
end
|
242
|
+
|
243
|
+
def alias_candidate(name)
|
244
|
+
"#{plural_name}_#{name}"
|
245
|
+
end
|
246
|
+
|
247
|
+
def chain
|
248
|
+
collect_join_chain
|
249
|
+
end
|
188
250
|
end
|
251
|
+
|
189
252
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
190
253
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
191
|
-
#
|
192
|
-
# MacroReflection
|
193
|
-
# AssociationReflection
|
194
|
-
# AggregateReflection
|
195
|
-
# HasManyReflection
|
196
|
-
# HasOneReflection
|
197
|
-
# BelongsToReflection
|
198
|
-
# ThroughReflection
|
199
254
|
class MacroReflection < AbstractReflection
|
200
255
|
# Returns the name of the macro.
|
201
256
|
#
|
@@ -228,7 +283,7 @@ module ActiveRecord
|
|
228
283
|
def autosave=(autosave)
|
229
284
|
@automatic_inverse_of = false
|
230
285
|
@options[:autosave] = autosave
|
231
|
-
|
286
|
+
parent_reflection = self.parent_reflection
|
232
287
|
if parent_reflection
|
233
288
|
parent_reflection.autosave = autosave
|
234
289
|
end
|
@@ -296,7 +351,7 @@ module ActiveRecord
|
|
296
351
|
end
|
297
352
|
|
298
353
|
attr_reader :type, :foreign_type
|
299
|
-
attr_accessor :parent_reflection #
|
354
|
+
attr_accessor :parent_reflection # Reflection
|
300
355
|
|
301
356
|
def initialize(name, scope, options, active_record)
|
302
357
|
super
|
@@ -343,14 +398,6 @@ module ActiveRecord
|
|
343
398
|
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
|
344
399
|
end
|
345
400
|
|
346
|
-
def counter_cache_column
|
347
|
-
if options[:counter_cache] == true
|
348
|
-
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
349
|
-
elsif options[:counter_cache]
|
350
|
-
options[:counter_cache].to_s
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
401
|
def check_validity!
|
355
402
|
check_validity_of_inverse!
|
356
403
|
end
|
@@ -359,13 +406,10 @@ module ActiveRecord
|
|
359
406
|
return unless scope
|
360
407
|
|
361
408
|
if scope.arity > 0
|
362
|
-
|
409
|
+
raise ArgumentError, <<-MSG.squish
|
363
410
|
The association scope '#{name}' is instance dependent (the scope
|
364
|
-
block takes an argument). Preloading
|
365
|
-
|
366
|
-
passed to the association scope. This will most likely result in
|
367
|
-
broken or incorrect behavior. Joining, Preloading and eager loading
|
368
|
-
of these associations is deprecated and will be removed in the future.
|
411
|
+
block takes an argument). Preloading instance dependent scopes is
|
412
|
+
not supported.
|
369
413
|
MSG
|
370
414
|
end
|
371
415
|
end
|
@@ -385,10 +429,16 @@ module ActiveRecord
|
|
385
429
|
|
386
430
|
# A chain of reflections from this one back to the owner. For more see the explanation in
|
387
431
|
# ThroughReflection.
|
388
|
-
def
|
432
|
+
def collect_join_chain
|
389
433
|
[self]
|
390
434
|
end
|
391
435
|
|
436
|
+
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
437
|
+
# SQL queries on associations.
|
438
|
+
def clear_association_scope_cache # :nodoc:
|
439
|
+
@association_scope_cache.clear
|
440
|
+
end
|
441
|
+
|
392
442
|
def nested?
|
393
443
|
false
|
394
444
|
end
|
@@ -399,6 +449,10 @@ module ActiveRecord
|
|
399
449
|
scope ? [[scope]] : [[]]
|
400
450
|
end
|
401
451
|
|
452
|
+
def has_scope?
|
453
|
+
scope
|
454
|
+
end
|
455
|
+
|
402
456
|
def has_inverse?
|
403
457
|
inverse_name
|
404
458
|
end
|
@@ -444,28 +498,7 @@ module ActiveRecord
|
|
444
498
|
# Returns +true+ if +self+ is a +has_one+ reflection.
|
445
499
|
def has_one?; false; end
|
446
500
|
|
447
|
-
def association_class
|
448
|
-
case macro
|
449
|
-
when :belongs_to
|
450
|
-
if polymorphic?
|
451
|
-
Associations::BelongsToPolymorphicAssociation
|
452
|
-
else
|
453
|
-
Associations::BelongsToAssociation
|
454
|
-
end
|
455
|
-
when :has_many
|
456
|
-
if options[:through]
|
457
|
-
Associations::HasManyThroughAssociation
|
458
|
-
else
|
459
|
-
Associations::HasManyAssociation
|
460
|
-
end
|
461
|
-
when :has_one
|
462
|
-
if options[:through]
|
463
|
-
Associations::HasOneThroughAssociation
|
464
|
-
else
|
465
|
-
Associations::HasOneAssociation
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
501
|
+
def association_class; raise NotImplementedError; end
|
469
502
|
|
470
503
|
def polymorphic?
|
471
504
|
options[:polymorphic]
|
@@ -474,6 +507,18 @@ module ActiveRecord
|
|
474
507
|
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
475
508
|
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
|
476
509
|
|
510
|
+
def add_as_source(seed)
|
511
|
+
seed
|
512
|
+
end
|
513
|
+
|
514
|
+
def add_as_polymorphic_through(reflection, seed)
|
515
|
+
seed + [PolymorphicReflection.new(self, reflection)]
|
516
|
+
end
|
517
|
+
|
518
|
+
def add_as_through(seed)
|
519
|
+
seed + [self]
|
520
|
+
end
|
521
|
+
|
477
522
|
protected
|
478
523
|
|
479
524
|
def actual_source_reflection # FIXME: this is a horrible name
|
@@ -483,14 +528,7 @@ module ActiveRecord
|
|
483
528
|
private
|
484
529
|
|
485
530
|
def calculate_constructable(macro, options)
|
486
|
-
|
487
|
-
when :belongs_to
|
488
|
-
!polymorphic?
|
489
|
-
when :has_one
|
490
|
-
!options[:through]
|
491
|
-
else
|
492
|
-
true
|
493
|
-
end
|
531
|
+
true
|
494
532
|
end
|
495
533
|
|
496
534
|
# Attempts to find the inverse association name automatically.
|
@@ -506,7 +544,7 @@ module ActiveRecord
|
|
506
544
|
end
|
507
545
|
end
|
508
546
|
|
509
|
-
# returns either
|
547
|
+
# returns either false or the inverse association name that it finds.
|
510
548
|
def automatic_inverse_of
|
511
549
|
if can_find_inverse_of_automatically?(self)
|
512
550
|
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
@@ -583,42 +621,66 @@ module ActiveRecord
|
|
583
621
|
end
|
584
622
|
|
585
623
|
class HasManyReflection < AssociationReflection # :nodoc:
|
586
|
-
def initialize(name, scope, options, active_record)
|
587
|
-
super(name, scope, options, active_record)
|
588
|
-
end
|
589
|
-
|
590
624
|
def macro; :has_many; end
|
591
625
|
|
592
626
|
def collection?; true; end
|
593
|
-
end
|
594
627
|
|
595
|
-
|
596
|
-
|
597
|
-
|
628
|
+
def association_class
|
629
|
+
if options[:through]
|
630
|
+
Associations::HasManyThroughAssociation
|
631
|
+
else
|
632
|
+
Associations::HasManyAssociation
|
633
|
+
end
|
598
634
|
end
|
635
|
+
end
|
599
636
|
|
637
|
+
class HasOneReflection < AssociationReflection # :nodoc:
|
600
638
|
def macro; :has_one; end
|
601
639
|
|
602
640
|
def has_one?; true; end
|
603
|
-
end
|
604
641
|
|
605
|
-
|
606
|
-
|
607
|
-
|
642
|
+
def association_class
|
643
|
+
if options[:through]
|
644
|
+
Associations::HasOneThroughAssociation
|
645
|
+
else
|
646
|
+
Associations::HasOneAssociation
|
647
|
+
end
|
608
648
|
end
|
609
649
|
|
650
|
+
private
|
651
|
+
|
652
|
+
def calculate_constructable(macro, options)
|
653
|
+
!options[:through]
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
class BelongsToReflection < AssociationReflection # :nodoc:
|
610
658
|
def macro; :belongs_to; end
|
611
659
|
|
612
660
|
def belongs_to?; true; end
|
613
661
|
|
614
|
-
def
|
615
|
-
|
662
|
+
def association_class
|
663
|
+
if polymorphic?
|
664
|
+
Associations::BelongsToPolymorphicAssociation
|
665
|
+
else
|
666
|
+
Associations::BelongsToAssociation
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
def join_keys(association_klass)
|
671
|
+
key = polymorphic? ? association_primary_key(association_klass) : association_primary_key
|
616
672
|
JoinKeys.new(key, foreign_key)
|
617
673
|
end
|
618
674
|
|
619
675
|
def join_id_for(owner) # :nodoc:
|
620
676
|
owner[foreign_key]
|
621
677
|
end
|
678
|
+
|
679
|
+
private
|
680
|
+
|
681
|
+
def calculate_constructable(macro, options)
|
682
|
+
!polymorphic?
|
683
|
+
end
|
622
684
|
end
|
623
685
|
|
624
686
|
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
@@ -646,6 +708,10 @@ module ActiveRecord
|
|
646
708
|
@source_reflection_name = delegate_reflection.options[:source]
|
647
709
|
end
|
648
710
|
|
711
|
+
def through_reflection?
|
712
|
+
true
|
713
|
+
end
|
714
|
+
|
649
715
|
def klass
|
650
716
|
@klass ||= delegate_reflection.compute_class(class_name)
|
651
717
|
end
|
@@ -704,14 +770,16 @@ module ActiveRecord
|
|
704
770
|
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
|
705
771
|
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
|
706
772
|
#
|
707
|
-
def
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
773
|
+
def collect_join_chain
|
774
|
+
collect_join_reflections [self]
|
775
|
+
end
|
776
|
+
|
777
|
+
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
778
|
+
# SQL queries on associations.
|
779
|
+
def clear_association_scope_cache # :nodoc:
|
780
|
+
delegate_reflection.clear_association_scope_cache
|
781
|
+
source_reflection.clear_association_scope_cache
|
782
|
+
through_reflection.clear_association_scope_cache
|
715
783
|
end
|
716
784
|
|
717
785
|
# Consider the following example:
|
@@ -755,23 +823,19 @@ module ActiveRecord
|
|
755
823
|
end
|
756
824
|
end
|
757
825
|
|
758
|
-
def
|
759
|
-
|
826
|
+
def has_scope?
|
827
|
+
scope || options[:source_type] ||
|
828
|
+
source_reflection.has_scope? ||
|
829
|
+
through_reflection.has_scope?
|
760
830
|
end
|
761
831
|
|
762
|
-
|
763
|
-
|
764
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
765
|
-
ActiveRecord::Base.source_macro is deprecated and will be removed
|
766
|
-
without replacement.
|
767
|
-
MSG
|
768
|
-
|
769
|
-
source_reflection.source_macro
|
832
|
+
def join_keys(association_klass)
|
833
|
+
source_reflection.join_keys(association_klass)
|
770
834
|
end
|
771
835
|
|
772
836
|
# A through association is nested if there would be more than one join table
|
773
837
|
def nested?
|
774
|
-
|
838
|
+
source_reflection.through_reflection? || through_reflection.through_reflection?
|
775
839
|
end
|
776
840
|
|
777
841
|
# We want to use the klass from this reflection, rather than just delegate straight to
|
@@ -801,7 +865,7 @@ module ActiveRecord
|
|
801
865
|
def source_reflection_name # :nodoc:
|
802
866
|
return @source_reflection_name if @source_reflection_name
|
803
867
|
|
804
|
-
names = [name.to_s.singularize, name].collect
|
868
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
805
869
|
names = names.find_all { |n|
|
806
870
|
through_reflection.klass._reflect_on_association(n)
|
807
871
|
}
|
@@ -810,7 +874,7 @@ module ActiveRecord
|
|
810
874
|
example_options = options.dup
|
811
875
|
example_options[:source] = source_reflection_names.first
|
812
876
|
ActiveSupport::Deprecation.warn \
|
813
|
-
"Ambiguous source reflection for through association.
|
877
|
+
"Ambiguous source reflection for through association. Please " \
|
814
878
|
"specify a :source directive on your declaration like:\n" \
|
815
879
|
"\n" \
|
816
880
|
" class #{active_record.name} < ActiveRecord::Base\n" \
|
@@ -865,6 +929,33 @@ module ActiveRecord
|
|
865
929
|
check_validity_of_inverse!
|
866
930
|
end
|
867
931
|
|
932
|
+
def constraints
|
933
|
+
scope_chain = source_reflection.constraints
|
934
|
+
scope_chain << scope if scope
|
935
|
+
scope_chain
|
936
|
+
end
|
937
|
+
|
938
|
+
def add_as_source(seed)
|
939
|
+
collect_join_reflections seed
|
940
|
+
end
|
941
|
+
|
942
|
+
def add_as_polymorphic_through(reflection, seed)
|
943
|
+
collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
|
944
|
+
end
|
945
|
+
|
946
|
+
def add_as_through(seed)
|
947
|
+
collect_join_reflections(seed + [self])
|
948
|
+
end
|
949
|
+
|
950
|
+
def collect_join_reflections(seed)
|
951
|
+
a = source_reflection.add_as_source seed
|
952
|
+
if options[:source_type]
|
953
|
+
through_reflection.add_as_polymorphic_through self, a
|
954
|
+
else
|
955
|
+
through_reflection.add_as_through a
|
956
|
+
end
|
957
|
+
end
|
958
|
+
|
868
959
|
protected
|
869
960
|
|
870
961
|
def actual_source_reflection # FIXME: this is a horrible name
|
@@ -889,5 +980,81 @@ module ActiveRecord
|
|
889
980
|
delegate(*delegate_methods, to: :delegate_reflection)
|
890
981
|
|
891
982
|
end
|
983
|
+
|
984
|
+
class PolymorphicReflection < ThroughReflection # :nodoc:
|
985
|
+
def initialize(reflection, previous_reflection)
|
986
|
+
@reflection = reflection
|
987
|
+
@previous_reflection = previous_reflection
|
988
|
+
end
|
989
|
+
|
990
|
+
def klass
|
991
|
+
@reflection.klass
|
992
|
+
end
|
993
|
+
|
994
|
+
def scope
|
995
|
+
@reflection.scope
|
996
|
+
end
|
997
|
+
|
998
|
+
def table_name
|
999
|
+
@reflection.table_name
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def plural_name
|
1003
|
+
@reflection.plural_name
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
def join_keys(association_klass)
|
1007
|
+
@reflection.join_keys(association_klass)
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
def type
|
1011
|
+
@reflection.type
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
def constraints
|
1015
|
+
@reflection.constraints + [source_type_info]
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
def source_type_info
|
1019
|
+
type = @previous_reflection.foreign_type
|
1020
|
+
source_type = @previous_reflection.options[:source_type]
|
1021
|
+
lambda { |object| where(type => source_type) }
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
class RuntimeReflection < PolymorphicReflection # :nodoc:
|
1026
|
+
attr_accessor :next
|
1027
|
+
|
1028
|
+
def initialize(reflection, association)
|
1029
|
+
@reflection = reflection
|
1030
|
+
@association = association
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
def klass
|
1034
|
+
@association.klass
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
def table_name
|
1038
|
+
klass.table_name
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
def constraints
|
1042
|
+
@reflection.constraints
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
def source_type_info
|
1046
|
+
@reflection.source_type_info
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def alias_candidate(name)
|
1050
|
+
"#{plural_name}_#{name}_join"
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
def alias_name
|
1054
|
+
Arel::Table.new(table_name)
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def all_includes; yield; end
|
1058
|
+
end
|
892
1059
|
end
|
893
1060
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Batches
|
3
|
+
class BatchEnumerator
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(of: 1000, start: nil, finish: nil, relation:) #:nodoc:
|
7
|
+
@of = of
|
8
|
+
@relation = relation
|
9
|
+
@start = start
|
10
|
+
@finish = finish
|
11
|
+
end
|
12
|
+
|
13
|
+
# Looping through a collection of records from the database (using the
|
14
|
+
# +all+ method, for example) is very inefficient since it will try to
|
15
|
+
# instantiate all the objects at once.
|
16
|
+
#
|
17
|
+
# In that case, batch processing methods allow you to work with the
|
18
|
+
# records in batches, thereby greatly reducing memory consumption.
|
19
|
+
#
|
20
|
+
# Person.in_batches.each_record do |person|
|
21
|
+
# person.do_awesome_stuff
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Person.where("age > 21").in_batches(of: 10).each_record do |person|
|
25
|
+
# person.party_all_night!
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# If you do not provide a block to #each_record, it will return an Enumerator
|
29
|
+
# for chaining with other methods:
|
30
|
+
#
|
31
|
+
# Person.in_batches.each_record.with_index do |person, index|
|
32
|
+
# person.award_trophy(index + 1)
|
33
|
+
# end
|
34
|
+
def each_record
|
35
|
+
return to_enum(:each_record) unless block_given?
|
36
|
+
|
37
|
+
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
|
38
|
+
relation.records.each { |record| yield record }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Delegates #delete_all, #update_all, #destroy_all methods to each batch.
|
43
|
+
#
|
44
|
+
# People.in_batches.delete_all
|
45
|
+
# People.where('age < 10').in_batches.destroy_all
|
46
|
+
# People.in_batches.update_all('age = age + 1')
|
47
|
+
[:delete_all, :update_all, :destroy_all].each do |method|
|
48
|
+
define_method(method) do |*args, &block|
|
49
|
+
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false).each do |relation|
|
50
|
+
relation.send(method, *args, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Yields an ActiveRecord::Relation object for each batch of records.
|
56
|
+
#
|
57
|
+
# Person.in_batches.each do |relation|
|
58
|
+
# relation.update_all(awesome: true)
|
59
|
+
# end
|
60
|
+
def each
|
61
|
+
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
|
62
|
+
return enum.each { |relation| yield relation } if block_given?
|
63
|
+
enum
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|