activerecord 4.2.9 → 6.1.4.1
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 +964 -1382
- data/MIT-LICENSE +4 -2
- data/README.rdoc +15 -14
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +266 -251
- data/lib/active_record/association_relation.rb +40 -15
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +162 -69
- data/lib/active_record/associations/association_scope.rb +105 -130
- data/lib/active_record/associations/belongs_to_association.rb +83 -65
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +57 -43
- data/lib/active_record/associations/builder/belongs_to.rb +74 -57
- data/lib/active_record/associations/builder/collection_association.rb +15 -37
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +49 -66
- data/lib/active_record/associations/builder/has_many.rb +13 -5
- data/lib/active_record/associations/builder/has_one.rb +44 -6
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +148 -287
- data/lib/active_record/associations/collection_proxy.rb +252 -150
- data/lib/active_record/associations/foreign_association.rb +23 -1
- data/lib/active_record/associations/has_many_association.rb +56 -98
- data/lib/active_record/associations/has_many_through_association.rb +68 -89
- data/lib/active_record/associations/has_one_association.rb +73 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -81
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/join_dependency.rb +174 -169
- data/lib/active_record/associations/preloader/association.rb +108 -115
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +97 -94
- data/lib/active_record/associations/singular_association.rb +18 -39
- data/lib/active_record/associations/through_association.rb +39 -19
- data/lib/active_record/associations.rb +1845 -1598
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +18 -10
- data/lib/active_record/attribute_methods/dirty.rb +168 -148
- data/lib/active_record/attribute_methods/primary_key.rb +93 -83
- data/lib/active_record/attribute_methods/query.rb +8 -10
- data/lib/active_record/attribute_methods/read.rb +19 -79
- data/lib/active_record/attribute_methods/serialization.rb +49 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +55 -36
- data/lib/active_record/attribute_methods/write.rb +24 -55
- data/lib/active_record/attribute_methods.rb +149 -154
- data/lib/active_record/attributes.rb +234 -78
- data/lib/active_record/autosave_association.rb +133 -60
- data/lib/active_record/base.rb +46 -46
- data/lib/active_record/callbacks.rb +234 -79
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -323
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +292 -124
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +177 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +473 -255
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +869 -286
- data/lib/active_record/connection_adapters/abstract/transaction.rb +257 -91
- data/lib/active_record/connection_adapters/abstract_adapter.rb +483 -230
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +557 -640
- data/lib/active_record/connection_adapters/column.rb +67 -40
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +194 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +268 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +80 -192
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -160
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -58
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +14 -19
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -5
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +58 -54
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +145 -48
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +496 -298
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +588 -375
- data/lib/active_record/connection_adapters/schema_cache.rb +167 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -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 +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -373
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +314 -41
- data/lib/active_record/core.rb +458 -241
- data/lib/active_record/counter_cache.rb +70 -49
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +272 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -106
- data/lib/active_record/enum.rb +211 -92
- data/lib/active_record/errors.rb +224 -54
- data/lib/active_record/explain.rb +27 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +33 -14
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +275 -500
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +175 -110
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +121 -29
- data/lib/active_record/internal_metadata.rb +62 -0
- data/lib/active_record/legacy_yaml_adapter.rb +27 -5
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +98 -92
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +93 -31
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +185 -90
- data/lib/active_record/migration/compatibility.rb +295 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +673 -325
- data/lib/active_record/model_schema.rb +418 -113
- data/lib/active_record/nested_attributes.rb +263 -224
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +572 -136
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +170 -51
- data/lib/active_record/railties/console_sandbox.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +523 -199
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +454 -291
- data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
- data/lib/active_record/relation/batches.rb +217 -59
- data/lib/active_record/relation/calculations.rb +324 -249
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +316 -242
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +95 -103
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -26
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +136 -122
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -413
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +18 -20
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -343
- data/lib/active_record/result.rb +91 -47
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +134 -122
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +141 -92
- data/lib/active_record/schema_migration.rb +24 -23
- data/lib/active_record/scoping/default.rb +96 -83
- data/lib/active_record/scoping/named.rb +78 -36
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +48 -0
- data/lib/active_record/serialization.rb +8 -6
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +89 -36
- data/lib/active_record/store.rb +128 -43
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +81 -0
- data/lib/active_record/tasks/database_tasks.rb +364 -130
- data/lib/active_record/tasks/mysql_database_tasks.rb +67 -113
- data/lib/active_record/tasks/postgresql_database_tasks.rb +86 -49
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +287 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +182 -163
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -45
- data/lib/active_record/type/date_time.rb +4 -49
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +5 -4
- 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 +27 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +84 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +63 -56
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +42 -29
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -4
- data/lib/rails/generators/active_record/migration.rb +35 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +172 -65
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -163
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -106
- data/lib/active_record/attribute_set.rb +0 -81
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -110
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -19
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/string/filters"
|
3
4
|
|
4
5
|
module ActiveRecord
|
5
6
|
# = Active Record Reflection
|
@@ -7,42 +8,46 @@ module ActiveRecord
|
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
|
9
10
|
included do
|
10
|
-
class_attribute :_reflections, instance_writer: false
|
11
|
-
class_attribute :aggregate_reflections, instance_writer: false
|
12
|
-
self._reflections = {}
|
13
|
-
self.aggregate_reflections = {}
|
11
|
+
class_attribute :_reflections, instance_writer: false, default: {}
|
12
|
+
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
14
13
|
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
HasManyReflection
|
22
|
-
when :has_one
|
23
|
-
HasOneReflection
|
24
|
-
when :belongs_to
|
25
|
-
BelongsToReflection
|
26
|
-
else
|
27
|
-
raise "Unsupported Macro: #{macro}"
|
28
|
-
end
|
29
|
-
|
30
|
-
reflection = klass.new(name, scope, options, ar)
|
31
|
-
options[:through] ? ThroughReflection.new(reflection) : reflection
|
32
|
-
end
|
15
|
+
class << self
|
16
|
+
def create(macro, name, scope, options, ar)
|
17
|
+
reflection = reflection_class_for(macro).new(name, scope, options, ar)
|
18
|
+
options[:through] ? ThroughReflection.new(reflection) : reflection
|
19
|
+
end
|
33
20
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
21
|
+
def add_reflection(ar, name, reflection)
|
22
|
+
ar.clear_reflections_cache
|
23
|
+
name = -name.to_s
|
24
|
+
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
25
|
+
end
|
38
26
|
|
39
|
-
|
40
|
-
|
27
|
+
def add_aggregate_reflection(ar, name, reflection)
|
28
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def reflection_class_for(macro)
|
33
|
+
case macro
|
34
|
+
when :composed_of
|
35
|
+
AggregateReflection
|
36
|
+
when :has_many
|
37
|
+
HasManyReflection
|
38
|
+
when :has_one
|
39
|
+
HasOneReflection
|
40
|
+
when :belongs_to
|
41
|
+
BelongsToReflection
|
42
|
+
else
|
43
|
+
raise "Unsupported Macro: #{macro}"
|
44
|
+
end
|
45
|
+
end
|
41
46
|
end
|
42
47
|
|
43
|
-
# \Reflection enables
|
44
|
-
#
|
45
|
-
#
|
48
|
+
# \Reflection enables the ability to examine the associations and aggregations of
|
49
|
+
# Active Record classes and objects. This information, for example,
|
50
|
+
# can be used in a form builder that takes an Active Record object
|
46
51
|
# and creates input fields for all of the attributes depending on their type
|
47
52
|
# and displays the associations to other objects.
|
48
53
|
#
|
@@ -62,20 +67,20 @@ module ActiveRecord
|
|
62
67
|
aggregate_reflections[aggregation.to_s]
|
63
68
|
end
|
64
69
|
|
65
|
-
# Returns a Hash of name of the reflection as the key and
|
70
|
+
# Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
|
66
71
|
#
|
67
72
|
# Account.reflections # => {"balance" => AggregateReflection}
|
68
73
|
#
|
69
|
-
# @api public
|
70
74
|
def reflections
|
71
75
|
@__reflections ||= begin
|
72
76
|
ref = {}
|
73
77
|
|
74
78
|
_reflections.each do |name, reflection|
|
75
|
-
|
79
|
+
parent_reflection = reflection.parent_reflection
|
76
80
|
|
77
|
-
if
|
78
|
-
|
81
|
+
if parent_reflection
|
82
|
+
parent_name = parent_reflection.name
|
83
|
+
ref[parent_name.to_s] = parent_reflection
|
79
84
|
else
|
80
85
|
ref[name] = reflection
|
81
86
|
end
|
@@ -95,10 +100,10 @@ module ActiveRecord
|
|
95
100
|
# Account.reflect_on_all_associations # returns an array of all associations
|
96
101
|
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
97
102
|
#
|
98
|
-
# @api public
|
99
103
|
def reflect_on_all_associations(macro = nil)
|
100
104
|
association_reflections = reflections.values
|
101
|
-
|
105
|
+
association_reflections.select! { |reflection| reflection.macro == macro } if macro
|
106
|
+
association_reflections
|
102
107
|
end
|
103
108
|
|
104
109
|
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
@@ -106,31 +111,42 @@ module ActiveRecord
|
|
106
111
|
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
107
112
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
108
113
|
#
|
109
|
-
# @api public
|
110
114
|
def reflect_on_association(association)
|
111
115
|
reflections[association.to_s]
|
112
116
|
end
|
113
117
|
|
114
|
-
# @api private
|
115
118
|
def _reflect_on_association(association) #:nodoc:
|
116
119
|
_reflections[association.to_s]
|
117
120
|
end
|
118
121
|
|
119
122
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
120
|
-
#
|
121
|
-
# @api public
|
122
123
|
def reflect_on_all_autosave_associations
|
123
124
|
reflections.values.select { |reflection| reflection.options[:autosave] }
|
124
125
|
end
|
125
126
|
|
126
|
-
def clear_reflections_cache
|
127
|
+
def clear_reflections_cache # :nodoc:
|
127
128
|
@__reflections = nil
|
128
129
|
end
|
129
130
|
end
|
130
131
|
|
131
|
-
# Holds all the methods that are shared between MacroReflection
|
132
|
-
#
|
132
|
+
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
133
|
+
#
|
134
|
+
# AbstractReflection
|
135
|
+
# MacroReflection
|
136
|
+
# AggregateReflection
|
137
|
+
# AssociationReflection
|
138
|
+
# HasManyReflection
|
139
|
+
# HasOneReflection
|
140
|
+
# BelongsToReflection
|
141
|
+
# HasAndBelongsToManyReflection
|
142
|
+
# ThroughReflection
|
143
|
+
# PolymorphicReflection
|
144
|
+
# RuntimeReflection
|
133
145
|
class AbstractReflection # :nodoc:
|
146
|
+
def through_reflection?
|
147
|
+
false
|
148
|
+
end
|
149
|
+
|
134
150
|
def table_name
|
135
151
|
klass.table_name
|
136
152
|
end
|
@@ -141,35 +157,70 @@ module ActiveRecord
|
|
141
157
|
klass.new(attributes, &block)
|
142
158
|
end
|
143
159
|
|
144
|
-
def quoted_table_name
|
145
|
-
klass.quoted_table_name
|
146
|
-
end
|
147
|
-
|
148
|
-
def primary_key_type
|
149
|
-
klass.type_for_attribute(klass.primary_key)
|
150
|
-
end
|
151
|
-
|
152
160
|
# Returns the class name for the macro.
|
153
161
|
#
|
154
162
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
155
163
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
156
164
|
def class_name
|
157
|
-
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
165
|
+
@class_name ||= -(options[:class_name] || derive_class_name).to_s
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns a list of scopes that should be applied for this Reflection
|
169
|
+
# object when querying the database.
|
170
|
+
def scopes
|
171
|
+
scope ? [scope] : []
|
158
172
|
end
|
159
173
|
|
160
|
-
|
174
|
+
def join_scope(table, foreign_table, foreign_klass)
|
175
|
+
predicate_builder = predicate_builder(table)
|
176
|
+
scope_chain_items = join_scopes(table, predicate_builder)
|
177
|
+
klass_scope = klass_join_scope(table, predicate_builder)
|
178
|
+
|
179
|
+
if type
|
180
|
+
klass_scope.where!(type => foreign_klass.polymorphic_name)
|
181
|
+
end
|
182
|
+
|
183
|
+
scope_chain_items.inject(klass_scope, &:merge!)
|
184
|
+
|
185
|
+
primary_key = join_primary_key
|
186
|
+
foreign_key = join_foreign_key
|
187
|
+
|
188
|
+
klass_scope.where!(table[primary_key].eq(foreign_table[foreign_key]))
|
189
|
+
|
190
|
+
if klass.finder_needs_type_condition?
|
191
|
+
klass_scope.where!(klass.send(:type_condition, table))
|
192
|
+
end
|
161
193
|
|
162
|
-
|
163
|
-
JoinKeys.new(foreign_key, active_record_primary_key)
|
194
|
+
klass_scope
|
164
195
|
end
|
165
196
|
|
166
|
-
def
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
197
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
198
|
+
if scope
|
199
|
+
[scope_for(build_scope(table, predicate_builder, klass))]
|
200
|
+
else
|
201
|
+
[]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def klass_join_scope(table, predicate_builder) # :nodoc:
|
206
|
+
relation = build_scope(table, predicate_builder)
|
207
|
+
klass.scope_for_association(relation)
|
208
|
+
end
|
171
209
|
|
172
|
-
|
210
|
+
def constraints
|
211
|
+
chain.flat_map(&:scopes)
|
212
|
+
end
|
213
|
+
|
214
|
+
def counter_cache_column
|
215
|
+
@counter_cache_column ||= if belongs_to?
|
216
|
+
if options[:counter_cache] == true
|
217
|
+
-"#{active_record.name.demodulize.underscore.pluralize}_count"
|
218
|
+
elsif options[:counter_cache]
|
219
|
+
-options[:counter_cache].to_s
|
220
|
+
end
|
221
|
+
else
|
222
|
+
-(options[:counter_cache]&.to_s || "#{name}_count")
|
223
|
+
end
|
173
224
|
end
|
174
225
|
|
175
226
|
def inverse_of
|
@@ -185,17 +236,80 @@ module ActiveRecord
|
|
185
236
|
end
|
186
237
|
end
|
187
238
|
end
|
239
|
+
|
240
|
+
# This shit is nasty. We need to avoid the following situation:
|
241
|
+
#
|
242
|
+
# * An associated record is deleted via record.destroy
|
243
|
+
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
244
|
+
# :counter_cache options which points back at our owner. So they update the
|
245
|
+
# counter cache.
|
246
|
+
# * In which case, we must make sure to *not* update the counter cache, or else
|
247
|
+
# it will be decremented twice.
|
248
|
+
#
|
249
|
+
# Hence this method.
|
250
|
+
def inverse_which_updates_counter_cache
|
251
|
+
return @inverse_which_updates_counter_cache if defined?(@inverse_which_updates_counter_cache)
|
252
|
+
@inverse_which_updates_counter_cache = klass.reflect_on_all_associations(:belongs_to).find do |inverse|
|
253
|
+
inverse.counter_cache_column == counter_cache_column
|
254
|
+
end
|
255
|
+
end
|
256
|
+
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
257
|
+
|
258
|
+
def inverse_updates_counter_in_memory?
|
259
|
+
inverse_of && inverse_which_updates_counter_cache == inverse_of
|
260
|
+
end
|
261
|
+
|
262
|
+
# Returns whether a counter cache should be used for this association.
|
263
|
+
#
|
264
|
+
# The counter_cache option must be given on either the owner or inverse
|
265
|
+
# association, and the column must be present on the owner.
|
266
|
+
def has_cached_counter?
|
267
|
+
options[:counter_cache] ||
|
268
|
+
inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache] &&
|
269
|
+
active_record.has_attribute?(counter_cache_column)
|
270
|
+
end
|
271
|
+
|
272
|
+
def counter_must_be_updated_by_has_many?
|
273
|
+
!inverse_updates_counter_in_memory? && has_cached_counter?
|
274
|
+
end
|
275
|
+
|
276
|
+
def alias_candidate(name)
|
277
|
+
"#{plural_name}_#{name}"
|
278
|
+
end
|
279
|
+
|
280
|
+
def chain
|
281
|
+
collect_join_chain
|
282
|
+
end
|
283
|
+
|
284
|
+
def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
|
285
|
+
Relation.create(
|
286
|
+
klass,
|
287
|
+
table: table,
|
288
|
+
predicate_builder: predicate_builder
|
289
|
+
)
|
290
|
+
end
|
291
|
+
|
292
|
+
def strict_loading?
|
293
|
+
options[:strict_loading]
|
294
|
+
end
|
295
|
+
|
296
|
+
protected
|
297
|
+
def actual_source_reflection # FIXME: this is a horrible name
|
298
|
+
self
|
299
|
+
end
|
300
|
+
|
301
|
+
private
|
302
|
+
def predicate_builder(table)
|
303
|
+
PredicateBuilder.new(TableMetadata.new(klass, table))
|
304
|
+
end
|
305
|
+
|
306
|
+
def primary_key(klass)
|
307
|
+
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
308
|
+
end
|
188
309
|
end
|
310
|
+
|
189
311
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
190
312
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
191
|
-
#
|
192
|
-
# MacroReflection
|
193
|
-
# AssociationReflection
|
194
|
-
# AggregateReflection
|
195
|
-
# HasManyReflection
|
196
|
-
# HasOneReflection
|
197
|
-
# BelongsToReflection
|
198
|
-
# ThroughReflection
|
199
313
|
class MacroReflection < AbstractReflection
|
200
314
|
# Returns the name of the macro.
|
201
315
|
#
|
@@ -226,9 +340,8 @@ module ActiveRecord
|
|
226
340
|
end
|
227
341
|
|
228
342
|
def autosave=(autosave)
|
229
|
-
@automatic_inverse_of = false
|
230
343
|
@options[:autosave] = autosave
|
231
|
-
|
344
|
+
parent_reflection = self.parent_reflection
|
232
345
|
if parent_reflection
|
233
346
|
parent_reflection.autosave = autosave
|
234
347
|
end
|
@@ -238,6 +351,17 @@ module ActiveRecord
|
|
238
351
|
#
|
239
352
|
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
240
353
|
# <tt>has_many :clients</tt> returns the Client class
|
354
|
+
#
|
355
|
+
# class Company < ActiveRecord::Base
|
356
|
+
# has_many :clients
|
357
|
+
# end
|
358
|
+
#
|
359
|
+
# Company.reflect_on_association(:clients).klass
|
360
|
+
# # => Client
|
361
|
+
#
|
362
|
+
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
363
|
+
# a new association object. Use +build_association+ or +create_association+
|
364
|
+
# instead. This allows plugins to hook into association object creation.
|
241
365
|
def klass
|
242
366
|
@klass ||= compute_class(class_name)
|
243
367
|
end
|
@@ -256,14 +380,17 @@ module ActiveRecord
|
|
256
380
|
active_record == other_aggregation.active_record
|
257
381
|
end
|
258
382
|
|
383
|
+
def scope_for(relation, owner = nil)
|
384
|
+
relation.instance_exec(owner, &scope) || relation
|
385
|
+
end
|
386
|
+
|
259
387
|
private
|
260
388
|
def derive_class_name
|
261
389
|
name.to_s.camelize
|
262
390
|
end
|
263
391
|
end
|
264
392
|
|
265
|
-
|
266
|
-
# Holds all the meta-data about an aggregation as it was specified in the
|
393
|
+
# Holds all the metadata about an aggregation as it was specified in the
|
267
394
|
# Active Record class.
|
268
395
|
class AggregateReflection < MacroReflection #:nodoc:
|
269
396
|
def mapping
|
@@ -272,50 +399,36 @@ module ActiveRecord
|
|
272
399
|
end
|
273
400
|
end
|
274
401
|
|
275
|
-
# Holds all the
|
402
|
+
# Holds all the metadata about an association as it was specified in the
|
276
403
|
# Active Record class.
|
277
404
|
class AssociationReflection < MacroReflection #:nodoc:
|
278
|
-
# Returns the target association's class.
|
279
|
-
#
|
280
|
-
# class Author < ActiveRecord::Base
|
281
|
-
# has_many :books
|
282
|
-
# end
|
283
|
-
#
|
284
|
-
# Author.reflect_on_association(:books).klass
|
285
|
-
# # => Book
|
286
|
-
#
|
287
|
-
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
288
|
-
# a new association object. Use +build_association+ or +create_association+
|
289
|
-
# instead. This allows plugins to hook into association object creation.
|
290
|
-
def klass
|
291
|
-
@klass ||= compute_class(class_name)
|
292
|
-
end
|
293
|
-
|
294
405
|
def compute_class(name)
|
406
|
+
if polymorphic?
|
407
|
+
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
408
|
+
end
|
295
409
|
active_record.send(:compute_type, name)
|
296
410
|
end
|
297
411
|
|
298
412
|
attr_reader :type, :foreign_type
|
299
|
-
attr_accessor :parent_reflection #
|
413
|
+
attr_accessor :parent_reflection # Reflection
|
300
414
|
|
301
415
|
def initialize(name, scope, options, active_record)
|
302
416
|
super
|
303
|
-
@
|
304
|
-
@
|
305
|
-
@foreign_type = options[:foreign_type] || "#{name}_type"
|
417
|
+
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
418
|
+
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
306
419
|
@constructable = calculate_constructable(macro, options)
|
307
|
-
|
308
|
-
|
420
|
+
|
421
|
+
if options[:class_name] && options[:class_name].class == Class
|
422
|
+
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
|
423
|
+
end
|
309
424
|
end
|
310
425
|
|
311
|
-
def association_scope_cache(
|
312
|
-
key =
|
426
|
+
def association_scope_cache(klass, owner, &block)
|
427
|
+
key = self
|
313
428
|
if polymorphic?
|
314
429
|
key = [key, owner._read_attribute(@foreign_type)]
|
315
430
|
end
|
316
|
-
|
317
|
-
@association_scope_cache[key] ||= yield
|
318
|
-
}
|
431
|
+
klass.cached_find_by_statement(key, &block)
|
319
432
|
end
|
320
433
|
|
321
434
|
def constructable? # :nodoc:
|
@@ -323,32 +436,31 @@ module ActiveRecord
|
|
323
436
|
end
|
324
437
|
|
325
438
|
def join_table
|
326
|
-
@join_table ||= options[:join_table] || derive_join_table
|
439
|
+
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
327
440
|
end
|
328
441
|
|
329
442
|
def foreign_key
|
330
|
-
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
443
|
+
@foreign_key ||= -(options[:foreign_key]&.to_s || derive_foreign_key)
|
331
444
|
end
|
332
445
|
|
333
446
|
def association_foreign_key
|
334
|
-
@association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
|
447
|
+
@association_foreign_key ||= -(options[:association_foreign_key]&.to_s || class_name.foreign_key)
|
335
448
|
end
|
336
449
|
|
337
|
-
# klass option is necessary to support loading polymorphic associations
|
338
450
|
def association_primary_key(klass = nil)
|
339
|
-
|
451
|
+
primary_key(klass || self.klass)
|
340
452
|
end
|
341
453
|
|
342
454
|
def active_record_primary_key
|
343
|
-
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
|
455
|
+
@active_record_primary_key ||= -(options[:primary_key]&.to_s || primary_key(active_record))
|
344
456
|
end
|
345
457
|
|
346
|
-
def
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
458
|
+
def join_primary_key(klass = nil)
|
459
|
+
foreign_key
|
460
|
+
end
|
461
|
+
|
462
|
+
def join_foreign_key
|
463
|
+
active_record_primary_key
|
352
464
|
end
|
353
465
|
|
354
466
|
def check_validity!
|
@@ -358,21 +470,18 @@ module ActiveRecord
|
|
358
470
|
def check_preloadable!
|
359
471
|
return unless scope
|
360
472
|
|
361
|
-
|
362
|
-
|
473
|
+
unless scope.arity == 0
|
474
|
+
raise ArgumentError, <<-MSG.squish
|
363
475
|
The association scope '#{name}' is instance dependent (the scope
|
364
|
-
block takes an argument). Preloading
|
365
|
-
|
366
|
-
passed to the association scope. This will most likely result in
|
367
|
-
broken or incorrect behavior. Joining, Preloading and eager loading
|
368
|
-
of these associations is deprecated and will be removed in the future.
|
476
|
+
block takes an argument). Preloading instance dependent scopes is
|
477
|
+
not supported.
|
369
478
|
MSG
|
370
479
|
end
|
371
480
|
end
|
372
481
|
alias :check_eager_loadable! :check_preloadable!
|
373
482
|
|
374
483
|
def join_id_for(owner) # :nodoc:
|
375
|
-
owner[
|
484
|
+
owner[join_foreign_key]
|
376
485
|
end
|
377
486
|
|
378
487
|
def through_reflection
|
@@ -385,18 +494,22 @@ module ActiveRecord
|
|
385
494
|
|
386
495
|
# A chain of reflections from this one back to the owner. For more see the explanation in
|
387
496
|
# ThroughReflection.
|
388
|
-
def
|
497
|
+
def collect_join_chain
|
389
498
|
[self]
|
390
499
|
end
|
391
500
|
|
501
|
+
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
502
|
+
# SQL queries on associations.
|
503
|
+
def clear_association_scope_cache # :nodoc:
|
504
|
+
klass.initialize_find_by_cache
|
505
|
+
end
|
506
|
+
|
392
507
|
def nested?
|
393
508
|
false
|
394
509
|
end
|
395
510
|
|
396
|
-
|
397
|
-
|
398
|
-
def scope_chain
|
399
|
-
scope ? [[scope]] : [[]]
|
511
|
+
def has_scope?
|
512
|
+
scope
|
400
513
|
end
|
401
514
|
|
402
515
|
def has_inverse?
|
@@ -444,69 +557,48 @@ module ActiveRecord
|
|
444
557
|
# Returns +true+ if +self+ is a +has_one+ reflection.
|
445
558
|
def has_one?; false; end
|
446
559
|
|
447
|
-
def association_class
|
448
|
-
case macro
|
449
|
-
when :belongs_to
|
450
|
-
if polymorphic?
|
451
|
-
Associations::BelongsToPolymorphicAssociation
|
452
|
-
else
|
453
|
-
Associations::BelongsToAssociation
|
454
|
-
end
|
455
|
-
when :has_many
|
456
|
-
if options[:through]
|
457
|
-
Associations::HasManyThroughAssociation
|
458
|
-
else
|
459
|
-
Associations::HasManyAssociation
|
460
|
-
end
|
461
|
-
when :has_one
|
462
|
-
if options[:through]
|
463
|
-
Associations::HasOneThroughAssociation
|
464
|
-
else
|
465
|
-
Associations::HasOneAssociation
|
466
|
-
end
|
467
|
-
end
|
468
|
-
end
|
560
|
+
def association_class; raise NotImplementedError; end
|
469
561
|
|
470
562
|
def polymorphic?
|
471
563
|
options[:polymorphic]
|
472
564
|
end
|
473
565
|
|
474
566
|
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
475
|
-
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:
|
567
|
+
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
|
476
568
|
|
477
|
-
|
569
|
+
def add_as_source(seed)
|
570
|
+
seed
|
571
|
+
end
|
478
572
|
|
479
|
-
|
480
|
-
|
481
|
-
|
573
|
+
def add_as_polymorphic_through(reflection, seed)
|
574
|
+
seed + [PolymorphicReflection.new(self, reflection)]
|
575
|
+
end
|
482
576
|
|
483
|
-
|
577
|
+
def add_as_through(seed)
|
578
|
+
seed + [self]
|
579
|
+
end
|
484
580
|
|
581
|
+
def extensions
|
582
|
+
Array(options[:extend])
|
583
|
+
end
|
584
|
+
|
585
|
+
private
|
485
586
|
def calculate_constructable(macro, options)
|
486
|
-
|
487
|
-
when :belongs_to
|
488
|
-
!polymorphic?
|
489
|
-
when :has_one
|
490
|
-
!options[:through]
|
491
|
-
else
|
492
|
-
true
|
493
|
-
end
|
587
|
+
true
|
494
588
|
end
|
495
589
|
|
496
590
|
# Attempts to find the inverse association name automatically.
|
497
591
|
# If it cannot find a suitable inverse association name, it returns
|
498
|
-
# nil
|
592
|
+
# +nil+.
|
499
593
|
def inverse_name
|
500
|
-
|
501
|
-
|
502
|
-
nil
|
503
|
-
else
|
504
|
-
@automatic_inverse_of ||= automatic_inverse_of
|
505
|
-
end
|
594
|
+
unless defined?(@inverse_name)
|
595
|
+
@inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
|
506
596
|
end
|
597
|
+
|
598
|
+
@inverse_name
|
507
599
|
end
|
508
600
|
|
509
|
-
# returns either nil or the inverse association name that it finds.
|
601
|
+
# returns either +nil+ or the inverse association name that it finds.
|
510
602
|
def automatic_inverse_of
|
511
603
|
if can_find_inverse_of_automatically?(self)
|
512
604
|
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
@@ -520,23 +612,19 @@ module ActiveRecord
|
|
520
612
|
end
|
521
613
|
|
522
614
|
if valid_inverse_reflection?(reflection)
|
523
|
-
|
615
|
+
inverse_name
|
524
616
|
end
|
525
617
|
end
|
526
|
-
|
527
|
-
false
|
528
618
|
end
|
529
619
|
|
530
620
|
# Checks if the inverse reflection that is returned from the
|
531
621
|
# +automatic_inverse_of+ method is a valid reflection. We must
|
532
622
|
# make sure that the reflection's active_record name matches up
|
533
623
|
# with the current reflection's klass name.
|
534
|
-
#
|
535
|
-
# Note: klass will always be valid because when there's a NameError
|
536
|
-
# from calling +klass+, +reflection+ will already be set to false.
|
537
624
|
def valid_inverse_reflection?(reflection)
|
538
625
|
reflection &&
|
539
|
-
|
626
|
+
foreign_key == reflection.foreign_key &&
|
627
|
+
klass <= reflection.active_record &&
|
540
628
|
can_find_inverse_of_automatically?(reflection)
|
541
629
|
end
|
542
630
|
|
@@ -544,9 +632,8 @@ module ActiveRecord
|
|
544
632
|
# us from being able to guess the inverse automatically. First, the
|
545
633
|
# <tt>inverse_of</tt> option cannot be set to false. Second, we must
|
546
634
|
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
|
547
|
-
# Third, we must not have options such as <tt>:
|
548
|
-
#
|
549
|
-
# inverse association.
|
635
|
+
# Third, we must not have options such as <tt>:foreign_key</tt>
|
636
|
+
# which prevent us from correctly guessing the inverse association.
|
550
637
|
#
|
551
638
|
# Anything with a scope can additionally ruin our attempt at finding an
|
552
639
|
# inverse, so we exclude reflections with scopes.
|
@@ -576,56 +663,86 @@ module ActiveRecord
|
|
576
663
|
def derive_join_table
|
577
664
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
578
665
|
end
|
579
|
-
|
580
|
-
def primary_key(klass)
|
581
|
-
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
582
|
-
end
|
583
666
|
end
|
584
667
|
|
585
668
|
class HasManyReflection < AssociationReflection # :nodoc:
|
586
|
-
def initialize(name, scope, options, active_record)
|
587
|
-
super(name, scope, options, active_record)
|
588
|
-
end
|
589
|
-
|
590
669
|
def macro; :has_many; end
|
591
670
|
|
592
671
|
def collection?; true; end
|
593
|
-
end
|
594
672
|
|
595
|
-
|
596
|
-
|
597
|
-
|
673
|
+
def association_class
|
674
|
+
if options[:through]
|
675
|
+
Associations::HasManyThroughAssociation
|
676
|
+
else
|
677
|
+
Associations::HasManyAssociation
|
678
|
+
end
|
598
679
|
end
|
680
|
+
end
|
599
681
|
|
682
|
+
class HasOneReflection < AssociationReflection # :nodoc:
|
600
683
|
def macro; :has_one; end
|
601
684
|
|
602
685
|
def has_one?; true; end
|
603
|
-
end
|
604
686
|
|
605
|
-
|
606
|
-
|
607
|
-
|
687
|
+
def association_class
|
688
|
+
if options[:through]
|
689
|
+
Associations::HasOneThroughAssociation
|
690
|
+
else
|
691
|
+
Associations::HasOneAssociation
|
692
|
+
end
|
608
693
|
end
|
609
694
|
|
695
|
+
private
|
696
|
+
def calculate_constructable(macro, options)
|
697
|
+
!options[:through]
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
class BelongsToReflection < AssociationReflection # :nodoc:
|
610
702
|
def macro; :belongs_to; end
|
611
703
|
|
612
704
|
def belongs_to?; true; end
|
613
705
|
|
614
|
-
def
|
615
|
-
|
616
|
-
|
706
|
+
def association_class
|
707
|
+
if polymorphic?
|
708
|
+
Associations::BelongsToPolymorphicAssociation
|
709
|
+
else
|
710
|
+
Associations::BelongsToAssociation
|
711
|
+
end
|
617
712
|
end
|
618
713
|
|
619
|
-
|
620
|
-
|
714
|
+
# klass option is necessary to support loading polymorphic associations
|
715
|
+
def association_primary_key(klass = nil)
|
716
|
+
if primary_key = options[:primary_key]
|
717
|
+
@association_primary_key ||= -primary_key.to_s
|
718
|
+
else
|
719
|
+
primary_key(klass || self.klass)
|
720
|
+
end
|
621
721
|
end
|
622
|
-
end
|
623
722
|
|
624
|
-
|
625
|
-
|
626
|
-
|
723
|
+
def join_primary_key(klass = nil)
|
724
|
+
polymorphic? ? association_primary_key(klass) : association_primary_key
|
725
|
+
end
|
726
|
+
|
727
|
+
def join_foreign_key
|
728
|
+
foreign_key
|
729
|
+
end
|
730
|
+
|
731
|
+
def join_foreign_type
|
732
|
+
foreign_type
|
627
733
|
end
|
628
734
|
|
735
|
+
private
|
736
|
+
def can_find_inverse_of_automatically?(_)
|
737
|
+
!polymorphic? && super
|
738
|
+
end
|
739
|
+
|
740
|
+
def calculate_constructable(macro, options)
|
741
|
+
!polymorphic?
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
629
746
|
def macro; :has_and_belongs_to_many; end
|
630
747
|
|
631
748
|
def collection?
|
@@ -633,19 +750,22 @@ module ActiveRecord
|
|
633
750
|
end
|
634
751
|
end
|
635
752
|
|
636
|
-
# Holds all the
|
753
|
+
# Holds all the metadata about a :through association as it was specified
|
637
754
|
# in the Active Record class.
|
638
755
|
class ThroughReflection < AbstractReflection #:nodoc:
|
639
|
-
|
640
|
-
|
641
|
-
:active_record_primary_key, :type, :to => :source_reflection
|
756
|
+
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
|
757
|
+
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
642
758
|
|
643
759
|
def initialize(delegate_reflection)
|
644
760
|
@delegate_reflection = delegate_reflection
|
645
|
-
@klass
|
761
|
+
@klass = delegate_reflection.options[:anonymous_class]
|
646
762
|
@source_reflection_name = delegate_reflection.options[:source]
|
647
763
|
end
|
648
764
|
|
765
|
+
def through_reflection?
|
766
|
+
true
|
767
|
+
end
|
768
|
+
|
649
769
|
def klass
|
650
770
|
@klass ||= delegate_reflection.compute_class(class_name)
|
651
771
|
end
|
@@ -704,74 +824,35 @@ module ActiveRecord
|
|
704
824
|
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
|
705
825
|
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
|
706
826
|
#
|
707
|
-
def
|
708
|
-
|
709
|
-
a = source_reflection.chain
|
710
|
-
b = through_reflection.chain
|
711
|
-
chain = a + b
|
712
|
-
chain[0] = self # Use self so we don't lose the information from :source_type
|
713
|
-
chain
|
714
|
-
end
|
827
|
+
def collect_join_chain
|
828
|
+
collect_join_reflections [self]
|
715
829
|
end
|
716
830
|
|
717
|
-
#
|
718
|
-
#
|
719
|
-
#
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
#
|
724
|
-
# class Article
|
725
|
-
# has_many :comments
|
726
|
-
# has_many :comment_tags, through: :comments, source: :tags
|
727
|
-
# end
|
728
|
-
#
|
729
|
-
# class Comment
|
730
|
-
# has_many :tags
|
731
|
-
# end
|
732
|
-
#
|
733
|
-
# There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
|
734
|
-
# but only Comment.tags will be represented in the #chain. So this method creates an array
|
735
|
-
# of scopes corresponding to the chain.
|
736
|
-
def scope_chain
|
737
|
-
@scope_chain ||= begin
|
738
|
-
scope_chain = source_reflection.scope_chain.map(&:dup)
|
739
|
-
|
740
|
-
# Add to it the scope from this reflection (if any)
|
741
|
-
scope_chain.first << scope if scope
|
742
|
-
|
743
|
-
through_scope_chain = through_reflection.scope_chain.map(&:dup)
|
744
|
-
|
745
|
-
if options[:source_type]
|
746
|
-
type = foreign_type
|
747
|
-
source_type = options[:source_type]
|
748
|
-
through_scope_chain.first << lambda { |object|
|
749
|
-
where(type => source_type)
|
750
|
-
}
|
751
|
-
end
|
752
|
-
|
753
|
-
# Recursively fill out the rest of the array from the through reflection
|
754
|
-
scope_chain + through_scope_chain
|
755
|
-
end
|
831
|
+
# This is for clearing cache on the reflection. Useful for tests that need to compare
|
832
|
+
# SQL queries on associations.
|
833
|
+
def clear_association_scope_cache # :nodoc:
|
834
|
+
delegate_reflection.clear_association_scope_cache
|
835
|
+
source_reflection.clear_association_scope_cache
|
836
|
+
through_reflection.clear_association_scope_cache
|
756
837
|
end
|
757
838
|
|
758
|
-
def
|
759
|
-
source_reflection.
|
839
|
+
def scopes
|
840
|
+
source_reflection.scopes + super
|
760
841
|
end
|
761
842
|
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
ActiveRecord::Base.source_macro is deprecated and will be removed
|
766
|
-
without replacement.
|
767
|
-
MSG
|
843
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
844
|
+
source_reflection.join_scopes(table, predicate_builder, klass) + super
|
845
|
+
end
|
768
846
|
|
769
|
-
|
847
|
+
def has_scope?
|
848
|
+
scope || options[:source_type] ||
|
849
|
+
source_reflection.has_scope? ||
|
850
|
+
through_reflection.has_scope?
|
770
851
|
end
|
771
852
|
|
772
853
|
# A through association is nested if there would be more than one join table
|
773
854
|
def nested?
|
774
|
-
|
855
|
+
source_reflection.through_reflection? || through_reflection.through_reflection?
|
775
856
|
end
|
776
857
|
|
777
858
|
# We want to use the klass from this reflection, rather than just delegate straight to
|
@@ -780,7 +861,15 @@ module ActiveRecord
|
|
780
861
|
def association_primary_key(klass = nil)
|
781
862
|
# Get the "actual" source reflection if the immediate source reflection has a
|
782
863
|
# source reflection itself
|
783
|
-
actual_source_reflection.options[:primary_key]
|
864
|
+
if primary_key = actual_source_reflection.options[:primary_key]
|
865
|
+
@association_primary_key ||= -primary_key.to_s
|
866
|
+
else
|
867
|
+
primary_key(klass || self.klass)
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
def join_primary_key(klass = self.klass)
|
872
|
+
source_reflection.join_primary_key(klass)
|
784
873
|
end
|
785
874
|
|
786
875
|
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
|
@@ -801,21 +890,19 @@ module ActiveRecord
|
|
801
890
|
def source_reflection_name # :nodoc:
|
802
891
|
return @source_reflection_name if @source_reflection_name
|
803
892
|
|
804
|
-
names = [name.to_s.singularize, name].collect
|
893
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
805
894
|
names = names.find_all { |n|
|
806
895
|
through_reflection.klass._reflect_on_association(n)
|
807
896
|
}
|
808
897
|
|
809
898
|
if names.length > 1
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
" #{macro} :#{name}, #{example_options}\n" \
|
818
|
-
" end"
|
899
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
900
|
+
active_record.name,
|
901
|
+
macro,
|
902
|
+
name,
|
903
|
+
options,
|
904
|
+
source_reflection_names
|
905
|
+
)
|
819
906
|
end
|
820
907
|
|
821
908
|
@source_reflection_name = names.first
|
@@ -829,13 +916,9 @@ module ActiveRecord
|
|
829
916
|
through_reflection.options
|
830
917
|
end
|
831
918
|
|
832
|
-
def join_id_for(owner) # :nodoc:
|
833
|
-
source_reflection.join_id_for(owner)
|
834
|
-
end
|
835
|
-
|
836
919
|
def check_validity!
|
837
920
|
if through_reflection.nil?
|
838
|
-
raise HasManyThroughAssociationNotFoundError.new(active_record
|
921
|
+
raise HasManyThroughAssociationNotFoundError.new(active_record, self)
|
839
922
|
end
|
840
923
|
|
841
924
|
if through_reflection.polymorphic?
|
@@ -862,22 +945,54 @@ module ActiveRecord
|
|
862
945
|
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
|
863
946
|
end
|
864
947
|
|
948
|
+
if parent_reflection.nil?
|
949
|
+
reflections = active_record.reflections.keys.map(&:to_sym)
|
950
|
+
|
951
|
+
if reflections.index(through_reflection.name) > reflections.index(name)
|
952
|
+
raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
865
956
|
check_validity_of_inverse!
|
866
957
|
end
|
867
958
|
|
868
|
-
|
959
|
+
def constraints
|
960
|
+
scope_chain = source_reflection.constraints
|
961
|
+
scope_chain << scope if scope
|
962
|
+
scope_chain
|
963
|
+
end
|
964
|
+
|
965
|
+
def add_as_source(seed)
|
966
|
+
collect_join_reflections seed
|
967
|
+
end
|
968
|
+
|
969
|
+
def add_as_polymorphic_through(reflection, seed)
|
970
|
+
collect_join_reflections(seed + [PolymorphicReflection.new(self, reflection)])
|
971
|
+
end
|
972
|
+
|
973
|
+
def add_as_through(seed)
|
974
|
+
collect_join_reflections(seed + [self])
|
975
|
+
end
|
869
976
|
|
977
|
+
protected
|
870
978
|
def actual_source_reflection # FIXME: this is a horrible name
|
871
|
-
source_reflection.
|
979
|
+
source_reflection.actual_source_reflection
|
872
980
|
end
|
873
981
|
|
874
|
-
|
875
|
-
|
982
|
+
private
|
983
|
+
attr_reader :delegate_reflection
|
984
|
+
|
985
|
+
def collect_join_reflections(seed)
|
986
|
+
a = source_reflection.add_as_source seed
|
987
|
+
if options[:source_type]
|
988
|
+
through_reflection.add_as_polymorphic_through self, a
|
989
|
+
else
|
990
|
+
through_reflection.add_as_through a
|
991
|
+
end
|
876
992
|
end
|
877
993
|
|
878
994
|
def inverse_name; delegate_reflection.send(:inverse_name); end
|
879
995
|
|
880
|
-
private
|
881
996
|
def derive_class_name
|
882
997
|
# get the class_name of the belongs_to association of the through reflection
|
883
998
|
options[:source_type] || source_reflection.class_name
|
@@ -887,7 +1002,55 @@ module ActiveRecord
|
|
887
1002
|
public_instance_methods
|
888
1003
|
|
889
1004
|
delegate(*delegate_methods, to: :delegate_reflection)
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
class PolymorphicReflection < AbstractReflection # :nodoc:
|
1008
|
+
delegate :klass, :scope, :plural_name, :type, :join_primary_key, :join_foreign_key,
|
1009
|
+
:name, :scope_for, to: :@reflection
|
1010
|
+
|
1011
|
+
def initialize(reflection, previous_reflection)
|
1012
|
+
@reflection = reflection
|
1013
|
+
@previous_reflection = previous_reflection
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
1017
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
|
1018
|
+
scopes << build_scope(table, predicate_builder, klass).instance_exec(nil, &source_type_scope)
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
def constraints
|
1022
|
+
@reflection.constraints + [source_type_scope]
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
private
|
1026
|
+
def source_type_scope
|
1027
|
+
type = @previous_reflection.foreign_type
|
1028
|
+
source_type = @previous_reflection.options[:source_type]
|
1029
|
+
lambda { |object| where(type => source_type) }
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
class RuntimeReflection < AbstractReflection # :nodoc:
|
1034
|
+
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1035
|
+
|
1036
|
+
def initialize(reflection, association)
|
1037
|
+
@reflection = reflection
|
1038
|
+
@association = association
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
def klass
|
1042
|
+
@association.klass
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
def aliased_table
|
1046
|
+
klass.arel_table
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def join_primary_key(klass = self.klass)
|
1050
|
+
@reflection.join_primary_key(klass)
|
1051
|
+
end
|
890
1052
|
|
1053
|
+
def all_includes; yield; end
|
891
1054
|
end
|
892
1055
|
end
|
893
1056
|
end
|