activerecord 4.2.11.1 → 5.2.4.5
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 +594 -1620
- data/MIT-LICENSE +2 -2
- data/README.rdoc +10 -11
- data/examples/performance.rb +32 -31
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +263 -249
- data/lib/active_record/association_relation.rb +11 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +77 -43
- data/lib/active_record/associations/association_scope.rb +106 -133
- data/lib/active_record/associations/belongs_to_association.rb +52 -41
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
- data/lib/active_record/associations/builder/association.rb +29 -38
- data/lib/active_record/associations/builder/belongs_to.rb +77 -30
- data/lib/active_record/associations/builder/collection_association.rb +9 -22
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
- data/lib/active_record/associations/builder/has_many.rb +6 -4
- data/lib/active_record/associations/builder/has_one.rb +13 -6
- data/lib/active_record/associations/builder/singular_association.rb +15 -11
- data/lib/active_record/associations/collection_association.rb +139 -280
- data/lib/active_record/associations/collection_proxy.rb +231 -133
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_many_association.rb +34 -89
- data/lib/active_record/associations/has_many_through_association.rb +49 -76
- data/lib/active_record/associations/has_one_association.rb +38 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -87
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
- data/lib/active_record/associations/join_dependency.rb +133 -159
- data/lib/active_record/associations/preloader/association.rb +85 -120
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +81 -91
- data/lib/active_record/associations/singular_association.rb +27 -34
- data/lib/active_record/associations/through_association.rb +38 -18
- data/lib/active_record/associations.rb +1732 -1597
- data/lib/active_record/attribute_assignment.rb +58 -182
- data/lib/active_record/attribute_decorators.rb +39 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +10 -8
- data/lib/active_record/attribute_methods/dirty.rb +94 -135
- data/lib/active_record/attribute_methods/primary_key.rb +86 -71
- data/lib/active_record/attribute_methods/query.rb +4 -2
- data/lib/active_record/attribute_methods/read.rb +45 -63
- data/lib/active_record/attribute_methods/serialization.rb +40 -20
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +58 -36
- data/lib/active_record/attribute_methods/write.rb +30 -45
- data/lib/active_record/attribute_methods.rb +166 -109
- data/lib/active_record/attributes.rb +201 -82
- data/lib/active_record/autosave_association.rb +94 -36
- data/lib/active_record/base.rb +57 -44
- data/lib/active_record/callbacks.rb +97 -57
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +24 -12
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
- data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +570 -228
- data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
- data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -601
- data/lib/active_record/connection_adapters/column.rb +50 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +41 -180
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -22
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -284
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +432 -323
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -308
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +40 -27
- data/lib/active_record/core.rb +178 -198
- data/lib/active_record/counter_cache.rb +79 -36
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +135 -88
- data/lib/active_record/errors.rb +179 -52
- data/lib/active_record/explain.rb +23 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +188 -132
- data/lib/active_record/gem_version.rb +5 -3
- data/lib/active_record/inheritance.rb +148 -112
- data/lib/active_record/integration.rb +70 -28
- data/lib/active_record/internal_metadata.rb +45 -0
- data/lib/active_record/legacy_yaml_adapter.rb +21 -3
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +88 -96
- data/lib/active_record/locking/pessimistic.rb +15 -3
- data/lib/active_record/log_subscriber.rb +95 -33
- data/lib/active_record/migration/command_recorder.rb +133 -90
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +8 -6
- data/lib/active_record/migration.rb +581 -282
- data/lib/active_record/model_schema.rb +290 -111
- data/lib/active_record/nested_attributes.rb +264 -222
- data/lib/active_record/no_touching.rb +7 -1
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +347 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +94 -32
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +9 -3
- data/lib/active_record/railties/databases.rake +149 -156
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +414 -267
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +204 -55
- data/lib/active_record/relation/calculations.rb +256 -248
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +288 -239
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +86 -86
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +116 -119
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +448 -393
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -13
- data/lib/active_record/relation/where_clause.rb +186 -0
- data/lib/active_record/relation/where_clause_factory.rb +34 -0
- data/lib/active_record/relation.rb +287 -340
- data/lib/active_record/result.rb +54 -36
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +155 -124
- data/lib/active_record/schema.rb +30 -24
- data/lib/active_record/schema_dumper.rb +91 -87
- data/lib/active_record/schema_migration.rb +19 -16
- data/lib/active_record/scoping/default.rb +102 -85
- data/lib/active_record/scoping/named.rb +81 -32
- data/lib/active_record/scoping.rb +45 -26
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +5 -5
- data/lib/active_record/statement_cache.rb +45 -35
- data/lib/active_record/store.rb +42 -36
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +82 -0
- data/lib/active_record/tasks/database_tasks.rb +134 -96
- data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
- data/lib/active_record/timestamp.rb +70 -38
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +199 -124
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +136 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +24 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +11 -16
- data/lib/active_record/type/type_map.rb +15 -17
- data/lib/active_record/type/unsigned_integer.rb +9 -7
- data/lib/active_record/type.rb +79 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +13 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +40 -41
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +34 -22
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -3
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
- data/lib/rails/generators/active_record/migration.rb +18 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +72 -49
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- 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/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- 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/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/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Associations
|
3
5
|
# = Active Record Association Collection
|
@@ -10,9 +12,9 @@ module ActiveRecord
|
|
10
12
|
# HasManyAssociation => has_many
|
11
13
|
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
12
14
|
#
|
13
|
-
# CollectionAssociation class provides common methods to the collections
|
15
|
+
# The CollectionAssociation class provides common methods to the collections
|
14
16
|
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
15
|
-
# +:through association+ option.
|
17
|
+
# the +:through association+ option.
|
16
18
|
#
|
17
19
|
# You need to be careful with assumptions regarding the target: The proxy
|
18
20
|
# does not fetch records from the database until it needs them, but new
|
@@ -24,22 +26,14 @@ module ActiveRecord
|
|
24
26
|
# If you need to work on all current children, new and existing records,
|
25
27
|
# +load_target+ and the +loaded+ flag are your friends.
|
26
28
|
class CollectionAssociation < Association #:nodoc:
|
27
|
-
|
28
29
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
29
|
-
def reader
|
30
|
-
if
|
31
|
-
klass.uncached { reload }
|
32
|
-
elsif stale_target?
|
30
|
+
def reader
|
31
|
+
if stale_target?
|
33
32
|
reload
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
# or else a post-save proxy will still lack the id
|
39
|
-
@new_record_proxy ||= CollectionProxy.create(klass, self)
|
40
|
-
else
|
41
|
-
@proxy ||= CollectionProxy.create(klass, self)
|
42
|
-
end
|
35
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
36
|
+
@proxy.reset_scope
|
43
37
|
end
|
44
38
|
|
45
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -50,103 +44,60 @@ module ActiveRecord
|
|
50
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
51
45
|
def ids_reader
|
52
46
|
if loaded?
|
53
|
-
|
54
|
-
|
55
|
-
|
47
|
+
target.pluck(reflection.association_primary_key)
|
48
|
+
elsif !target.empty?
|
49
|
+
load_target.pluck(reflection.association_primary_key)
|
56
50
|
else
|
57
|
-
|
58
|
-
scope.pluck(column)
|
51
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
59
52
|
end
|
60
53
|
end
|
61
54
|
|
62
55
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
56
|
def ids_writer(ids)
|
64
|
-
|
65
|
-
pk_type = klass.type_for_attribute(
|
66
|
-
ids = Array(ids).reject(&:blank?)
|
67
|
-
|
68
|
-
end
|
57
|
+
primary_key = reflection.association_primary_key
|
58
|
+
pk_type = klass.type_for_attribute(primary_key)
|
59
|
+
ids = Array(ids).reject(&:blank?)
|
60
|
+
ids.map! { |i| pk_type.cast(i) }
|
69
61
|
|
70
|
-
|
71
|
-
r.
|
62
|
+
records = klass.where(primary_key => ids).index_by do |r|
|
63
|
+
r.public_send(primary_key)
|
72
64
|
end.values_at(*ids).compact
|
73
65
|
|
74
|
-
if
|
75
|
-
|
66
|
+
if records.size != ids.size
|
67
|
+
found_ids = records.map { |record| record.public_send(primary_key) }
|
68
|
+
not_found_ids = ids - found_ids
|
69
|
+
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
|
76
70
|
else
|
77
|
-
|
71
|
+
replace(records)
|
78
72
|
end
|
79
73
|
end
|
80
74
|
|
81
75
|
def reset
|
82
76
|
super
|
83
77
|
@target = []
|
84
|
-
|
85
|
-
|
86
|
-
def select(*fields)
|
87
|
-
if block_given?
|
88
|
-
load_target.select.each { |e| yield e }
|
89
|
-
else
|
90
|
-
scope.select(*fields)
|
91
|
-
end
|
78
|
+
@association_ids = nil
|
92
79
|
end
|
93
80
|
|
94
81
|
def find(*args)
|
95
|
-
if
|
96
|
-
|
97
|
-
|
98
|
-
if options[:inverse_of] && loaded?
|
99
|
-
args_flatten = args.flatten
|
100
|
-
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
101
|
-
result = find_by_scan(*args)
|
102
|
-
|
103
|
-
result_size = Array(result).size
|
104
|
-
if !result || result_size != args_flatten.size
|
105
|
-
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
106
|
-
else
|
107
|
-
result
|
108
|
-
end
|
109
|
-
else
|
110
|
-
scope.find(*args)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def first(*args)
|
116
|
-
first_nth_or_last(:first, *args)
|
117
|
-
end
|
118
|
-
|
119
|
-
def second(*args)
|
120
|
-
first_nth_or_last(:second, *args)
|
121
|
-
end
|
82
|
+
if options[:inverse_of] && loaded?
|
83
|
+
args_flatten = args.flatten
|
84
|
+
model = scope.klass
|
122
85
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
def fourth(*args)
|
128
|
-
first_nth_or_last(:fourth, *args)
|
129
|
-
end
|
130
|
-
|
131
|
-
def fifth(*args)
|
132
|
-
first_nth_or_last(:fifth, *args)
|
133
|
-
end
|
86
|
+
if args_flatten.blank?
|
87
|
+
error_message = "Couldn't find #{model.name} without an ID"
|
88
|
+
raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
|
89
|
+
end
|
134
90
|
|
135
|
-
|
136
|
-
first_nth_or_last(:forty_two, *args)
|
137
|
-
end
|
91
|
+
result = find_by_scan(*args)
|
138
92
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
if loaded?
|
145
|
-
n ? target.take(n) : target.first
|
146
|
-
else
|
147
|
-
scope.take(n).tap do |record|
|
148
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
93
|
+
result_size = Array(result).size
|
94
|
+
if !result || result_size != args_flatten.size
|
95
|
+
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
96
|
+
else
|
97
|
+
result
|
149
98
|
end
|
99
|
+
else
|
100
|
+
scope.find(*args)
|
150
101
|
end
|
151
102
|
end
|
152
103
|
|
@@ -154,24 +105,14 @@ module ActiveRecord
|
|
154
105
|
if attributes.is_a?(Array)
|
155
106
|
attributes.collect { |attr| build(attr, &block) }
|
156
107
|
else
|
157
|
-
add_to_target(build_record(attributes))
|
158
|
-
yield(record) if block_given?
|
159
|
-
end
|
108
|
+
add_to_target(build_record(attributes, &block))
|
160
109
|
end
|
161
110
|
end
|
162
111
|
|
163
|
-
|
164
|
-
|
165
|
-
end
|
166
|
-
|
167
|
-
def create!(attributes = {}, &block)
|
168
|
-
_create_record(attributes, true, &block)
|
169
|
-
end
|
170
|
-
|
171
|
-
# Add +records+ to this association. Returns +self+ so method calls may
|
172
|
-
# be chained. Since << flattens its argument list and inserts each record,
|
173
|
-
# +push+ and +concat+ behave identically.
|
112
|
+
# Add +records+ to this association. Since +<<+ flattens its argument list
|
113
|
+
# and inserts each record, +push+ and +concat+ behave identically.
|
174
114
|
def concat(*records)
|
115
|
+
records = records.flatten
|
175
116
|
if owner.new_record?
|
176
117
|
load_target
|
177
118
|
concat_records(records)
|
@@ -214,12 +155,12 @@ module ActiveRecord
|
|
214
155
|
end
|
215
156
|
|
216
157
|
dependent = if dependent
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
158
|
+
dependent
|
159
|
+
elsif options[:dependent] == :destroy
|
160
|
+
:delete_all
|
161
|
+
else
|
162
|
+
options[:dependent]
|
163
|
+
end
|
223
164
|
|
224
165
|
delete_or_nullify_all_records(dependent).tap do
|
225
166
|
reset
|
@@ -237,32 +178,6 @@ module ActiveRecord
|
|
237
178
|
end
|
238
179
|
end
|
239
180
|
|
240
|
-
# Count all records using SQL. Construct options and pass them with
|
241
|
-
# scope to the target class's +count+.
|
242
|
-
def count(column_name = nil, count_options = {})
|
243
|
-
# TODO: Remove count_options argument as soon we remove support to
|
244
|
-
# activerecord-deprecated_finders.
|
245
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
246
|
-
|
247
|
-
relation = scope
|
248
|
-
if association_scope.distinct_value
|
249
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
250
|
-
column_name ||= reflection.klass.primary_key
|
251
|
-
relation = relation.distinct
|
252
|
-
end
|
253
|
-
|
254
|
-
value = relation.count(column_name)
|
255
|
-
|
256
|
-
limit = options[:limit]
|
257
|
-
offset = options[:offset]
|
258
|
-
|
259
|
-
if limit || offset
|
260
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
261
|
-
else
|
262
|
-
value
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
181
|
# Removes +records+ from this association calling +before_remove+ and
|
267
182
|
# +after_remove+ callbacks.
|
268
183
|
#
|
@@ -271,12 +186,7 @@ module ActiveRecord
|
|
271
186
|
# are actually removed from the database, that depends precisely on
|
272
187
|
# +delete_records+. They are in any case removed from the collection.
|
273
188
|
def delete(*records)
|
274
|
-
|
275
|
-
_options = records.extract_options!
|
276
|
-
dependent = _options[:dependent] || options[:dependent]
|
277
|
-
|
278
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
279
|
-
delete_or_destroy(records, dependent)
|
189
|
+
delete_or_destroy(records, options[:dependent])
|
280
190
|
end
|
281
191
|
|
282
192
|
# Deletes the +records+ and removes them from this association calling
|
@@ -285,8 +195,6 @@ module ActiveRecord
|
|
285
195
|
# Note that this method removes records from the database ignoring the
|
286
196
|
# +:dependent+ option.
|
287
197
|
def destroy(*records)
|
288
|
-
return if records.empty?
|
289
|
-
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
290
198
|
delete_or_destroy(records, :destroy)
|
291
199
|
end
|
292
200
|
|
@@ -302,30 +210,17 @@ module ActiveRecord
|
|
302
210
|
# +count_records+, which is a method descendants have to provide.
|
303
211
|
def size
|
304
212
|
if !find_target? || loaded?
|
305
|
-
|
306
|
-
|
307
|
-
else
|
308
|
-
target.size
|
309
|
-
end
|
310
|
-
elsif !loaded? && !association_scope.group_values.empty?
|
213
|
+
target.size
|
214
|
+
elsif !association_scope.group_values.empty?
|
311
215
|
load_target.size
|
312
|
-
elsif !
|
313
|
-
unsaved_records = target.select
|
216
|
+
elsif !association_scope.distinct_value && target.is_a?(Array)
|
217
|
+
unsaved_records = target.select(&:new_record?)
|
314
218
|
unsaved_records.size + count_records
|
315
219
|
else
|
316
220
|
count_records
|
317
221
|
end
|
318
222
|
end
|
319
223
|
|
320
|
-
# Returns the size of the collection calling +size+ on the target.
|
321
|
-
#
|
322
|
-
# If the collection has been already loaded +length+ and +size+ are
|
323
|
-
# equivalent. If not and you are going to need the records anyway this
|
324
|
-
# method will take one less query. Otherwise +size+ is more efficient.
|
325
|
-
def length
|
326
|
-
load_target.size
|
327
|
-
end
|
328
|
-
|
329
224
|
# Returns true if the collection is empty.
|
330
225
|
#
|
331
226
|
# If the collection has been loaded
|
@@ -342,34 +237,6 @@ module ActiveRecord
|
|
342
237
|
end
|
343
238
|
end
|
344
239
|
|
345
|
-
# Returns true if the collections is not empty.
|
346
|
-
# Equivalent to +!collection.empty?+.
|
347
|
-
def any?
|
348
|
-
if block_given?
|
349
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
350
|
-
else
|
351
|
-
!empty?
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
# Returns true if the collection has more than 1 record.
|
356
|
-
# Equivalent to +collection.size > 1+.
|
357
|
-
def many?
|
358
|
-
if block_given?
|
359
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
360
|
-
else
|
361
|
-
size > 1
|
362
|
-
end
|
363
|
-
end
|
364
|
-
|
365
|
-
def distinct
|
366
|
-
seen = {}
|
367
|
-
load_target.find_all do |record|
|
368
|
-
seen[record.id] = true unless seen.key?(record.id)
|
369
|
-
end
|
370
|
-
end
|
371
|
-
alias uniq distinct
|
372
|
-
|
373
240
|
# Replace this collection with +other_array+. This will perform a diff
|
374
241
|
# and delete/add only records that have changed.
|
375
242
|
def replace(other_array)
|
@@ -382,6 +249,8 @@ module ActiveRecord
|
|
382
249
|
replace_common_records_in_memory(other_array, original_target)
|
383
250
|
if other_array != original_target
|
384
251
|
transaction { replace_records(other_array, original_target) }
|
252
|
+
else
|
253
|
+
other_array
|
385
254
|
end
|
386
255
|
end
|
387
256
|
end
|
@@ -414,25 +283,9 @@ module ActiveRecord
|
|
414
283
|
replace_on_target(record, index, skip_callbacks, &block)
|
415
284
|
end
|
416
285
|
|
417
|
-
def
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
if index
|
422
|
-
@target[index] = record
|
423
|
-
else
|
424
|
-
@target << record
|
425
|
-
end
|
426
|
-
|
427
|
-
callback(:after_add, record) unless skip_callbacks
|
428
|
-
set_inverse_instance(record)
|
429
|
-
|
430
|
-
record
|
431
|
-
end
|
432
|
-
|
433
|
-
def scope(opts = {})
|
434
|
-
scope = super()
|
435
|
-
scope.none! if opts.fetch(:nullify, true) && null_scope?
|
286
|
+
def scope
|
287
|
+
scope = super
|
288
|
+
scope.none! if null_scope?
|
436
289
|
scope
|
437
290
|
end
|
438
291
|
|
@@ -440,26 +293,28 @@ module ActiveRecord
|
|
440
293
|
owner.new_record? && !foreign_key_present?
|
441
294
|
end
|
442
295
|
|
296
|
+
def find_from_target?
|
297
|
+
loaded? ||
|
298
|
+
owner.new_record? ||
|
299
|
+
target.any? { |record| record.new_record? || record.changed? }
|
300
|
+
end
|
301
|
+
|
443
302
|
private
|
444
|
-
def get_records
|
445
|
-
return scope.to_a if skip_statement_cache?
|
446
303
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
as = AssociationScope.create { params.bind }
|
451
|
-
target_scope.merge as.scope(self, conn)
|
452
|
-
}
|
453
|
-
end
|
304
|
+
def find_target
|
305
|
+
scope = self.scope
|
306
|
+
return scope.to_a if skip_statement_cache?(scope)
|
454
307
|
|
455
|
-
|
456
|
-
|
457
|
-
|
308
|
+
conn = klass.connection
|
309
|
+
sc = reflection.association_scope_cache(conn, owner) do |params|
|
310
|
+
as = AssociationScope.create { params.bind }
|
311
|
+
target_scope.merge!(as.scope(self))
|
312
|
+
end
|
458
313
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
314
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
315
|
+
sc.execute(binds, conn) do |record|
|
316
|
+
set_inverse_instance(record)
|
317
|
+
end
|
463
318
|
end
|
464
319
|
|
465
320
|
# We have some records loaded from the database (persisted) and some that are
|
@@ -479,7 +334,7 @@ module ActiveRecord
|
|
479
334
|
persisted.map! do |record|
|
480
335
|
if mem_record = memory.delete(record)
|
481
336
|
|
482
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.
|
337
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
483
338
|
mem_record[name] = record[name]
|
484
339
|
end
|
485
340
|
|
@@ -500,28 +355,35 @@ module ActiveRecord
|
|
500
355
|
if attributes.is_a?(Array)
|
501
356
|
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
502
357
|
else
|
358
|
+
record = build_record(attributes, &block)
|
503
359
|
transaction do
|
504
|
-
|
505
|
-
|
506
|
-
insert_record(record, true, raise)
|
360
|
+
result = nil
|
361
|
+
add_to_target(record) do
|
362
|
+
result = insert_record(record, true, raise) {
|
363
|
+
@_was_loaded = loaded?
|
364
|
+
}
|
507
365
|
end
|
366
|
+
raise ActiveRecord::Rollback unless result
|
508
367
|
end
|
368
|
+
record
|
509
369
|
end
|
510
370
|
end
|
511
371
|
|
512
372
|
# Do the relevant stuff to insert the given record into the association collection.
|
513
|
-
def insert_record(record, validate = true, raise = false)
|
514
|
-
raise
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
373
|
+
def insert_record(record, validate = true, raise = false, &block)
|
374
|
+
if raise
|
375
|
+
record.save!(validate: validate, &block)
|
376
|
+
else
|
377
|
+
record.save(validate: validate, &block)
|
378
|
+
end
|
519
379
|
end
|
520
380
|
|
521
381
|
def delete_or_destroy(records, method)
|
382
|
+
return if records.empty?
|
383
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
522
384
|
records = records.flatten
|
523
385
|
records.each { |record| raise_on_type_mismatch!(record) }
|
524
|
-
existing_records = records.reject
|
386
|
+
existing_records = records.reject(&:new_record?)
|
525
387
|
|
526
388
|
if existing_records.empty?
|
527
389
|
remove_records(existing_records, records, method)
|
@@ -535,20 +397,22 @@ module ActiveRecord
|
|
535
397
|
|
536
398
|
delete_records(existing_records, method) if existing_records.any?
|
537
399
|
records.each { |record| target.delete(record) }
|
400
|
+
@association_ids = nil
|
538
401
|
|
539
402
|
records.each { |record| callback(:after_remove, record) }
|
540
403
|
end
|
541
404
|
|
542
|
-
# Delete the given records from the association,
|
543
|
-
#
|
405
|
+
# Delete the given records from the association,
|
406
|
+
# using one of the methods +:destroy+, +:delete_all+
|
407
|
+
# or +:nullify+ (or +nil+, in which case a default is used).
|
544
408
|
def delete_records(records, method)
|
545
409
|
raise NotImplementedError
|
546
410
|
end
|
547
411
|
|
548
412
|
def replace_records(new_target, original_target)
|
549
|
-
delete(target
|
413
|
+
delete(difference(target, new_target))
|
550
414
|
|
551
|
-
unless concat(new_target
|
415
|
+
unless concat(difference(new_target, target))
|
552
416
|
@target = original_target
|
553
417
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
554
418
|
"new records could not be saved."
|
@@ -558,24 +422,53 @@ module ActiveRecord
|
|
558
422
|
end
|
559
423
|
|
560
424
|
def replace_common_records_in_memory(new_target, original_target)
|
561
|
-
common_records = new_target
|
425
|
+
common_records = intersection(new_target, original_target)
|
562
426
|
common_records.each do |record|
|
563
427
|
skip_callbacks = true
|
564
428
|
replace_on_target(record, @target.index(record), skip_callbacks)
|
565
429
|
end
|
566
430
|
end
|
567
431
|
|
568
|
-
def concat_records(records,
|
432
|
+
def concat_records(records, raise = false)
|
569
433
|
result = true
|
570
434
|
|
571
|
-
records.
|
435
|
+
records.each do |record|
|
572
436
|
raise_on_type_mismatch!(record)
|
573
|
-
add_to_target(record) do
|
574
|
-
|
437
|
+
add_to_target(record) do
|
438
|
+
unless owner.new_record?
|
439
|
+
result &&= insert_record(record, true, raise) {
|
440
|
+
@_was_loaded = loaded?
|
441
|
+
}
|
442
|
+
end
|
575
443
|
end
|
576
444
|
end
|
577
445
|
|
578
|
-
|
446
|
+
raise ActiveRecord::Rollback unless result
|
447
|
+
|
448
|
+
records
|
449
|
+
end
|
450
|
+
|
451
|
+
def replace_on_target(record, index, skip_callbacks)
|
452
|
+
callback(:before_add, record) unless skip_callbacks
|
453
|
+
|
454
|
+
set_inverse_instance(record)
|
455
|
+
|
456
|
+
@_was_loaded = true
|
457
|
+
|
458
|
+
yield(record) if block_given?
|
459
|
+
|
460
|
+
if index
|
461
|
+
target[index] = record
|
462
|
+
elsif @_was_loaded || !loaded?
|
463
|
+
@association_ids = nil
|
464
|
+
target << record
|
465
|
+
end
|
466
|
+
|
467
|
+
callback(:after_add, record) unless skip_callbacks
|
468
|
+
|
469
|
+
record
|
470
|
+
ensure
|
471
|
+
@_was_loaded = nil
|
579
472
|
end
|
580
473
|
|
581
474
|
def callback(method, record)
|
@@ -589,36 +482,12 @@ module ActiveRecord
|
|
589
482
|
owner.class.send(full_callback_name)
|
590
483
|
end
|
591
484
|
|
592
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
593
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
594
|
-
#
|
595
|
-
# If the args is just a non-empty options hash, go to the database.
|
596
|
-
#
|
597
|
-
# Otherwise, go to the database only if none of the following are true:
|
598
|
-
# * target already loaded
|
599
|
-
# * owner is new record
|
600
|
-
# * target contains new or changed record(s)
|
601
|
-
def fetch_first_nth_or_last_using_find?(args)
|
602
|
-
if args.first.is_a?(Hash)
|
603
|
-
true
|
604
|
-
else
|
605
|
-
!(loaded? ||
|
606
|
-
owner.new_record? ||
|
607
|
-
target.any? { |record| record.new_record? || record.changed? })
|
608
|
-
end
|
609
|
-
end
|
610
|
-
|
611
485
|
def include_in_memory?(record)
|
612
486
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
613
487
|
assoc = owner.association(reflection.through_reflection.name)
|
614
488
|
assoc.reader.any? { |source|
|
615
|
-
|
616
|
-
|
617
|
-
if target_association.respond_to?(:include?)
|
618
|
-
target_association.include?(record)
|
619
|
-
else
|
620
|
-
target_association == record
|
621
|
-
end
|
489
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
490
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
622
491
|
} || target.include?(record)
|
623
492
|
else
|
624
493
|
target.include?(record)
|
@@ -629,7 +498,7 @@ module ActiveRecord
|
|
629
498
|
# specified, then #find scans the entire collection.
|
630
499
|
def find_by_scan(*args)
|
631
500
|
expects_array = args.first.kind_of?(Array)
|
632
|
-
ids = args.flatten.compact.map
|
501
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
633
502
|
|
634
503
|
if ids.size == 1
|
635
504
|
id = ids.first
|
@@ -639,16 +508,6 @@ module ActiveRecord
|
|
639
508
|
load_target.select { |r| ids.include?(r.id.to_s) }
|
640
509
|
end
|
641
510
|
end
|
642
|
-
|
643
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
644
|
-
def first_nth_or_last(type, *args)
|
645
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
646
|
-
|
647
|
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
|
648
|
-
collection.send(type, *args).tap do |record|
|
649
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
650
|
-
end
|
651
|
-
end
|
652
511
|
end
|
653
512
|
end
|
654
513
|
end
|