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