activerecord 3.2.19 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1715 -604
- data/MIT-LICENSE +2 -2
- data/README.rdoc +40 -45
- data/examples/performance.rb +33 -22
- data/examples/simple.rb +3 -4
- data/lib/active_record/aggregations.rb +76 -51
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +54 -40
- data/lib/active_record/associations/association.rb +76 -56
- data/lib/active_record/associations/association_scope.rb +125 -93
- data/lib/active_record/associations/belongs_to_association.rb +57 -28
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +120 -32
- data/lib/active_record/associations/builder/belongs_to.rb +115 -62
- data/lib/active_record/associations/builder/collection_association.rb +61 -53
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
- data/lib/active_record/associations/builder/has_many.rb +9 -65
- data/lib/active_record/associations/builder/has_one.rb +18 -52
- data/lib/active_record/associations/builder/singular_association.rb +18 -19
- data/lib/active_record/associations/collection_association.rb +268 -186
- data/lib/active_record/associations/collection_proxy.rb +1003 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +81 -41
- data/lib/active_record/associations/has_many_through_association.rb +76 -55
- data/lib/active_record/associations/has_one_association.rb +51 -21
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +239 -155
- data/lib/active_record/associations/preloader/association.rb +97 -62
- data/lib/active_record/associations/preloader/collection_association.rb +2 -8
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +75 -33
- data/lib/active_record/associations/preloader.rb +111 -79
- data/lib/active_record/associations/singular_association.rb +35 -13
- data/lib/active_record/associations/through_association.rb +41 -19
- data/lib/active_record/associations.rb +727 -501
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +213 -0
- data/lib/active_record/attribute_assignment.rb +32 -162
- data/lib/active_record/attribute_decorators.rb +67 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -61
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +7 -6
- data/lib/active_record/attribute_methods/read.rb +56 -117
- data/lib/active_record/attribute_methods/serialization.rb +43 -96
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
- data/lib/active_record/attribute_methods/write.rb +34 -45
- data/lib/active_record/attribute_methods.rb +333 -144
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +108 -0
- data/lib/active_record/attribute_set.rb +108 -0
- data/lib/active_record/attributes.rb +265 -0
- data/lib/active_record/autosave_association.rb +285 -223
- data/lib/active_record/base.rb +95 -490
- data/lib/active_record/callbacks.rb +95 -61
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +28 -19
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
- data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
- data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
- data/lib/active_record/connection_adapters/column.rb +30 -259
- data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
- data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +155 -0
- data/lib/active_record/core.rb +561 -0
- data/lib/active_record/counter_cache.rb +146 -105
- data/lib/active_record/dynamic_matchers.rb +101 -64
- data/lib/active_record/enum.rb +234 -0
- data/lib/active_record/errors.rb +153 -56
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +10 -6
- data/lib/active_record/fixture_set/file.rb +77 -0
- data/lib/active_record/fixtures.rb +355 -232
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +144 -79
- data/lib/active_record/integration.rb +66 -13
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +77 -56
- data/lib/active_record/locking/pessimistic.rb +6 -6
- data/lib/active_record/log_subscriber.rb +53 -28
- data/lib/active_record/migration/command_recorder.rb +166 -33
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +792 -264
- data/lib/active_record/model_schema.rb +192 -130
- data/lib/active_record/nested_attributes.rb +238 -145
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +89 -0
- data/lib/active_record/persistence.rb +357 -157
- data/lib/active_record/query_cache.rb +22 -43
- data/lib/active_record/querying.rb +34 -23
- data/lib/active_record/railtie.rb +88 -48
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +5 -4
- data/lib/active_record/railties/databases.rake +170 -422
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -5
- data/lib/active_record/reflection.rb +715 -189
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +203 -50
- data/lib/active_record/relation/calculations.rb +203 -194
- data/lib/active_record/relation/delegation.rb +103 -25
- data/lib/active_record/relation/finder_methods.rb +457 -261
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +167 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +153 -48
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +1019 -194
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +46 -150
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +450 -245
- data/lib/active_record/result.rb +104 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +120 -94
- data/lib/active_record/schema.rb +28 -18
- data/lib/active_record/schema_dumper.rb +141 -74
- data/lib/active_record/schema_migration.rb +50 -0
- data/lib/active_record/scoping/default.rb +64 -57
- data/lib/active_record/scoping/named.rb +93 -108
- data/lib/active_record/scoping.rb +73 -121
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +7 -5
- data/lib/active_record/statement_cache.rb +113 -0
- data/lib/active_record/store.rb +173 -15
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +313 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
- data/lib/active_record/timestamp.rb +42 -24
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +233 -105
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +7 -0
- data/lib/active_record/type/date_time.rb +7 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +63 -0
- data/lib/active_record/type/time.rb +20 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type.rb +72 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +33 -18
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +66 -0
- data/lib/active_record/validations/uniqueness.rb +128 -68
- data/lib/active_record/validations.rb +48 -40
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +71 -47
- data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
- data/lib/rails/generators/active_record/migration.rb +18 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +188 -134
- 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/has_and_belongs_to_many.rb +0 -60
- 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/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/model/templates/migration.rb +0 -15
- 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
@@ -18,14 +18,8 @@ module ActiveRecord
|
|
18
18
|
# <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
|
19
19
|
# the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
|
20
20
|
#
|
21
|
-
# This class
|
22
|
-
#
|
23
|
-
# corner case, it even removes the +class+ method and that's why you get
|
24
|
-
#
|
25
|
-
# blog.posts.class # => Array
|
26
|
-
#
|
27
|
-
# though the object behind <tt>blog.posts</tt> is not an Array, but an
|
28
|
-
# ActiveRecord::Associations::HasManyAssociation.
|
21
|
+
# This class delegates unknown methods to <tt>@target</tt> via
|
22
|
+
# <tt>method_missing</tt>.
|
29
23
|
#
|
30
24
|
# The <tt>@target</tt> object is not \loaded until needed. For example,
|
31
25
|
#
|
@@ -33,101 +27,1047 @@ module ActiveRecord
|
|
33
27
|
#
|
34
28
|
# is computed directly through SQL and does not trigger by itself the
|
35
29
|
# instantiation of the actual post records.
|
36
|
-
class CollectionProxy
|
37
|
-
|
30
|
+
class CollectionProxy < Relation
|
31
|
+
delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
|
32
|
+
delegate :find_nth, to: :scope
|
38
33
|
|
39
|
-
|
34
|
+
def initialize(klass, association) #:nodoc:
|
35
|
+
@association = association
|
36
|
+
super klass, klass.arel_table, klass.predicate_builder
|
37
|
+
merge! association.scope(nullify: false)
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
|
40
|
+
def target
|
41
|
+
@association.target
|
42
|
+
end
|
43
43
|
|
44
|
-
|
44
|
+
def load_target
|
45
|
+
@association.load_target
|
46
|
+
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
# Returns +true+ if the association has been loaded, otherwise +false+.
|
49
|
+
#
|
50
|
+
# person.pets.loaded? # => false
|
51
|
+
# person.pets
|
52
|
+
# person.pets.loaded? # => true
|
53
|
+
def loaded?
|
54
|
+
@association.loaded?
|
55
|
+
end
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
57
|
+
# Works in two ways.
|
58
|
+
#
|
59
|
+
# *First:* Specify a subset of fields to be selected from the result set.
|
60
|
+
#
|
61
|
+
# class Person < ActiveRecord::Base
|
62
|
+
# has_many :pets
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# person.pets
|
66
|
+
# # => [
|
67
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
68
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
69
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
70
|
+
# # ]
|
71
|
+
#
|
72
|
+
# person.pets.select(:name)
|
73
|
+
# # => [
|
74
|
+
# # #<Pet id: nil, name: "Fancy-Fancy">,
|
75
|
+
# # #<Pet id: nil, name: "Spook">,
|
76
|
+
# # #<Pet id: nil, name: "Choo-Choo">
|
77
|
+
# # ]
|
78
|
+
#
|
79
|
+
# person.pets.select(:id, :name )
|
80
|
+
# # => [
|
81
|
+
# # #<Pet id: 1, name: "Fancy-Fancy">,
|
82
|
+
# # #<Pet id: 2, name: "Spook">,
|
83
|
+
# # #<Pet id: 3, name: "Choo-Choo">
|
84
|
+
# # ]
|
85
|
+
#
|
86
|
+
# Be careful because this also means you're initializing a model
|
87
|
+
# object with only the fields that you've selected. If you attempt
|
88
|
+
# to access a field except +id+ that is not in the initialized record you'll
|
89
|
+
# receive:
|
90
|
+
#
|
91
|
+
# person.pets.select(:name).first.person_id
|
92
|
+
# # => ActiveModel::MissingAttributeError: missing attribute: person_id
|
93
|
+
#
|
94
|
+
# *Second:* You can pass a block so it can be used just like Array#select.
|
95
|
+
# This builds an array of objects from the database for the scope,
|
96
|
+
# converting them into an array and iterating through them using
|
97
|
+
# Array#select.
|
98
|
+
#
|
99
|
+
# person.pets.select { |pet| pet.name =~ /oo/ }
|
100
|
+
# # => [
|
101
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
102
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
103
|
+
# # ]
|
104
|
+
#
|
105
|
+
# person.pets.select(:name) { |pet| pet.name =~ /oo/ }
|
106
|
+
# # => [
|
107
|
+
# # #<Pet id: 2, name: "Spook">,
|
108
|
+
# # #<Pet id: 3, name: "Choo-Choo">
|
109
|
+
# # ]
|
110
|
+
def select(*fields, &block)
|
111
|
+
@association.select(*fields, &block)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Finds an object in the collection responding to the +id+. Uses the same
|
115
|
+
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
|
116
|
+
# error if the object cannot be found.
|
117
|
+
#
|
118
|
+
# class Person < ActiveRecord::Base
|
119
|
+
# has_many :pets
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# person.pets
|
123
|
+
# # => [
|
124
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
125
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
126
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
127
|
+
# # ]
|
128
|
+
#
|
129
|
+
# person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
130
|
+
# person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=4
|
131
|
+
#
|
132
|
+
# person.pets.find(2) { |pet| pet.name.downcase! }
|
133
|
+
# # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
|
134
|
+
#
|
135
|
+
# person.pets.find(2, 3)
|
136
|
+
# # => [
|
137
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
138
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
139
|
+
# # ]
|
140
|
+
def find(*args, &block)
|
141
|
+
@association.find(*args, &block)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns the first record, or the first +n+ records, from the collection.
|
145
|
+
# If the collection is empty, the first form returns +nil+, and the second
|
146
|
+
# form returns an empty array.
|
147
|
+
#
|
148
|
+
# class Person < ActiveRecord::Base
|
149
|
+
# has_many :pets
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# person.pets
|
153
|
+
# # => [
|
154
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
155
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
156
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
157
|
+
# # ]
|
158
|
+
#
|
159
|
+
# person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
160
|
+
#
|
161
|
+
# person.pets.first(2)
|
162
|
+
# # => [
|
163
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
164
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>
|
165
|
+
# # ]
|
166
|
+
#
|
167
|
+
# another_person_without.pets # => []
|
168
|
+
# another_person_without.pets.first # => nil
|
169
|
+
# another_person_without.pets.first(3) # => []
|
170
|
+
def first(*args)
|
171
|
+
@association.first(*args)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Same as #first except returns only the second record.
|
175
|
+
def second(*args)
|
176
|
+
@association.second(*args)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Same as #first except returns only the third record.
|
180
|
+
def third(*args)
|
181
|
+
@association.third(*args)
|
182
|
+
end
|
183
|
+
|
184
|
+
# Same as #first except returns only the fourth record.
|
185
|
+
def fourth(*args)
|
186
|
+
@association.fourth(*args)
|
56
187
|
end
|
57
188
|
|
189
|
+
# Same as #first except returns only the fifth record.
|
190
|
+
def fifth(*args)
|
191
|
+
@association.fifth(*args)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Same as #first except returns only the forty second record.
|
195
|
+
# Also known as accessing "the reddit".
|
196
|
+
def forty_two(*args)
|
197
|
+
@association.forty_two(*args)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Same as #first except returns only the third-to-last record.
|
201
|
+
def third_to_last(*args)
|
202
|
+
@association.third_to_last(*args)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Same as #first except returns only the second-to-last record.
|
206
|
+
def second_to_last(*args)
|
207
|
+
@association.second_to_last(*args)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns the last record, or the last +n+ records, from the collection.
|
211
|
+
# If the collection is empty, the first form returns +nil+, and the second
|
212
|
+
# form returns an empty array.
|
213
|
+
#
|
214
|
+
# class Person < ActiveRecord::Base
|
215
|
+
# has_many :pets
|
216
|
+
# end
|
217
|
+
#
|
218
|
+
# person.pets
|
219
|
+
# # => [
|
220
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
221
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
222
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
223
|
+
# # ]
|
224
|
+
#
|
225
|
+
# person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
226
|
+
#
|
227
|
+
# person.pets.last(2)
|
228
|
+
# # => [
|
229
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
230
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
231
|
+
# # ]
|
232
|
+
#
|
233
|
+
# another_person_without.pets # => []
|
234
|
+
# another_person_without.pets.last # => nil
|
235
|
+
# another_person_without.pets.last(3) # => []
|
236
|
+
def last(*args)
|
237
|
+
@association.last(*args)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Gives a record (or N records if a parameter is supplied) from the collection
|
241
|
+
# using the same rules as <tt>ActiveRecord::Base.take</tt>.
|
242
|
+
#
|
243
|
+
# class Person < ActiveRecord::Base
|
244
|
+
# has_many :pets
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# person.pets
|
248
|
+
# # => [
|
249
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
250
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
251
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
252
|
+
# # ]
|
253
|
+
#
|
254
|
+
# person.pets.take # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
255
|
+
#
|
256
|
+
# person.pets.take(2)
|
257
|
+
# # => [
|
258
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
259
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>
|
260
|
+
# # ]
|
261
|
+
#
|
262
|
+
# another_person_without.pets # => []
|
263
|
+
# another_person_without.pets.take # => nil
|
264
|
+
# another_person_without.pets.take(2) # => []
|
265
|
+
def take(n = nil)
|
266
|
+
@association.take(n)
|
267
|
+
end
|
268
|
+
|
269
|
+
# Returns a new object of the collection type that has been instantiated
|
270
|
+
# with +attributes+ and linked to this object, but have not yet been saved.
|
271
|
+
# You can pass an array of attributes hashes, this will return an array
|
272
|
+
# with the new objects.
|
273
|
+
#
|
274
|
+
# class Person
|
275
|
+
# has_many :pets
|
276
|
+
# end
|
277
|
+
#
|
278
|
+
# person.pets.build
|
279
|
+
# # => #<Pet id: nil, name: nil, person_id: 1>
|
280
|
+
#
|
281
|
+
# person.pets.build(name: 'Fancy-Fancy')
|
282
|
+
# # => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
|
283
|
+
#
|
284
|
+
# person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
|
285
|
+
# # => [
|
286
|
+
# # #<Pet id: nil, name: "Spook", person_id: 1>,
|
287
|
+
# # #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
|
288
|
+
# # #<Pet id: nil, name: "Brain", person_id: 1>
|
289
|
+
# # ]
|
290
|
+
#
|
291
|
+
# person.pets.size # => 5 # size of the collection
|
292
|
+
# person.pets.count # => 0 # count from database
|
293
|
+
def build(attributes = {}, &block)
|
294
|
+
@association.build(attributes, &block)
|
295
|
+
end
|
58
296
|
alias_method :new, :build
|
59
297
|
|
60
|
-
|
61
|
-
|
298
|
+
# Returns a new object of the collection type that has been instantiated with
|
299
|
+
# attributes, linked to this object and that has already been saved (if it
|
300
|
+
# passes the validations).
|
301
|
+
#
|
302
|
+
# class Person
|
303
|
+
# has_many :pets
|
304
|
+
# end
|
305
|
+
#
|
306
|
+
# person.pets.create(name: 'Fancy-Fancy')
|
307
|
+
# # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
|
308
|
+
#
|
309
|
+
# person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
|
310
|
+
# # => [
|
311
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
312
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
313
|
+
# # ]
|
314
|
+
#
|
315
|
+
# person.pets.size # => 3
|
316
|
+
# person.pets.count # => 3
|
317
|
+
#
|
318
|
+
# person.pets.find(1, 2, 3)
|
319
|
+
# # => [
|
320
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
321
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
322
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
323
|
+
# # ]
|
324
|
+
def create(attributes = {}, &block)
|
325
|
+
@association.create(attributes, &block)
|
326
|
+
end
|
327
|
+
|
328
|
+
# Like #create, except that if the record is invalid, raises an exception.
|
329
|
+
#
|
330
|
+
# class Person
|
331
|
+
# has_many :pets
|
332
|
+
# end
|
333
|
+
#
|
334
|
+
# class Pet
|
335
|
+
# validates :name, presence: true
|
336
|
+
# end
|
337
|
+
#
|
338
|
+
# person.pets.create!(name: nil)
|
339
|
+
# # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
|
340
|
+
def create!(attributes = {}, &block)
|
341
|
+
@association.create!(attributes, &block)
|
342
|
+
end
|
343
|
+
|
344
|
+
# Add one or more records to the collection by setting their foreign keys
|
345
|
+
# to the association's primary key. Since #<< flattens its argument list and
|
346
|
+
# inserts each record, +push+ and #concat behave identically. Returns +self+
|
347
|
+
# so method calls may be chained.
|
348
|
+
#
|
349
|
+
# class Person < ActiveRecord::Base
|
350
|
+
# has_many :pets
|
351
|
+
# end
|
352
|
+
#
|
353
|
+
# person.pets.size # => 0
|
354
|
+
# person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
|
355
|
+
# person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
|
356
|
+
# person.pets.size # => 3
|
357
|
+
#
|
358
|
+
# person.id # => 1
|
359
|
+
# person.pets
|
360
|
+
# # => [
|
361
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
362
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
363
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
364
|
+
# # ]
|
365
|
+
#
|
366
|
+
# person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
|
367
|
+
# person.pets.size # => 5
|
368
|
+
def concat(*records)
|
369
|
+
@association.concat(*records)
|
370
|
+
end
|
371
|
+
|
372
|
+
# Replaces this collection with +other_array+. This will perform a diff
|
373
|
+
# and delete/add only records that have changed.
|
374
|
+
#
|
375
|
+
# class Person < ActiveRecord::Base
|
376
|
+
# has_many :pets
|
377
|
+
# end
|
378
|
+
#
|
379
|
+
# person.pets
|
380
|
+
# # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
|
381
|
+
#
|
382
|
+
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
|
383
|
+
#
|
384
|
+
# person.pets.replace(other_pets)
|
385
|
+
#
|
386
|
+
# person.pets
|
387
|
+
# # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
|
388
|
+
#
|
389
|
+
# If the supplied array has an incorrect association type, it raises
|
390
|
+
# an <tt>ActiveRecord::AssociationTypeMismatch</tt> error:
|
391
|
+
#
|
392
|
+
# person.pets.replace(["doo", "ggie", "gaga"])
|
393
|
+
# # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
|
394
|
+
def replace(other_array)
|
395
|
+
@association.replace(other_array)
|
396
|
+
end
|
397
|
+
|
398
|
+
# Deletes all the records from the collection according to the strategy
|
399
|
+
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
400
|
+
# then it will follow the default strategy.
|
401
|
+
#
|
402
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
403
|
+
# +:delete_all+.
|
404
|
+
#
|
405
|
+
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
406
|
+
# This sets the foreign keys to +NULL+.
|
407
|
+
#
|
408
|
+
# class Person < ActiveRecord::Base
|
409
|
+
# has_many :pets # dependent: :nullify option by default
|
410
|
+
# end
|
411
|
+
#
|
412
|
+
# person.pets.size # => 3
|
413
|
+
# person.pets
|
414
|
+
# # => [
|
415
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
416
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
417
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
418
|
+
# # ]
|
419
|
+
#
|
420
|
+
# person.pets.delete_all
|
421
|
+
# # => [
|
422
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
423
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
424
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
425
|
+
# # ]
|
426
|
+
#
|
427
|
+
# person.pets.size # => 0
|
428
|
+
# person.pets # => []
|
429
|
+
#
|
430
|
+
# Pet.find(1, 2, 3)
|
431
|
+
# # => [
|
432
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
|
433
|
+
# # #<Pet id: 2, name: "Spook", person_id: nil>,
|
434
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
|
435
|
+
# # ]
|
436
|
+
#
|
437
|
+
# Both +has_many+ and <tt>has_many :through</tt> dependencies default to the
|
438
|
+
# +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
|
439
|
+
# Records are not instantiated and callbacks will not be fired.
|
440
|
+
#
|
441
|
+
# class Person < ActiveRecord::Base
|
442
|
+
# has_many :pets, dependent: :destroy
|
443
|
+
# end
|
444
|
+
#
|
445
|
+
# person.pets.size # => 3
|
446
|
+
# person.pets
|
447
|
+
# # => [
|
448
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
449
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
450
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
451
|
+
# # ]
|
452
|
+
#
|
453
|
+
# person.pets.delete_all
|
454
|
+
#
|
455
|
+
# Pet.find(1, 2, 3)
|
456
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
457
|
+
#
|
458
|
+
# If it is set to <tt>:delete_all</tt>, all the objects are deleted
|
459
|
+
# *without* calling their +destroy+ method.
|
460
|
+
#
|
461
|
+
# class Person < ActiveRecord::Base
|
462
|
+
# has_many :pets, dependent: :delete_all
|
463
|
+
# end
|
464
|
+
#
|
465
|
+
# person.pets.size # => 3
|
466
|
+
# person.pets
|
467
|
+
# # => [
|
468
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
469
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
470
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
471
|
+
# # ]
|
472
|
+
#
|
473
|
+
# person.pets.delete_all
|
474
|
+
#
|
475
|
+
# Pet.find(1, 2, 3)
|
476
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
477
|
+
def delete_all(dependent = nil)
|
478
|
+
@association.delete_all(dependent)
|
479
|
+
end
|
480
|
+
|
481
|
+
# Deletes the records of the collection directly from the database
|
482
|
+
# ignoring the +:dependent+ option. Records are instantiated and it
|
483
|
+
# invokes +before_remove+, +after_remove+ , +before_destroy+ and
|
484
|
+
# +after_destroy+ callbacks.
|
485
|
+
#
|
486
|
+
# class Person < ActiveRecord::Base
|
487
|
+
# has_many :pets
|
488
|
+
# end
|
489
|
+
#
|
490
|
+
# person.pets.size # => 3
|
491
|
+
# person.pets
|
492
|
+
# # => [
|
493
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
494
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
495
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
496
|
+
# # ]
|
497
|
+
#
|
498
|
+
# person.pets.destroy_all
|
499
|
+
#
|
500
|
+
# person.pets.size # => 0
|
501
|
+
# person.pets # => []
|
502
|
+
#
|
503
|
+
# Pet.find(1) # => Couldn't find Pet with id=1
|
504
|
+
def destroy_all
|
505
|
+
@association.destroy_all
|
506
|
+
end
|
507
|
+
|
508
|
+
# Deletes the +records+ supplied from the collection according to the strategy
|
509
|
+
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
510
|
+
# then it will follow the default strategy. Returns an array with the
|
511
|
+
# deleted records.
|
512
|
+
#
|
513
|
+
# For <tt>has_many :through</tt> associations, the default deletion strategy is
|
514
|
+
# +:delete_all+.
|
515
|
+
#
|
516
|
+
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
517
|
+
# This sets the foreign keys to +NULL+.
|
518
|
+
#
|
519
|
+
# class Person < ActiveRecord::Base
|
520
|
+
# has_many :pets # dependent: :nullify option by default
|
521
|
+
# end
|
522
|
+
#
|
523
|
+
# person.pets.size # => 3
|
524
|
+
# person.pets
|
525
|
+
# # => [
|
526
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
527
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
528
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
529
|
+
# # ]
|
530
|
+
#
|
531
|
+
# person.pets.delete(Pet.find(1))
|
532
|
+
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
|
533
|
+
#
|
534
|
+
# person.pets.size # => 2
|
535
|
+
# person.pets
|
536
|
+
# # => [
|
537
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
538
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
539
|
+
# # ]
|
540
|
+
#
|
541
|
+
# Pet.find(1)
|
542
|
+
# # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
|
543
|
+
#
|
544
|
+
# If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
|
545
|
+
# their +destroy+ method. See +destroy+ for more information.
|
546
|
+
#
|
547
|
+
# class Person < ActiveRecord::Base
|
548
|
+
# has_many :pets, dependent: :destroy
|
549
|
+
# end
|
550
|
+
#
|
551
|
+
# person.pets.size # => 3
|
552
|
+
# person.pets
|
553
|
+
# # => [
|
554
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
555
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
556
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
557
|
+
# # ]
|
558
|
+
#
|
559
|
+
# person.pets.delete(Pet.find(1), Pet.find(3))
|
560
|
+
# # => [
|
561
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
562
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
563
|
+
# # ]
|
564
|
+
#
|
565
|
+
# person.pets.size # => 1
|
566
|
+
# person.pets
|
567
|
+
# # => [#<Pet id: 2, name: "Spook", person_id: 1>]
|
568
|
+
#
|
569
|
+
# Pet.find(1, 3)
|
570
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 3)
|
571
|
+
#
|
572
|
+
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
|
573
|
+
# *without* calling their +destroy+ method.
|
574
|
+
#
|
575
|
+
# class Person < ActiveRecord::Base
|
576
|
+
# has_many :pets, dependent: :delete_all
|
577
|
+
# end
|
578
|
+
#
|
579
|
+
# person.pets.size # => 3
|
580
|
+
# person.pets
|
581
|
+
# # => [
|
582
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
583
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
584
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
585
|
+
# # ]
|
586
|
+
#
|
587
|
+
# person.pets.delete(Pet.find(1))
|
588
|
+
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
|
589
|
+
#
|
590
|
+
# person.pets.size # => 2
|
591
|
+
# person.pets
|
592
|
+
# # => [
|
593
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
594
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
595
|
+
# # ]
|
596
|
+
#
|
597
|
+
# Pet.find(1)
|
598
|
+
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with 'id'=1
|
599
|
+
#
|
600
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
601
|
+
# responding to the +id+ and executes delete on them.
|
602
|
+
#
|
603
|
+
# class Person < ActiveRecord::Base
|
604
|
+
# has_many :pets
|
605
|
+
# end
|
606
|
+
#
|
607
|
+
# person.pets.size # => 3
|
608
|
+
# person.pets
|
609
|
+
# # => [
|
610
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
611
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
612
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
613
|
+
# # ]
|
614
|
+
#
|
615
|
+
# person.pets.delete("1")
|
616
|
+
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
|
617
|
+
#
|
618
|
+
# person.pets.delete(2, 3)
|
619
|
+
# # => [
|
620
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
621
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
622
|
+
# # ]
|
623
|
+
def delete(*records)
|
624
|
+
@association.delete(*records)
|
625
|
+
end
|
626
|
+
|
627
|
+
# Destroys the +records+ supplied and removes them from the collection.
|
628
|
+
# This method will _always_ remove record from the database ignoring
|
629
|
+
# the +:dependent+ option. Returns an array with the removed records.
|
630
|
+
#
|
631
|
+
# class Person < ActiveRecord::Base
|
632
|
+
# has_many :pets
|
633
|
+
# end
|
634
|
+
#
|
635
|
+
# person.pets.size # => 3
|
636
|
+
# person.pets
|
637
|
+
# # => [
|
638
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
639
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
640
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
641
|
+
# # ]
|
642
|
+
#
|
643
|
+
# person.pets.destroy(Pet.find(1))
|
644
|
+
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
|
645
|
+
#
|
646
|
+
# person.pets.size # => 2
|
647
|
+
# person.pets
|
648
|
+
# # => [
|
649
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
650
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
651
|
+
# # ]
|
652
|
+
#
|
653
|
+
# person.pets.destroy(Pet.find(2), Pet.find(3))
|
654
|
+
# # => [
|
655
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
656
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
657
|
+
# # ]
|
658
|
+
#
|
659
|
+
# person.pets.size # => 0
|
660
|
+
# person.pets # => []
|
661
|
+
#
|
662
|
+
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
|
663
|
+
#
|
664
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
665
|
+
# responding to the +id+ and then deletes them from the database.
|
666
|
+
#
|
667
|
+
# person.pets.size # => 3
|
668
|
+
# person.pets
|
669
|
+
# # => [
|
670
|
+
# # #<Pet id: 4, name: "Benny", person_id: 1>,
|
671
|
+
# # #<Pet id: 5, name: "Brain", person_id: 1>,
|
672
|
+
# # #<Pet id: 6, name: "Boss", person_id: 1>
|
673
|
+
# # ]
|
674
|
+
#
|
675
|
+
# person.pets.destroy("4")
|
676
|
+
# # => #<Pet id: 4, name: "Benny", person_id: 1>
|
677
|
+
#
|
678
|
+
# person.pets.size # => 2
|
679
|
+
# person.pets
|
680
|
+
# # => [
|
681
|
+
# # #<Pet id: 5, name: "Brain", person_id: 1>,
|
682
|
+
# # #<Pet id: 6, name: "Boss", person_id: 1>
|
683
|
+
# # ]
|
684
|
+
#
|
685
|
+
# person.pets.destroy(5, 6)
|
686
|
+
# # => [
|
687
|
+
# # #<Pet id: 5, name: "Brain", person_id: 1>,
|
688
|
+
# # #<Pet id: 6, name: "Boss", person_id: 1>
|
689
|
+
# # ]
|
690
|
+
#
|
691
|
+
# person.pets.size # => 0
|
692
|
+
# person.pets # => []
|
693
|
+
#
|
694
|
+
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
|
695
|
+
def destroy(*records)
|
696
|
+
@association.destroy(*records)
|
697
|
+
end
|
698
|
+
|
699
|
+
# Specifies whether the records should be unique or not.
|
700
|
+
#
|
701
|
+
# class Person < ActiveRecord::Base
|
702
|
+
# has_many :pets
|
703
|
+
# end
|
704
|
+
#
|
705
|
+
# person.pets.select(:name)
|
706
|
+
# # => [
|
707
|
+
# # #<Pet name: "Fancy-Fancy">,
|
708
|
+
# # #<Pet name: "Fancy-Fancy">
|
709
|
+
# # ]
|
710
|
+
#
|
711
|
+
# person.pets.select(:name).distinct
|
712
|
+
# # => [#<Pet name: "Fancy-Fancy">]
|
713
|
+
def distinct
|
714
|
+
@association.distinct
|
715
|
+
end
|
716
|
+
alias uniq distinct
|
717
|
+
|
718
|
+
# Count all records using SQL.
|
719
|
+
#
|
720
|
+
# class Person < ActiveRecord::Base
|
721
|
+
# has_many :pets
|
722
|
+
# end
|
723
|
+
#
|
724
|
+
# person.pets.count # => 3
|
725
|
+
# person.pets
|
726
|
+
# # => [
|
727
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
728
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
729
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
730
|
+
# # ]
|
731
|
+
def count(column_name = nil)
|
732
|
+
@association.count(column_name)
|
733
|
+
end
|
734
|
+
|
735
|
+
# Returns the size of the collection. If the collection hasn't been loaded,
|
736
|
+
# it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
|
737
|
+
#
|
738
|
+
# If the collection has been already loaded +size+ and +length+ are
|
739
|
+
# equivalent. If not and you are going to need the records anyway
|
740
|
+
# +length+ will take one less query. Otherwise +size+ is more efficient.
|
741
|
+
#
|
742
|
+
# class Person < ActiveRecord::Base
|
743
|
+
# has_many :pets
|
744
|
+
# end
|
745
|
+
#
|
746
|
+
# person.pets.size # => 3
|
747
|
+
# # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1
|
748
|
+
#
|
749
|
+
# person.pets # This will execute a SELECT * FROM query
|
750
|
+
# # => [
|
751
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
752
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
753
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
754
|
+
# # ]
|
755
|
+
#
|
756
|
+
# person.pets.size # => 3
|
757
|
+
# # Because the collection is already loaded, this will behave like
|
758
|
+
# # collection.size and no SQL count query is executed.
|
759
|
+
def size
|
760
|
+
@association.size
|
761
|
+
end
|
762
|
+
|
763
|
+
# Returns the size of the collection calling +size+ on the target.
|
764
|
+
# If the collection has been already loaded, +length+ and +size+ are
|
765
|
+
# equivalent. If not and you are going to need the records anyway this
|
766
|
+
# method will take one less query. Otherwise +size+ is more efficient.
|
767
|
+
#
|
768
|
+
# class Person < ActiveRecord::Base
|
769
|
+
# has_many :pets
|
770
|
+
# end
|
771
|
+
#
|
772
|
+
# person.pets.length # => 3
|
773
|
+
# # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1
|
774
|
+
#
|
775
|
+
# # Because the collection is loaded, you can
|
776
|
+
# # call the collection with no additional queries:
|
777
|
+
# person.pets
|
778
|
+
# # => [
|
779
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
780
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
781
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
782
|
+
# # ]
|
783
|
+
def length
|
784
|
+
@association.length
|
785
|
+
end
|
786
|
+
|
787
|
+
# Returns +true+ if the collection is empty. If the collection has been
|
788
|
+
# loaded it is equivalent
|
789
|
+
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
|
790
|
+
# it is equivalent to <tt>collection.exists?</tt>. If the collection has
|
791
|
+
# not already been loaded and you are going to fetch the records anyway it
|
792
|
+
# is better to check <tt>collection.length.zero?</tt>.
|
793
|
+
#
|
794
|
+
# class Person < ActiveRecord::Base
|
795
|
+
# has_many :pets
|
796
|
+
# end
|
797
|
+
#
|
798
|
+
# person.pets.count # => 1
|
799
|
+
# person.pets.empty? # => false
|
800
|
+
#
|
801
|
+
# person.pets.delete_all
|
802
|
+
#
|
803
|
+
# person.pets.count # => 0
|
804
|
+
# person.pets.empty? # => true
|
805
|
+
def empty?
|
806
|
+
@association.empty?
|
807
|
+
end
|
808
|
+
|
809
|
+
# Returns +true+ if the collection is not empty.
|
810
|
+
#
|
811
|
+
# class Person < ActiveRecord::Base
|
812
|
+
# has_many :pets
|
813
|
+
# end
|
814
|
+
#
|
815
|
+
# person.pets.count # => 0
|
816
|
+
# person.pets.any? # => false
|
817
|
+
#
|
818
|
+
# person.pets << Pet.new(name: 'Snoop')
|
819
|
+
# person.pets.count # => 1
|
820
|
+
# person.pets.any? # => true
|
821
|
+
#
|
822
|
+
# You can also pass a +block+ to define criteria. The behavior
|
823
|
+
# is the same, it returns true if the collection based on the
|
824
|
+
# criteria is not empty.
|
825
|
+
#
|
826
|
+
# person.pets
|
827
|
+
# # => [#<Pet name: "Snoop", group: "dogs">]
|
828
|
+
#
|
829
|
+
# person.pets.any? do |pet|
|
830
|
+
# pet.group == 'cats'
|
831
|
+
# end
|
832
|
+
# # => false
|
833
|
+
#
|
834
|
+
# person.pets.any? do |pet|
|
835
|
+
# pet.group == 'dogs'
|
836
|
+
# end
|
837
|
+
# # => true
|
838
|
+
def any?(&block)
|
839
|
+
@association.any?(&block)
|
62
840
|
end
|
63
841
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
842
|
+
# Returns true if the collection has more than one record.
|
843
|
+
# Equivalent to <tt>collection.size > 1</tt>.
|
844
|
+
#
|
845
|
+
# class Person < ActiveRecord::Base
|
846
|
+
# has_many :pets
|
847
|
+
# end
|
848
|
+
#
|
849
|
+
# person.pets.count # => 1
|
850
|
+
# person.pets.many? # => false
|
851
|
+
#
|
852
|
+
# person.pets << Pet.new(name: 'Snoopy')
|
853
|
+
# person.pets.count # => 2
|
854
|
+
# person.pets.many? # => true
|
855
|
+
#
|
856
|
+
# You can also pass a +block+ to define criteria. The
|
857
|
+
# behavior is the same, it returns true if the collection
|
858
|
+
# based on the criteria has more than one record.
|
859
|
+
#
|
860
|
+
# person.pets
|
861
|
+
# # => [
|
862
|
+
# # #<Pet name: "Gorby", group: "cats">,
|
863
|
+
# # #<Pet name: "Puff", group: "cats">,
|
864
|
+
# # #<Pet name: "Snoop", group: "dogs">
|
865
|
+
# # ]
|
866
|
+
#
|
867
|
+
# person.pets.many? do |pet|
|
868
|
+
# pet.group == 'dogs'
|
869
|
+
# end
|
870
|
+
# # => false
|
871
|
+
#
|
872
|
+
# person.pets.many? do |pet|
|
873
|
+
# pet.group == 'cats'
|
874
|
+
# end
|
875
|
+
# # => true
|
876
|
+
def many?(&block)
|
877
|
+
@association.many?(&block)
|
69
878
|
end
|
70
879
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
880
|
+
# Returns +true+ if the given +record+ is present in the collection.
|
881
|
+
#
|
882
|
+
# class Person < ActiveRecord::Base
|
883
|
+
# has_many :pets
|
884
|
+
# end
|
885
|
+
#
|
886
|
+
# person.pets # => [#<Pet id: 20, name: "Snoop">]
|
887
|
+
#
|
888
|
+
# person.pets.include?(Pet.find(20)) # => true
|
889
|
+
# person.pets.include?(Pet.find(21)) # => false
|
890
|
+
def include?(record)
|
891
|
+
!!@association.include?(record)
|
75
892
|
end
|
76
893
|
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
end.tap do |record|
|
85
|
-
proxy_association.send :set_inverse_instance, record
|
86
|
-
end
|
894
|
+
def arel #:nodoc:
|
895
|
+
scope.arel
|
896
|
+
end
|
897
|
+
|
898
|
+
def proxy_association
|
899
|
+
@association
|
900
|
+
end
|
87
901
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
rescue NoMethodError => e
|
96
|
-
raise e, e.message.sub(/ for #<.*$/, " via proxy for #{target}")
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
902
|
+
# We don't want this object to be put on the scoping stack, because
|
903
|
+
# that could create an infinite loop where we call an @association
|
904
|
+
# method, which gets the current scope, which is this object, which
|
905
|
+
# delegates to @association, and so on.
|
906
|
+
def scoping
|
907
|
+
@association.scope.scoping { yield }
|
908
|
+
end
|
100
909
|
|
101
|
-
|
102
|
-
|
103
|
-
|
910
|
+
# Returns a <tt>Relation</tt> object for the records in this association
|
911
|
+
def scope
|
912
|
+
@association.scope
|
104
913
|
end
|
914
|
+
alias spawn scope
|
105
915
|
|
106
|
-
#
|
107
|
-
#
|
108
|
-
|
109
|
-
|
916
|
+
# Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
|
917
|
+
# contain the same number of elements and if each element is equal
|
918
|
+
# to the corresponding element in the +other+ array, otherwise returns
|
919
|
+
# +false+.
|
920
|
+
#
|
921
|
+
# class Person < ActiveRecord::Base
|
922
|
+
# has_many :pets
|
923
|
+
# end
|
924
|
+
#
|
925
|
+
# person.pets
|
926
|
+
# # => [
|
927
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
928
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>
|
929
|
+
# # ]
|
930
|
+
#
|
931
|
+
# other = person.pets.to_ary
|
932
|
+
#
|
933
|
+
# person.pets == other
|
934
|
+
# # => true
|
935
|
+
#
|
936
|
+
# other = [Pet.new(id: 1), Pet.new(id: 2)]
|
937
|
+
#
|
938
|
+
# person.pets == other
|
939
|
+
# # => false
|
940
|
+
def ==(other)
|
941
|
+
load_target == other
|
110
942
|
end
|
111
943
|
|
944
|
+
# Returns a new array of objects from the collection. If the collection
|
945
|
+
# hasn't been loaded, it fetches the records from the database.
|
946
|
+
#
|
947
|
+
# class Person < ActiveRecord::Base
|
948
|
+
# has_many :pets
|
949
|
+
# end
|
950
|
+
#
|
951
|
+
# person.pets
|
952
|
+
# # => [
|
953
|
+
# # #<Pet id: 4, name: "Benny", person_id: 1>,
|
954
|
+
# # #<Pet id: 5, name: "Brain", person_id: 1>,
|
955
|
+
# # #<Pet id: 6, name: "Boss", person_id: 1>
|
956
|
+
# # ]
|
957
|
+
#
|
958
|
+
# other_pets = person.pets.to_ary
|
959
|
+
# # => [
|
960
|
+
# # #<Pet id: 4, name: "Benny", person_id: 1>,
|
961
|
+
# # #<Pet id: 5, name: "Brain", person_id: 1>,
|
962
|
+
# # #<Pet id: 6, name: "Boss", person_id: 1>
|
963
|
+
# # ]
|
964
|
+
#
|
965
|
+
# other_pets.replace([Pet.new(name: 'BooGoo')])
|
966
|
+
#
|
967
|
+
# other_pets
|
968
|
+
# # => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
|
969
|
+
#
|
970
|
+
# person.pets
|
971
|
+
# # This is not affected by replace
|
972
|
+
# # => [
|
973
|
+
# # #<Pet id: 4, name: "Benny", person_id: 1>,
|
974
|
+
# # #<Pet id: 5, name: "Brain", person_id: 1>,
|
975
|
+
# # #<Pet id: 6, name: "Boss", person_id: 1>
|
976
|
+
# # ]
|
112
977
|
def to_ary
|
113
978
|
load_target.dup
|
114
979
|
end
|
115
980
|
alias_method :to_a, :to_ary
|
116
981
|
|
982
|
+
def records # :nodoc:
|
983
|
+
load_target
|
984
|
+
end
|
985
|
+
|
986
|
+
# Adds one or more +records+ to the collection by setting their foreign keys
|
987
|
+
# to the association's primary key. Returns +self+, so several appends may be
|
988
|
+
# chained together.
|
989
|
+
#
|
990
|
+
# class Person < ActiveRecord::Base
|
991
|
+
# has_many :pets
|
992
|
+
# end
|
993
|
+
#
|
994
|
+
# person.pets.size # => 0
|
995
|
+
# person.pets << Pet.new(name: 'Fancy-Fancy')
|
996
|
+
# person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
|
997
|
+
# person.pets.size # => 3
|
998
|
+
#
|
999
|
+
# person.id # => 1
|
1000
|
+
# person.pets
|
1001
|
+
# # => [
|
1002
|
+
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
1003
|
+
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
1004
|
+
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
1005
|
+
# # ]
|
117
1006
|
def <<(*records)
|
118
1007
|
proxy_association.concat(records) && self
|
119
1008
|
end
|
120
1009
|
alias_method :push, :<<
|
1010
|
+
alias_method :append, :<<
|
121
1011
|
|
1012
|
+
def prepend(*args)
|
1013
|
+
raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
# Equivalent to +delete_all+. The difference is that returns +self+, instead
|
1017
|
+
# of an array with the deleted objects, so methods can be chained. See
|
1018
|
+
# +delete_all+ for more information.
|
1019
|
+
# Note that because +delete_all+ removes records by directly
|
1020
|
+
# running an SQL query into the database, the +updated_at+ column of
|
1021
|
+
# the object is not changed.
|
122
1022
|
def clear
|
123
1023
|
delete_all
|
124
1024
|
self
|
125
1025
|
end
|
126
1026
|
|
1027
|
+
# Reloads the collection from the database. Returns +self+.
|
1028
|
+
# Equivalent to <tt>collection(true)</tt>.
|
1029
|
+
#
|
1030
|
+
# class Person < ActiveRecord::Base
|
1031
|
+
# has_many :pets
|
1032
|
+
# end
|
1033
|
+
#
|
1034
|
+
# person.pets # fetches pets from the database
|
1035
|
+
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1036
|
+
#
|
1037
|
+
# person.pets # uses the pets cache
|
1038
|
+
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1039
|
+
#
|
1040
|
+
# person.pets.reload # fetches pets from the database
|
1041
|
+
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1042
|
+
#
|
1043
|
+
# person.pets(true) # fetches pets from the database
|
1044
|
+
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
127
1045
|
def reload
|
128
1046
|
proxy_association.reload
|
129
1047
|
self
|
130
1048
|
end
|
1049
|
+
|
1050
|
+
# Unloads the association. Returns +self+.
|
1051
|
+
#
|
1052
|
+
# class Person < ActiveRecord::Base
|
1053
|
+
# has_many :pets
|
1054
|
+
# end
|
1055
|
+
#
|
1056
|
+
# person.pets # fetches pets from the database
|
1057
|
+
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1058
|
+
#
|
1059
|
+
# person.pets # uses the pets cache
|
1060
|
+
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1061
|
+
#
|
1062
|
+
# person.pets.reset # clears the pets cache
|
1063
|
+
#
|
1064
|
+
# person.pets # fetches pets from the database
|
1065
|
+
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
|
1066
|
+
def reset
|
1067
|
+
proxy_association.reset
|
1068
|
+
proxy_association.reset_scope
|
1069
|
+
self
|
1070
|
+
end
|
131
1071
|
end
|
132
1072
|
end
|
133
1073
|
end
|