activerecord 3.2.22.5 → 5.2.8
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 +657 -621
- data/MIT-LICENSE +2 -2
- data/README.rdoc +41 -46
- data/examples/performance.rb +55 -42
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +264 -236
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +127 -75
- data/lib/active_record/associations/association_scope.rb +126 -92
- data/lib/active_record/associations/belongs_to_association.rb +78 -27
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
- data/lib/active_record/associations/builder/association.rb +117 -32
- data/lib/active_record/associations/builder/belongs_to.rb +135 -60
- data/lib/active_record/associations/builder/collection_association.rb +61 -54
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
- data/lib/active_record/associations/builder/has_many.rb +10 -64
- data/lib/active_record/associations/builder/has_one.rb +19 -51
- data/lib/active_record/associations/builder/singular_association.rb +28 -18
- data/lib/active_record/associations/collection_association.rb +226 -293
- data/lib/active_record/associations/collection_proxy.rb +1067 -69
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +83 -47
- data/lib/active_record/associations/has_many_through_association.rb +98 -65
- data/lib/active_record/associations/has_one_association.rb +57 -20
- data/lib/active_record/associations/has_one_through_association.rb +18 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +212 -164
- data/lib/active_record/associations/preloader/association.rb +95 -89
- data/lib/active_record/associations/preloader/through_association.rb +84 -44
- data/lib/active_record/associations/preloader.rb +123 -111
- data/lib/active_record/associations/singular_association.rb +33 -24
- data/lib/active_record/associations/through_association.rb +60 -26
- data/lib/active_record/associations.rb +1759 -1506
- data/lib/active_record/attribute_assignment.rb +60 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
- data/lib/active_record/attribute_methods/dirty.rb +113 -74
- data/lib/active_record/attribute_methods/primary_key.rb +106 -77
- data/lib/active_record/attribute_methods/query.rb +8 -5
- data/lib/active_record/attribute_methods/read.rb +63 -114
- data/lib/active_record/attribute_methods/serialization.rb +60 -90
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
- data/lib/active_record/attribute_methods/write.rb +43 -45
- data/lib/active_record/attribute_methods.rb +366 -149
- data/lib/active_record/attributes.rb +266 -0
- data/lib/active_record/autosave_association.rb +312 -225
- data/lib/active_record/base.rb +114 -505
- data/lib/active_record/callbacks.rb +145 -67
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/collection_cache_key.rb +53 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
- data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
- data/lib/active_record/connection_adapters/column.rb +50 -255
- data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
- 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 +59 -210
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
- data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
- 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 +545 -27
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +145 -0
- data/lib/active_record/core.rb +559 -0
- data/lib/active_record/counter_cache.rb +200 -105
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -69
- data/lib/active_record/enum.rb +244 -0
- data/lib/active_record/errors.rb +245 -60
- data/lib/active_record/explain.rb +35 -71
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -9
- data/lib/active_record/fixture_set/file.rb +82 -0
- data/lib/active_record/fixtures.rb +418 -275
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +209 -100
- data/lib/active_record/integration.rb +116 -21
- 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 +9 -1
- data/lib/active_record/locking/optimistic.rb +107 -94
- data/lib/active_record/locking/pessimistic.rb +20 -8
- data/lib/active_record/log_subscriber.rb +99 -34
- data/lib/active_record/migration/command_recorder.rb +199 -64
- data/lib/active_record/migration/compatibility.rb +217 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +893 -296
- data/lib/active_record/model_schema.rb +328 -175
- data/lib/active_record/nested_attributes.rb +338 -242
- data/lib/active_record/no_touching.rb +58 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +557 -170
- data/lib/active_record/query_cache.rb +14 -43
- data/lib/active_record/querying.rb +36 -24
- data/lib/active_record/railtie.rb +147 -52
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +13 -6
- data/lib/active_record/railties/databases.rake +206 -488
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +734 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +249 -52
- data/lib/active_record/relation/calculations.rb +330 -284
- data/lib/active_record/relation/delegation.rb +135 -37
- data/lib/active_record/relation/finder_methods.rb +450 -287
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- 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 +19 -0
- data/lib/active_record/relation/predicate_builder.rb +132 -43
- data/lib/active_record/relation/query_attribute.rb +45 -0
- data/lib/active_record/relation/query_methods.rb +1037 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +48 -151
- 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 +451 -359
- data/lib/active_record/result.rb +129 -20
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +164 -136
- data/lib/active_record/schema.rb +31 -19
- data/lib/active_record/schema_dumper.rb +154 -107
- data/lib/active_record/schema_migration.rb +56 -0
- data/lib/active_record/scoping/default.rb +108 -98
- data/lib/active_record/scoping/named.rb +125 -112
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +121 -0
- data/lib/active_record/store.rb +175 -16
- 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 +337 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
- data/lib/active_record/timestamp.rb +80 -41
- data/lib/active_record/touch_later.rb +64 -0
- data/lib/active_record/transactions.rb +240 -119
- 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 +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- 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 +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +79 -0
- 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 +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +133 -75
- data/lib/active_record/validations.rb +53 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +89 -57
- 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 +61 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
- data/lib/rails/generators/active_record/migration.rb +28 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +141 -62
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- 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_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- 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_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
@@ -6,7 +6,15 @@ module ActiveRecord
|
|
6
6
|
#
|
7
7
|
# CollectionAssociation is an abstract class that provides common stuff to
|
8
8
|
# ease the implementation of association proxies that represent
|
9
|
-
# collections. See the class hierarchy in
|
9
|
+
# collections. See the class hierarchy in Association.
|
10
|
+
#
|
11
|
+
# CollectionAssociation:
|
12
|
+
# HasManyAssociation => has_many
|
13
|
+
# HasManyThroughAssociation + ThroughAssociation => has_many :through
|
14
|
+
#
|
15
|
+
# The CollectionAssociation class provides common methods to the collections
|
16
|
+
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
17
|
+
# the +:through association+ option.
|
10
18
|
#
|
11
19
|
# You need to be careful with assumptions regarding the target: The proxy
|
12
20
|
# does not fetch records from the database until it needs them, but new
|
@@ -18,22 +26,14 @@ module ActiveRecord
|
|
18
26
|
# If you need to work on all current children, new and existing records,
|
19
27
|
# +load_target+ and the +loaded+ flag are your friends.
|
20
28
|
class CollectionAssociation < Association #:nodoc:
|
21
|
-
attr_reader :proxy
|
22
|
-
|
23
|
-
def initialize(owner, reflection)
|
24
|
-
super
|
25
|
-
@proxy = CollectionProxy.new(self)
|
26
|
-
end
|
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
|
-
proxy
|
35
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
36
|
+
@proxy.reset_scope
|
37
37
|
end
|
38
38
|
|
39
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -43,92 +43,78 @@ module ActiveRecord
|
|
43
43
|
|
44
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
45
45
|
def ids_reader
|
46
|
-
if
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
if loaded?
|
47
|
+
target.pluck(reflection.association_primary_key)
|
48
|
+
elsif !target.empty?
|
49
|
+
load_target.pluck(reflection.association_primary_key)
|
50
50
|
else
|
51
|
-
|
52
|
-
relation = scoped
|
53
|
-
|
54
|
-
including = (relation.eager_load_values + relation.includes_values).uniq
|
55
|
-
|
56
|
-
if including.any?
|
57
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, [])
|
58
|
-
relation = join_dependency.join_associations.inject(relation) do |r, association|
|
59
|
-
association.join_relation(r)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
relation.pluck(column)
|
51
|
+
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
64
52
|
end
|
65
53
|
end
|
66
54
|
|
67
55
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
68
56
|
def ids_writer(ids)
|
69
|
-
|
70
|
-
|
71
|
-
ids
|
72
|
-
|
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
|
73
73
|
end
|
74
74
|
|
75
75
|
def reset
|
76
|
-
|
76
|
+
super
|
77
77
|
@target = []
|
78
|
-
|
79
|
-
|
80
|
-
def select(select = nil)
|
81
|
-
if block_given?
|
82
|
-
load_target.select.each { |e| yield e }
|
83
|
-
else
|
84
|
-
scoped.select(select)
|
85
|
-
end
|
78
|
+
@association_ids = nil
|
86
79
|
end
|
87
80
|
|
88
81
|
def find(*args)
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
82
|
+
if options[:inverse_of] && loaded?
|
83
|
+
args_flatten = args.flatten
|
84
|
+
model = scope.klass
|
85
|
+
|
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)
|
96
89
|
end
|
97
|
-
end
|
98
|
-
end
|
99
90
|
|
100
|
-
|
101
|
-
first_or_last(:first, *args)
|
102
|
-
end
|
91
|
+
result = find_by_scan(*args)
|
103
92
|
|
104
|
-
|
105
|
-
|
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
|
106
102
|
end
|
107
103
|
|
108
|
-
def build(attributes = {},
|
104
|
+
def build(attributes = {}, &block)
|
109
105
|
if attributes.is_a?(Array)
|
110
|
-
attributes.collect { |attr| build(attr,
|
106
|
+
attributes.collect { |attr| build(attr, &block) }
|
111
107
|
else
|
112
|
-
add_to_target(build_record(attributes,
|
113
|
-
yield(record) if block_given?
|
114
|
-
end
|
108
|
+
add_to_target(build_record(attributes, &block))
|
115
109
|
end
|
116
110
|
end
|
117
111
|
|
118
|
-
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
def create!(attributes = {}, options = {}, &block)
|
123
|
-
create_record(attributes, options, true, &block)
|
124
|
-
end
|
125
|
-
|
126
|
-
# Add +records+ to this association. Returns +self+ so method calls may be chained.
|
127
|
-
# Since << flattens its argument list and inserts each record, +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.
|
128
114
|
def concat(*records)
|
129
|
-
|
130
|
-
|
115
|
+
records = records.flatten
|
131
116
|
if owner.new_record?
|
117
|
+
load_target
|
132
118
|
concat_records(records)
|
133
119
|
else
|
134
120
|
transaction { concat_records(records) }
|
@@ -150,23 +136,38 @@ module ActiveRecord
|
|
150
136
|
end
|
151
137
|
end
|
152
138
|
|
153
|
-
#
|
139
|
+
# Removes all records from the association without calling callbacks
|
140
|
+
# on the associated records. It honors the +:dependent+ option. However
|
141
|
+
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
142
|
+
# deletion strategy for the association is applied.
|
143
|
+
#
|
144
|
+
# You can force a particular deletion strategy by passing a parameter.
|
145
|
+
#
|
146
|
+
# Example:
|
147
|
+
#
|
148
|
+
# @author.books.delete_all(:nullify)
|
149
|
+
# @author.books.delete_all(:delete_all)
|
154
150
|
#
|
155
151
|
# See delete for more info.
|
156
|
-
def delete_all
|
157
|
-
|
152
|
+
def delete_all(dependent = nil)
|
153
|
+
if dependent && ![:nullify, :delete_all].include?(dependent)
|
154
|
+
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
155
|
+
end
|
156
|
+
|
157
|
+
dependent = if dependent
|
158
|
+
dependent
|
159
|
+
elsif options[:dependent] == :destroy
|
160
|
+
:delete_all
|
161
|
+
else
|
162
|
+
options[:dependent]
|
163
|
+
end
|
164
|
+
|
165
|
+
delete_or_nullify_all_records(dependent).tap do
|
158
166
|
reset
|
159
167
|
loaded!
|
160
168
|
end
|
161
169
|
end
|
162
170
|
|
163
|
-
# Called when the association is declared as :dependent => :delete_all. This is
|
164
|
-
# an optimised version which avoids loading the records into memory. Not really
|
165
|
-
# for public consumption.
|
166
|
-
def delete_all_on_destroy
|
167
|
-
scoped.delete_all
|
168
|
-
end
|
169
|
-
|
170
171
|
# Destroy all the records from this association.
|
171
172
|
#
|
172
173
|
# See destroy for more info.
|
@@ -177,49 +178,6 @@ module ActiveRecord
|
|
177
178
|
end
|
178
179
|
end
|
179
180
|
|
180
|
-
# Calculate sum using SQL, not Enumerable
|
181
|
-
def sum(*args)
|
182
|
-
if block_given?
|
183
|
-
scoped.sum(*args) { |*block_args| yield(*block_args) }
|
184
|
-
else
|
185
|
-
scoped.sum(*args)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
# Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
|
190
|
-
# association, it will be used for the query. Otherwise, construct options and pass them with
|
191
|
-
# scope to the target class's +count+.
|
192
|
-
def count(column_name = nil, count_options = {})
|
193
|
-
return 0 if owner.new_record?
|
194
|
-
|
195
|
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
196
|
-
|
197
|
-
if options[:counter_sql] || options[:finder_sql]
|
198
|
-
unless count_options.blank?
|
199
|
-
raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
|
200
|
-
end
|
201
|
-
|
202
|
-
reflection.klass.count_by_sql(custom_counter_sql)
|
203
|
-
else
|
204
|
-
if options[:uniq]
|
205
|
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
206
|
-
column_name ||= reflection.klass.primary_key
|
207
|
-
count_options.merge!(:distinct => true)
|
208
|
-
end
|
209
|
-
|
210
|
-
value = scoped.count(column_name, count_options)
|
211
|
-
|
212
|
-
limit = options[:limit]
|
213
|
-
offset = options[:offset]
|
214
|
-
|
215
|
-
if limit || offset
|
216
|
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
|
217
|
-
else
|
218
|
-
value
|
219
|
-
end
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
181
|
# Removes +records+ from this association calling +before_remove+ and
|
224
182
|
# +after_remove+ callbacks.
|
225
183
|
#
|
@@ -231,13 +189,12 @@ module ActiveRecord
|
|
231
189
|
delete_or_destroy(records, options[:dependent])
|
232
190
|
end
|
233
191
|
|
234
|
-
#
|
235
|
-
# +before_remove+
|
192
|
+
# Deletes the +records+ and removes them from this association calling
|
193
|
+
# +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
|
236
194
|
#
|
237
|
-
# Note that this method
|
238
|
-
#
|
195
|
+
# Note that this method removes records from the database ignoring the
|
196
|
+
# +:dependent+ option.
|
239
197
|
def destroy(*records)
|
240
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
241
198
|
delete_or_destroy(records, :destroy)
|
242
199
|
end
|
243
200
|
|
@@ -252,68 +209,49 @@ module ActiveRecord
|
|
252
209
|
# This method is abstract in the sense that it relies on
|
253
210
|
# +count_records+, which is a method descendants have to provide.
|
254
211
|
def size
|
255
|
-
if !find_target? ||
|
212
|
+
if !find_target? || loaded?
|
256
213
|
target.size
|
257
|
-
elsif !
|
214
|
+
elsif !association_scope.group_values.empty?
|
258
215
|
load_target.size
|
259
|
-
elsif !
|
260
|
-
unsaved_records = target.select
|
216
|
+
elsif !association_scope.distinct_value && target.is_a?(Array)
|
217
|
+
unsaved_records = target.select(&:new_record?)
|
261
218
|
unsaved_records.size + count_records
|
262
219
|
else
|
263
220
|
count_records
|
264
221
|
end
|
265
222
|
end
|
266
223
|
|
267
|
-
# Returns
|
224
|
+
# Returns true if the collection is empty.
|
268
225
|
#
|
269
|
-
# If the collection has been
|
270
|
-
#
|
271
|
-
#
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
# Equivalent to <tt>collection.size.zero?</tt>. If the collection has
|
277
|
-
# not been already loaded and you are going to fetch the records anyway
|
278
|
-
# it is better to check <tt>collection.length.zero?</tt>.
|
226
|
+
# If the collection has been loaded
|
227
|
+
# it is equivalent to <tt>collection.size.zero?</tt>. If the
|
228
|
+
# collection has not been loaded, it is equivalent to
|
229
|
+
# <tt>collection.exists?</tt>. If the collection has not already been
|
230
|
+
# loaded and you are going to fetch the records anyway it is better to
|
231
|
+
# check <tt>collection.length.zero?</tt>.
|
279
232
|
def empty?
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
def any?
|
284
|
-
if block_given?
|
285
|
-
load_target.any? { |*block_args| yield(*block_args) }
|
286
|
-
else
|
287
|
-
!empty?
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
# Returns true if the collection has more than 1 record. Equivalent to collection.size > 1.
|
292
|
-
def many?
|
293
|
-
if block_given?
|
294
|
-
load_target.many? { |*block_args| yield(*block_args) }
|
233
|
+
if loaded?
|
234
|
+
size.zero?
|
295
235
|
else
|
296
|
-
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
def uniq(collection = load_target)
|
301
|
-
seen = {}
|
302
|
-
collection.find_all do |record|
|
303
|
-
seen[record.id] = true unless seen.key?(record.id)
|
236
|
+
@target.blank? && !scope.exists?
|
304
237
|
end
|
305
238
|
end
|
306
239
|
|
307
|
-
# Replace this collection with +other_array
|
308
|
-
#
|
240
|
+
# Replace this collection with +other_array+. This will perform a diff
|
241
|
+
# and delete/add only records that have changed.
|
309
242
|
def replace(other_array)
|
310
|
-
other_array.each { |val| raise_on_type_mismatch(val) }
|
243
|
+
other_array.each { |val| raise_on_type_mismatch!(val) }
|
311
244
|
original_target = load_target.dup
|
312
245
|
|
313
246
|
if owner.new_record?
|
314
247
|
replace_records(other_array, original_target)
|
315
248
|
else
|
316
|
-
|
249
|
+
replace_common_records_in_memory(other_array, original_target)
|
250
|
+
if other_array != original_target
|
251
|
+
transaction { replace_records(other_array, original_target) }
|
252
|
+
else
|
253
|
+
other_array
|
254
|
+
end
|
317
255
|
end
|
318
256
|
end
|
319
257
|
|
@@ -322,8 +260,7 @@ module ActiveRecord
|
|
322
260
|
if record.new_record?
|
323
261
|
include_in_memory?(record)
|
324
262
|
else
|
325
|
-
|
326
|
-
loaded? ? target.include?(record) : scoped.exists?(record)
|
263
|
+
loaded? ? target.include?(record) : scope.exists?(record.id)
|
327
264
|
end
|
328
265
|
else
|
329
266
|
false
|
@@ -339,52 +276,45 @@ module ActiveRecord
|
|
339
276
|
target
|
340
277
|
end
|
341
278
|
|
342
|
-
def add_to_target(record)
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
if options[:uniq] && index = @target.index(record)
|
347
|
-
@target[index] = record
|
348
|
-
else
|
349
|
-
@target << record
|
279
|
+
def add_to_target(record, skip_callbacks = false, &block)
|
280
|
+
if association_scope.distinct_value
|
281
|
+
index = @target.index(record)
|
350
282
|
end
|
283
|
+
replace_on_target(record, index, skip_callbacks, &block)
|
284
|
+
end
|
351
285
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
286
|
+
def scope
|
287
|
+
scope = super
|
288
|
+
scope.none! if null_scope?
|
289
|
+
scope
|
356
290
|
end
|
357
291
|
|
358
|
-
|
292
|
+
def null_scope?
|
293
|
+
owner.new_record? && !foreign_key_present?
|
294
|
+
end
|
359
295
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
|
366
|
-
count_with = $2.to_s
|
367
|
-
count_with = '*' if count_with.blank? || count_with =~ /,/ || count_with =~ /\.\*/
|
368
|
-
"SELECT #{$1}COUNT(#{count_with}) FROM"
|
369
|
-
end
|
370
|
-
end
|
371
|
-
end
|
296
|
+
def find_from_target?
|
297
|
+
loaded? ||
|
298
|
+
owner.new_record? ||
|
299
|
+
target.any? { |record| record.new_record? || record.changed? }
|
300
|
+
end
|
372
301
|
|
373
|
-
|
374
|
-
interpolate(options[:finder_sql])
|
375
|
-
end
|
302
|
+
private
|
376
303
|
|
377
304
|
def find_target
|
378
|
-
|
379
|
-
|
380
|
-
reflection.klass.find_by_sql(custom_finder_sql)
|
381
|
-
else
|
382
|
-
scoped.all
|
383
|
-
end
|
305
|
+
scope = self.scope
|
306
|
+
return scope.to_a if skip_statement_cache?(scope)
|
384
307
|
|
385
|
-
|
386
|
-
|
387
|
-
|
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
|
313
|
+
|
314
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
315
|
+
sc.execute(binds, conn) do |record|
|
316
|
+
set_inverse_instance(record)
|
317
|
+
end
|
388
318
|
end
|
389
319
|
|
390
320
|
# We have some records loaded from the database (persisted) and some that are
|
@@ -402,14 +332,9 @@ module ActiveRecord
|
|
402
332
|
return memory if persisted.empty?
|
403
333
|
|
404
334
|
persisted.map! do |record|
|
405
|
-
|
406
|
-
# record rather than memory.at(memory.index(record)). The behavior is fixed in 1.9.
|
407
|
-
mem_index = memory.index(record)
|
408
|
-
|
409
|
-
if mem_index
|
410
|
-
mem_record = memory.delete_at(mem_index)
|
335
|
+
if mem_record = memory.delete(record)
|
411
336
|
|
412
|
-
((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|
|
413
338
|
mem_record[name] = record[name]
|
414
339
|
end
|
415
340
|
|
@@ -422,36 +347,43 @@ module ActiveRecord
|
|
422
347
|
persisted + memory
|
423
348
|
end
|
424
349
|
|
425
|
-
def
|
350
|
+
def _create_record(attributes, raise = false, &block)
|
426
351
|
unless owner.persisted?
|
427
352
|
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
428
353
|
end
|
429
354
|
|
430
355
|
if attributes.is_a?(Array)
|
431
|
-
attributes.collect { |attr|
|
356
|
+
attributes.collect { |attr| _create_record(attr, raise, &block) }
|
432
357
|
else
|
358
|
+
record = build_record(attributes, &block)
|
433
359
|
transaction do
|
434
|
-
|
435
|
-
|
436
|
-
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
|
+
}
|
437
365
|
end
|
366
|
+
raise ActiveRecord::Rollback unless result
|
438
367
|
end
|
368
|
+
record
|
439
369
|
end
|
440
370
|
end
|
441
371
|
|
442
372
|
# Do the relevant stuff to insert the given record into the association collection.
|
443
|
-
def insert_record(record, validate = true, raise = false)
|
444
|
-
raise
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
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
|
449
379
|
end
|
450
380
|
|
451
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) }
|
452
384
|
records = records.flatten
|
453
|
-
records.each { |record| raise_on_type_mismatch(record) }
|
454
|
-
existing_records = records.reject
|
385
|
+
records.each { |record| raise_on_type_mismatch!(record) }
|
386
|
+
existing_records = records.reject(&:new_record?)
|
455
387
|
|
456
388
|
if existing_records.empty?
|
457
389
|
remove_records(existing_records, records, method)
|
@@ -465,20 +397,22 @@ module ActiveRecord
|
|
465
397
|
|
466
398
|
delete_records(existing_records, method) if existing_records.any?
|
467
399
|
records.each { |record| target.delete(record) }
|
400
|
+
@association_ids = nil
|
468
401
|
|
469
402
|
records.each { |record| callback(:after_remove, record) }
|
470
403
|
end
|
471
404
|
|
472
|
-
# Delete the given records from the association,
|
473
|
-
#
|
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).
|
474
408
|
def delete_records(records, method)
|
475
409
|
raise NotImplementedError
|
476
410
|
end
|
477
411
|
|
478
412
|
def replace_records(new_target, original_target)
|
479
|
-
delete(target
|
413
|
+
delete(difference(target, new_target))
|
480
414
|
|
481
|
-
unless concat(new_target
|
415
|
+
unless concat(difference(new_target, target))
|
482
416
|
@target = original_target
|
483
417
|
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
484
418
|
"new records could not be saved."
|
@@ -487,92 +421,91 @@ module ActiveRecord
|
|
487
421
|
target
|
488
422
|
end
|
489
423
|
|
490
|
-
def
|
424
|
+
def replace_common_records_in_memory(new_target, original_target)
|
425
|
+
common_records = intersection(new_target, original_target)
|
426
|
+
common_records.each do |record|
|
427
|
+
skip_callbacks = true
|
428
|
+
replace_on_target(record, @target.index(record), skip_callbacks)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
def concat_records(records, raise = false)
|
491
433
|
result = true
|
492
434
|
|
493
|
-
records.
|
494
|
-
raise_on_type_mismatch(record)
|
495
|
-
add_to_target(record) do
|
496
|
-
|
435
|
+
records.each do |record|
|
436
|
+
raise_on_type_mismatch!(record)
|
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
|
497
443
|
end
|
498
444
|
end
|
499
445
|
|
500
|
-
|
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
|
501
472
|
end
|
502
473
|
|
503
474
|
def callback(method, record)
|
504
475
|
callbacks_for(method).each do |callback|
|
505
|
-
|
506
|
-
when Symbol
|
507
|
-
owner.send(callback, record)
|
508
|
-
when Proc
|
509
|
-
callback.call(owner, record)
|
510
|
-
else
|
511
|
-
callback.send(method, owner, record)
|
512
|
-
end
|
476
|
+
callback.call(method, owner, record)
|
513
477
|
end
|
514
478
|
end
|
515
479
|
|
516
480
|
def callbacks_for(callback_name)
|
517
481
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
518
|
-
owner.class.send(full_callback_name
|
519
|
-
end
|
520
|
-
|
521
|
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
|
522
|
-
# the database, or by getting the target, and then taking the first/last item from that?
|
523
|
-
#
|
524
|
-
# If the args is just a non-empty options hash, go to the database.
|
525
|
-
#
|
526
|
-
# Otherwise, go to the database only if none of the following are true:
|
527
|
-
# * target already loaded
|
528
|
-
# * owner is new record
|
529
|
-
# * custom :finder_sql exists
|
530
|
-
# * target contains new or changed record(s)
|
531
|
-
# * the first arg is an integer (which indicates the number of records to be returned)
|
532
|
-
def fetch_first_or_last_using_find?(args)
|
533
|
-
if args.first.is_a?(Hash)
|
534
|
-
true
|
535
|
-
else
|
536
|
-
!(loaded? ||
|
537
|
-
owner.new_record? ||
|
538
|
-
options[:finder_sql] ||
|
539
|
-
target.any? { |record| record.new_record? || record.changed? } ||
|
540
|
-
args.first.kind_of?(Integer))
|
541
|
-
end
|
482
|
+
owner.class.send(full_callback_name)
|
542
483
|
end
|
543
484
|
|
544
485
|
def include_in_memory?(record)
|
545
486
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
546
|
-
owner.
|
547
|
-
|
548
|
-
|
487
|
+
assoc = owner.association(reflection.through_reflection.name)
|
488
|
+
assoc.reader.any? { |source|
|
489
|
+
target_reflection = source.send(reflection.source_reflection.name)
|
490
|
+
target_reflection.respond_to?(:include?) ? target_reflection.include?(record) : target_reflection == record
|
549
491
|
} || target.include?(record)
|
550
492
|
else
|
551
493
|
target.include?(record)
|
552
494
|
end
|
553
495
|
end
|
554
496
|
|
555
|
-
# If
|
497
|
+
# If the :inverse_of option has been
|
498
|
+
# specified, then #find scans the entire collection.
|
556
499
|
def find_by_scan(*args)
|
557
500
|
expects_array = args.first.kind_of?(Array)
|
558
|
-
ids = args.flatten.compact.
|
501
|
+
ids = args.flatten.compact.map(&:to_s).uniq
|
559
502
|
|
560
503
|
if ids.size == 1
|
561
504
|
id = ids.first
|
562
|
-
record = load_target.detect { |r| id == r.id }
|
505
|
+
record = load_target.detect { |r| id == r.id.to_s }
|
563
506
|
expects_array ? [ record ] : record
|
564
507
|
else
|
565
|
-
load_target.select { |r| ids.include?(r.id) }
|
566
|
-
end
|
567
|
-
end
|
568
|
-
|
569
|
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
|
570
|
-
def first_or_last(type, *args)
|
571
|
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
572
|
-
|
573
|
-
collection = fetch_first_or_last_using_find?(args) ? scoped : load_target
|
574
|
-
collection.send(type, *args).tap do |record|
|
575
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
508
|
+
load_target.select { |r| ids.include?(r.id.to_s) }
|
576
509
|
end
|
577
510
|
end
|
578
511
|
end
|