activerecord 4.2.0 → 5.2.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +640 -928
- 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 +264 -247
- data/lib/active_record/association_relation.rb +24 -6
- data/lib/active_record/associations/alias_tracker.rb +29 -35
- data/lib/active_record/associations/association.rb +87 -41
- data/lib/active_record/associations/association_scope.rb +106 -132
- data/lib/active_record/associations/belongs_to_association.rb +55 -36
- 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 +14 -23
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
- 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 +145 -266
- data/lib/active_record/associations/collection_proxy.rb +242 -138
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +35 -75
- data/lib/active_record/associations/has_many_through_association.rb +51 -69
- data/lib/active_record/associations/has_one_association.rb +39 -24
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
- 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 +134 -154
- data/lib/active_record/associations/preloader/association.rb +85 -116
- data/lib/active_record/associations/preloader/through_association.rb +85 -74
- data/lib/active_record/associations/preloader.rb +83 -93
- data/lib/active_record/associations/singular_association.rb +27 -40
- data/lib/active_record/associations/through_association.rb +48 -23
- data/lib/active_record/associations.rb +1732 -1596
- 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 +12 -5
- data/lib/active_record/attribute_methods/dirty.rb +94 -125
- 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 +62 -36
- data/lib/active_record/attribute_methods/write.rb +31 -46
- data/lib/active_record/attribute_methods.rb +170 -117
- data/lib/active_record/attributes.rb +201 -74
- data/lib/active_record/autosave_association.rb +118 -45
- data/lib/active_record/base.rb +60 -48
- 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 +37 -13
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
- data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
- 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 +42 -195
- data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
- 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 +5 -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 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -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 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
- 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 +466 -280
- 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 +439 -330
- 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 -324
- 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 +205 -202
- data/lib/active_record/counter_cache.rb +80 -37
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +87 -105
- data/lib/active_record/enum.rb +136 -90
- data/lib/active_record/errors.rb +180 -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 +11 -6
- data/lib/active_record/fixture_set/file.rb +35 -9
- data/lib/active_record/fixtures.rb +193 -135
- 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 +48 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +92 -98
- 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 +594 -267
- data/lib/active_record/model_schema.rb +292 -111
- data/lib/active_record/nested_attributes.rb +266 -214
- data/lib/active_record/no_touching.rb +8 -2
- data/lib/active_record/null_relation.rb +24 -37
- data/lib/active_record/persistence.rb +350 -119
- data/lib/active_record/query_cache.rb +13 -24
- data/lib/active_record/querying.rb +19 -17
- data/lib/active_record/railtie.rb +117 -35
- 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 +160 -174
- data/lib/active_record/readonly_attributes.rb +5 -4
- data/lib/active_record/reflection.rb +447 -288
- 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 +259 -244
- data/lib/active_record/relation/delegation.rb +67 -60
- data/lib/active_record/relation/finder_methods.rb +290 -253
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +91 -68
- data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
- 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 +118 -92
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +446 -389
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -16
- 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 -339
- 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 -19
- data/lib/active_record/scoping/default.rb +102 -84
- 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 +136 -95
- data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
- data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
- 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 +208 -123
- 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 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
- 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 +30 -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 +41 -32
- data/lib/active_record/validations.rb +38 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +36 -21
- 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 -6
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
- 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.rb +7 -5
- metadata +77 -53
- 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 -149
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- 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 -30
- data/lib/active_record/type/decimal.rb +0 -40
- 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 -55
- 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 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -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,107 +44,75 @@ 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
|
-
|
66
|
-
ids
|
67
|
-
|
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) }
|
61
|
+
|
62
|
+
records = klass.where(primary_key => ids).index_by do |r|
|
63
|
+
r.public_send(primary_key)
|
64
|
+
end.values_at(*ids).compact
|
65
|
+
|
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)
|
70
|
+
else
|
71
|
+
replace(records)
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
75
|
def reset
|
71
76
|
super
|
72
77
|
@target = []
|
73
|
-
|
74
|
-
|
75
|
-
def select(*fields)
|
76
|
-
if block_given?
|
77
|
-
load_target.select.each { |e| yield e }
|
78
|
-
else
|
79
|
-
scope.select(*fields)
|
80
|
-
end
|
78
|
+
@association_ids = nil
|
81
79
|
end
|
82
80
|
|
83
81
|
def find(*args)
|
84
|
-
if
|
85
|
-
|
86
|
-
|
87
|
-
if options[:inverse_of] && loaded?
|
88
|
-
args_flatten = args.flatten
|
89
|
-
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
|
90
|
-
result = find_by_scan(*args)
|
91
|
-
|
92
|
-
result_size = Array(result).size
|
93
|
-
if !result || result_size != args_flatten.size
|
94
|
-
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
|
95
|
-
else
|
96
|
-
result
|
97
|
-
end
|
98
|
-
else
|
99
|
-
scope.find(*args)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def first(*args)
|
105
|
-
first_nth_or_last(:first, *args)
|
106
|
-
end
|
107
|
-
|
108
|
-
def second(*args)
|
109
|
-
first_nth_or_last(:second, *args)
|
110
|
-
end
|
111
|
-
|
112
|
-
def third(*args)
|
113
|
-
first_nth_or_last(:third, *args)
|
114
|
-
end
|
82
|
+
if options[:inverse_of] && loaded?
|
83
|
+
args_flatten = args.flatten
|
84
|
+
model = scope.klass
|
115
85
|
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
119
90
|
|
120
|
-
|
121
|
-
first_nth_or_last(:fifth, *args)
|
122
|
-
end
|
91
|
+
result = find_by_scan(*args)
|
123
92
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
98
|
+
end
|
99
|
+
else
|
100
|
+
scope.find(*args)
|
101
|
+
end
|
130
102
|
end
|
131
103
|
|
132
104
|
def build(attributes = {}, &block)
|
133
105
|
if attributes.is_a?(Array)
|
134
106
|
attributes.collect { |attr| build(attr, &block) }
|
135
107
|
else
|
136
|
-
add_to_target(build_record(attributes))
|
137
|
-
yield(record) if block_given?
|
138
|
-
end
|
108
|
+
add_to_target(build_record(attributes, &block))
|
139
109
|
end
|
140
110
|
end
|
141
111
|
|
142
|
-
|
143
|
-
|
144
|
-
end
|
145
|
-
|
146
|
-
def create!(attributes = {}, &block)
|
147
|
-
_create_record(attributes, true, &block)
|
148
|
-
end
|
149
|
-
|
150
|
-
# Add +records+ to this association. Returns +self+ so method calls may
|
151
|
-
# be chained. Since << flattens its argument list and inserts each record,
|
152
|
-
# +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.
|
153
114
|
def concat(*records)
|
115
|
+
records = records.flatten
|
154
116
|
if owner.new_record?
|
155
117
|
load_target
|
156
118
|
concat_records(records)
|
@@ -193,12 +155,12 @@ module ActiveRecord
|
|
193
155
|
end
|
194
156
|
|
195
157
|
dependent = if dependent
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
158
|
+
dependent
|
159
|
+
elsif options[:dependent] == :destroy
|
160
|
+
:delete_all
|
161
|
+
else
|
162
|
+
options[:dependent]
|
163
|
+
end
|
202
164
|
|
203
165
|
delete_or_nullify_all_records(dependent).tap do
|
204
166
|
reset
|
@@ -216,32 +178,6 @@ module ActiveRecord
|
|
216
178
|
end
|
217
179
|
end
|
218
180
|
|
219
|
-
# Count all records using SQL. Construct options and pass them with
|
220
|
-
# scope to the target class's +count+.
|
221
|
-
def count(column_name = nil, count_options = {})
|
222
|
-
# TODO: Remove count_options argument as soon we remove support to
|
223
|
-
# activerecord-deprecated_finders.
|
224
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
225
|
-
|
226
|
-
relation = scope
|
227
|
-
if association_scope.distinct_value
|
228
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
229
|
-
column_name ||= reflection.klass.primary_key
|
230
|
-
relation = relation.distinct
|
231
|
-
end
|
232
|
-
|
233
|
-
value = relation.count(column_name)
|
234
|
-
|
235
|
-
limit = options[:limit]
|
236
|
-
offset = options[:offset]
|
237
|
-
|
238
|
-
if limit || offset
|
239
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
240
|
-
else
|
241
|
-
value
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
181
|
# Removes +records+ from this association calling +before_remove+ and
|
246
182
|
# +after_remove+ callbacks.
|
247
183
|
#
|
@@ -250,12 +186,7 @@ module ActiveRecord
|
|
250
186
|
# are actually removed from the database, that depends precisely on
|
251
187
|
# +delete_records+. They are in any case removed from the collection.
|
252
188
|
def delete(*records)
|
253
|
-
|
254
|
-
_options = records.extract_options!
|
255
|
-
dependent = _options[:dependent] || options[:dependent]
|
256
|
-
|
257
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
258
|
-
delete_or_destroy(records, dependent)
|
189
|
+
delete_or_destroy(records, options[:dependent])
|
259
190
|
end
|
260
191
|
|
261
192
|
# Deletes the +records+ and removes them from this association calling
|
@@ -264,8 +195,6 @@ module ActiveRecord
|
|
264
195
|
# Note that this method removes records from the database ignoring the
|
265
196
|
# +:dependent+ option.
|
266
197
|
def destroy(*records)
|
267
|
-
return if records.empty?
|
268
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
269
198
|
delete_or_destroy(records, :destroy)
|
270
199
|
end
|
271
200
|
|
@@ -281,30 +210,17 @@ module ActiveRecord
|
|
281
210
|
# +count_records+, which is a method descendants have to provide.
|
282
211
|
def size
|
283
212
|
if !find_target? || loaded?
|
284
|
-
|
285
|
-
|
286
|
-
else
|
287
|
-
target.size
|
288
|
-
end
|
289
|
-
elsif !loaded? && !association_scope.group_values.empty?
|
213
|
+
target.size
|
214
|
+
elsif !association_scope.group_values.empty?
|
290
215
|
load_target.size
|
291
|
-
elsif !
|
292
|
-
unsaved_records = target.select
|
216
|
+
elsif !association_scope.distinct_value && target.is_a?(Array)
|
217
|
+
unsaved_records = target.select(&:new_record?)
|
293
218
|
unsaved_records.size + count_records
|
294
219
|
else
|
295
220
|
count_records
|
296
221
|
end
|
297
222
|
end
|
298
223
|
|
299
|
-
# Returns the size of the collection calling +size+ on the target.
|
300
|
-
#
|
301
|
-
# If the collection has been already loaded +length+ and +size+ are
|
302
|
-
# equivalent. If not and you are going to need the records anyway this
|
303
|
-
# method will take one less query. Otherwise +size+ is more efficient.
|
304
|
-
def length
|
305
|
-
load_target.size
|
306
|
-
end
|
307
|
-
|
308
224
|
# Returns true if the collection is empty.
|
309
225
|
#
|
310
226
|
# If the collection has been loaded
|
@@ -321,34 +237,6 @@ module ActiveRecord
|
|
321
237
|
end
|
322
238
|
end
|
323
239
|
|
324
|
-
# Returns true if the collections is not empty.
|
325
|
-
# Equivalent to +!collection.empty?+.
|
326
|
-
def any?
|
327
|
-
if block_given?
|
328
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
329
|
-
else
|
330
|
-
!empty?
|
331
|
-
end
|
332
|
-
end
|
333
|
-
|
334
|
-
# Returns true if the collection has more than 1 record.
|
335
|
-
# Equivalent to +collection.size > 1+.
|
336
|
-
def many?
|
337
|
-
if block_given?
|
338
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
339
|
-
else
|
340
|
-
size > 1
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
def distinct
|
345
|
-
seen = {}
|
346
|
-
load_target.find_all do |record|
|
347
|
-
seen[record.id] = true unless seen.key?(record.id)
|
348
|
-
end
|
349
|
-
end
|
350
|
-
alias uniq distinct
|
351
|
-
|
352
240
|
# Replace this collection with +other_array+. This will perform a diff
|
353
241
|
# and delete/add only records that have changed.
|
354
242
|
def replace(other_array)
|
@@ -361,6 +249,8 @@ module ActiveRecord
|
|
361
249
|
replace_common_records_in_memory(other_array, original_target)
|
362
250
|
if other_array != original_target
|
363
251
|
transaction { replace_records(other_array, original_target) }
|
252
|
+
else
|
253
|
+
other_array
|
364
254
|
end
|
365
255
|
end
|
366
256
|
end
|
@@ -393,25 +283,9 @@ module ActiveRecord
|
|
393
283
|
replace_on_target(record, index, skip_callbacks, &block)
|
394
284
|
end
|
395
285
|
|
396
|
-
def
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
if index
|
401
|
-
@target[index] = record
|
402
|
-
else
|
403
|
-
@target << record
|
404
|
-
end
|
405
|
-
|
406
|
-
callback(:after_add, record) unless skip_callbacks
|
407
|
-
set_inverse_instance(record)
|
408
|
-
|
409
|
-
record
|
410
|
-
end
|
411
|
-
|
412
|
-
def scope(opts = {})
|
413
|
-
scope = super()
|
414
|
-
scope.none! if opts.fetch(:nullify, true) && null_scope?
|
286
|
+
def scope
|
287
|
+
scope = super
|
288
|
+
scope.none! if null_scope?
|
415
289
|
scope
|
416
290
|
end
|
417
291
|
|
@@ -419,32 +293,28 @@ module ActiveRecord
|
|
419
293
|
owner.new_record? && !foreign_key_present?
|
420
294
|
end
|
421
295
|
|
296
|
+
def find_from_target?
|
297
|
+
loaded? ||
|
298
|
+
owner.new_record? ||
|
299
|
+
target.any? { |record| record.new_record? || record.changed? }
|
300
|
+
end
|
301
|
+
|
422
302
|
private
|
423
|
-
def get_records
|
424
|
-
if reflection.scope_chain.any?(&:any?) ||
|
425
|
-
scope.eager_loading? ||
|
426
|
-
klass.current_scope ||
|
427
|
-
klass.default_scopes.any?
|
428
303
|
|
429
|
-
|
430
|
-
|
304
|
+
def find_target
|
305
|
+
scope = self.scope
|
306
|
+
return scope.to_a if skip_statement_cache?(scope)
|
431
307
|
|
432
|
-
|
433
|
-
|
434
|
-
StatementCache.create(conn) { |params|
|
308
|
+
conn = klass.connection
|
309
|
+
sc = reflection.association_scope_cache(conn, owner) do |params|
|
435
310
|
as = AssociationScope.create { params.bind }
|
436
|
-
target_scope.merge
|
437
|
-
|
438
|
-
end
|
439
|
-
|
440
|
-
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
441
|
-
sc.execute binds, klass, klass.connection
|
442
|
-
end
|
311
|
+
target_scope.merge!(as.scope(self))
|
312
|
+
end
|
443
313
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
314
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
315
|
+
sc.execute(binds, conn) do |record|
|
316
|
+
set_inverse_instance(record)
|
317
|
+
end
|
448
318
|
end
|
449
319
|
|
450
320
|
# We have some records loaded from the database (persisted) and some that are
|
@@ -464,7 +334,7 @@ module ActiveRecord
|
|
464
334
|
persisted.map! do |record|
|
465
335
|
if mem_record = memory.delete(record)
|
466
336
|
|
467
|
-
((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|
|
468
338
|
mem_record[name] = record[name]
|
469
339
|
end
|
470
340
|
|
@@ -485,28 +355,35 @@ module ActiveRecord
|
|
485
355
|
if attributes.is_a?(Array)
|
486
356
|
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
487
357
|
else
|
358
|
+
record = build_record(attributes, &block)
|
488
359
|
transaction do
|
489
|
-
|
490
|
-
|
491
|
-
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
|
+
}
|
492
365
|
end
|
366
|
+
raise ActiveRecord::Rollback unless result
|
493
367
|
end
|
368
|
+
record
|
494
369
|
end
|
495
370
|
end
|
496
371
|
|
497
372
|
# Do the relevant stuff to insert the given record into the association collection.
|
498
|
-
def insert_record(record, validate = true, raise = false)
|
499
|
-
raise
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
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
|
504
379
|
end
|
505
380
|
|
506
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) }
|
507
384
|
records = records.flatten
|
508
385
|
records.each { |record| raise_on_type_mismatch!(record) }
|
509
|
-
existing_records = records.reject
|
386
|
+
existing_records = records.reject(&:new_record?)
|
510
387
|
|
511
388
|
if existing_records.empty?
|
512
389
|
remove_records(existing_records, records, method)
|
@@ -520,20 +397,22 @@ module ActiveRecord
|
|
520
397
|
|
521
398
|
delete_records(existing_records, method) if existing_records.any?
|
522
399
|
records.each { |record| target.delete(record) }
|
400
|
+
@association_ids = nil
|
523
401
|
|
524
402
|
records.each { |record| callback(:after_remove, record) }
|
525
403
|
end
|
526
404
|
|
527
|
-
# Delete the given records from the association,
|
528
|
-
#
|
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).
|
529
408
|
def delete_records(records, method)
|
530
409
|
raise NotImplementedError
|
531
410
|
end
|
532
411
|
|
533
412
|
def replace_records(new_target, original_target)
|
534
|
-
delete(target
|
413
|
+
delete(difference(target, new_target))
|
535
414
|
|
536
|
-
unless concat(new_target
|
415
|
+
unless concat(difference(new_target, target))
|
537
416
|
@target = original_target
|
538
417
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
539
418
|
"new records could not be saved."
|
@@ -543,24 +422,53 @@ module ActiveRecord
|
|
543
422
|
end
|
544
423
|
|
545
424
|
def replace_common_records_in_memory(new_target, original_target)
|
546
|
-
common_records = new_target
|
425
|
+
common_records = intersection(new_target, original_target)
|
547
426
|
common_records.each do |record|
|
548
427
|
skip_callbacks = true
|
549
428
|
replace_on_target(record, @target.index(record), skip_callbacks)
|
550
429
|
end
|
551
430
|
end
|
552
431
|
|
553
|
-
def concat_records(records,
|
432
|
+
def concat_records(records, raise = false)
|
554
433
|
result = true
|
555
434
|
|
556
|
-
records.
|
435
|
+
records.each do |record|
|
557
436
|
raise_on_type_mismatch!(record)
|
558
|
-
add_to_target(record) do
|
559
|
-
|
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
|
560
443
|
end
|
561
444
|
end
|
562
445
|
|
563
|
-
|
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
|
564
472
|
end
|
565
473
|
|
566
474
|
def callback(method, record)
|
@@ -574,31 +482,12 @@ module ActiveRecord
|
|
574
482
|
owner.class.send(full_callback_name)
|
575
483
|
end
|
576
484
|
|
577
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
578
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
579
|
-
#
|
580
|
-
# If the args is just a non-empty options hash, go to the database.
|
581
|
-
#
|
582
|
-
# Otherwise, go to the database only if none of the following are true:
|
583
|
-
# * target already loaded
|
584
|
-
# * owner is new record
|
585
|
-
# * target contains new or changed record(s)
|
586
|
-
def fetch_first_nth_or_last_using_find?(args)
|
587
|
-
if args.first.is_a?(Hash)
|
588
|
-
true
|
589
|
-
else
|
590
|
-
!(loaded? ||
|
591
|
-
owner.new_record? ||
|
592
|
-
target.any? { |record| record.new_record? || record.changed? })
|
593
|
-
end
|
594
|
-
end
|
595
|
-
|
596
485
|
def include_in_memory?(record)
|
597
486
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
598
487
|
assoc = owner.association(reflection.through_reflection.name)
|
599
488
|
assoc.reader.any? { |source|
|
600
|
-
|
601
|
-
|
489
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
490
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
602
491
|
} || target.include?(record)
|
603
492
|
else
|
604
493
|
target.include?(record)
|
@@ -609,7 +498,7 @@ module ActiveRecord
|
|
609
498
|
# specified, then #find scans the entire collection.
|
610
499
|
def find_by_scan(*args)
|
611
500
|
expects_array = args.first.kind_of?(Array)
|
612
|
-
ids = args.flatten.compact.map
|
501
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
613
502
|
|
614
503
|
if ids.size == 1
|
615
504
|
id = ids.first
|
@@ -619,16 +508,6 @@ module ActiveRecord
|
|
619
508
|
load_target.select { |r| ids.include?(r.id.to_s) }
|
620
509
|
end
|
621
510
|
end
|
622
|
-
|
623
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
624
|
-
def first_nth_or_last(type, *args)
|
625
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
626
|
-
|
627
|
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
|
628
|
-
collection.send(type, *args).tap do |record|
|
629
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
630
|
-
end
|
631
|
-
end
|
632
511
|
end
|
633
512
|
end
|
634
513
|
end
|