activerecord 3.2.6 → 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 +7 -0
- data/CHANGELOG.md +611 -6417
- data/MIT-LICENSE +4 -2
- data/README.rdoc +44 -47
- data/examples/performance.rb +79 -71
- data/examples/simple.rb +6 -5
- data/lib/active_record/aggregations.rb +268 -238
- data/lib/active_record/association_relation.rb +40 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -42
- data/lib/active_record/associations/association.rb +173 -81
- data/lib/active_record/associations/association_scope.rb +124 -92
- data/lib/active_record/associations/belongs_to_association.rb +83 -38
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +11 -9
- data/lib/active_record/associations/builder/association.rb +113 -32
- data/lib/active_record/associations/builder/belongs_to.rb +105 -60
- data/lib/active_record/associations/builder/collection_association.rb +53 -56
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +98 -41
- data/lib/active_record/associations/builder/has_many.rb +11 -63
- data/lib/active_record/associations/builder/has_one.rb +47 -45
- data/lib/active_record/associations/builder/singular_association.rb +30 -18
- data/lib/active_record/associations/collection_association.rb +217 -295
- data/lib/active_record/associations/collection_proxy.rb +1074 -77
- data/lib/active_record/associations/foreign_association.rb +20 -0
- data/lib/active_record/associations/has_many_association.rb +78 -50
- data/lib/active_record/associations/has_many_through_association.rb +99 -61
- data/lib/active_record/associations/has_one_association.rb +75 -30
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +45 -119
- data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
- data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
- data/lib/active_record/associations/join_dependency.rb +208 -164
- data/lib/active_record/associations/preloader/association.rb +93 -87
- data/lib/active_record/associations/preloader/through_association.rb +87 -38
- data/lib/active_record/associations/preloader.rb +134 -110
- data/lib/active_record/associations/singular_association.rb +19 -24
- data/lib/active_record/associations/through_association.rb +61 -27
- data/lib/active_record/associations.rb +1766 -1505
- data/lib/active_record/attribute_assignment.rb +57 -193
- data/lib/active_record/attribute_decorators.rb +90 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +58 -8
- data/lib/active_record/attribute_methods/dirty.rb +187 -67
- data/lib/active_record/attribute_methods/primary_key.rb +100 -78
- data/lib/active_record/attribute_methods/query.rb +10 -8
- data/lib/active_record/attribute_methods/read.rb +29 -118
- data/lib/active_record/attribute_methods/serialization.rb +60 -72
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -42
- data/lib/active_record/attribute_methods/write.rb +36 -44
- data/lib/active_record/attribute_methods.rb +306 -161
- data/lib/active_record/attributes.rb +279 -0
- data/lib/active_record/autosave_association.rb +324 -238
- data/lib/active_record/base.rb +114 -507
- data/lib/active_record/callbacks.rb +147 -83
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +32 -23
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +962 -279
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +32 -5
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +331 -209
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +201 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +510 -289
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +93 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1182 -313
- data/lib/active_record/connection_adapters/abstract/transaction.rb +323 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +585 -120
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +610 -463
- data/lib/active_record/connection_adapters/column.rb +58 -233
- data/lib/active_record/connection_adapters/connection_specification.rb +297 -0
- 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 +75 -207
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +182 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +113 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +205 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +222 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +776 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +695 -1052
- data/lib/active_record/connection_adapters/schema_cache.rb +115 -24
- 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 +528 -26
- data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
- data/lib/active_record/connection_handling.rb +267 -0
- data/lib/active_record/core.rb +599 -0
- data/lib/active_record/counter_cache.rb +177 -103
- 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/database_configurations.rb +233 -0
- data/lib/active_record/define_callbacks.rb +22 -0
- data/lib/active_record/dynamic_matchers.rb +107 -64
- data/lib/active_record/enum.rb +274 -0
- data/lib/active_record/errors.rb +254 -61
- data/lib/active_record/explain.rb +35 -70
- data/lib/active_record/explain_registry.rb +32 -0
- data/lib/active_record/explain_subscriber.rb +18 -8
- data/lib/active_record/fixture_set/file.rb +82 -0
- 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 +291 -475
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +219 -100
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +175 -17
- data/lib/active_record/internal_metadata.rb +53 -0
- data/lib/active_record/legacy_yaml_adapter.rb +48 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +106 -92
- data/lib/active_record/locking/pessimistic.rb +23 -11
- data/lib/active_record/log_subscriber.rb +80 -30
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +235 -56
- data/lib/active_record/migration/compatibility.rb +244 -0
- data/lib/active_record/migration/join_table.rb +17 -0
- data/lib/active_record/migration.rb +917 -301
- data/lib/active_record/model_schema.rb +351 -175
- data/lib/active_record/nested_attributes.rb +366 -235
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/null_relation.rb +68 -0
- data/lib/active_record/persistence.rb +761 -166
- data/lib/active_record/query_cache.rb +22 -44
- data/lib/active_record/querying.rb +55 -31
- data/lib/active_record/railtie.rb +185 -47
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -4
- data/lib/active_record/railties/controller_runtime.rb +35 -33
- data/lib/active_record/railties/databases.rake +366 -463
- data/lib/active_record/readonly_attributes.rb +4 -6
- data/lib/active_record/reflection.rb +736 -228
- data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
- data/lib/active_record/relation/batches.rb +252 -52
- data/lib/active_record/relation/calculations.rb +340 -270
- data/lib/active_record/relation/delegation.rb +117 -36
- data/lib/active_record/relation/finder_methods.rb +439 -286
- data/lib/active_record/relation/from_clause.rb +26 -0
- data/lib/active_record/relation/merger.rb +184 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +49 -0
- 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 +19 -0
- data/lib/active_record/relation/predicate_builder.rb +131 -39
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +1163 -221
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +49 -120
- 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/relation.rb +671 -349
- data/lib/active_record/result.rb +149 -15
- data/lib/active_record/runtime_registry.rb +24 -0
- data/lib/active_record/sanitization.rb +153 -133
- data/lib/active_record/schema.rb +22 -19
- data/lib/active_record/schema_dumper.rb +178 -112
- data/lib/active_record/schema_migration.rb +60 -0
- data/lib/active_record/scoping/default.rb +107 -98
- data/lib/active_record/scoping/named.rb +130 -115
- data/lib/active_record/scoping.rb +77 -123
- data/lib/active_record/secure_token.rb +40 -0
- data/lib/active_record/serialization.rb +10 -6
- data/lib/active_record/statement_cache.rb +148 -0
- data/lib/active_record/store.rb +256 -16
- 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 +506 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +141 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +77 -0
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +93 -39
- data/lib/active_record/touch_later.rb +66 -0
- data/lib/active_record/transactions.rb +260 -129
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +129 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +71 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +21 -0
- data/lib/active_record/type/type_map.rb +62 -0
- data/lib/active_record/type/unsigned_integer.rb +17 -0
- data/lib/active_record/type.rb +78 -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/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +35 -18
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/presence.rb +68 -0
- data/lib/active_record/validations/uniqueness.rb +123 -77
- data/lib/active_record/validations.rb +54 -43
- data/lib/active_record/version.rb +7 -7
- data/lib/active_record.rb +97 -49
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -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/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/nodes.rb +68 -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/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/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +59 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +41 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +24 -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} +1 -1
- data/lib/rails/generators/active_record.rb +10 -16
- metadata +285 -149
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -188
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -426
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -579
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,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,35 +9,55 @@ module ActiveRecord
|
|
7
9
|
extend ActiveSupport::Concern
|
8
10
|
|
9
11
|
included do
|
10
|
-
class_attribute :
|
11
|
-
|
12
|
+
class_attribute :_reflections, instance_writer: false, default: {}
|
13
|
+
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
14
|
+
end
|
15
|
+
|
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
|
21
|
+
|
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
|
31
|
+
|
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
|
12
47
|
end
|
13
48
|
|
14
|
-
# Reflection enables to
|
15
|
-
#
|
16
|
-
#
|
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
|
17
52
|
# and creates input fields for all of the attributes depending on their type
|
18
53
|
# and displays the associations to other objects.
|
19
54
|
#
|
20
55
|
# MacroReflection class has info for AggregateReflection and AssociationReflection
|
21
56
|
# classes.
|
22
57
|
module ClassMethods
|
23
|
-
def create_reflection(macro, name, options, active_record)
|
24
|
-
case macro
|
25
|
-
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
|
26
|
-
klass = options[:through] ? ThroughReflection : AssociationReflection
|
27
|
-
reflection = klass.new(macro, name, options, active_record)
|
28
|
-
when :composed_of
|
29
|
-
reflection = AggregateReflection.new(macro, name, options, active_record)
|
30
|
-
end
|
31
|
-
|
32
|
-
self.reflections = self.reflections.merge(name => reflection)
|
33
|
-
reflection
|
34
|
-
end
|
35
|
-
|
36
58
|
# Returns an array of AggregateReflection objects for all the aggregations in the class.
|
37
59
|
def reflect_on_all_aggregations
|
38
|
-
|
60
|
+
aggregate_reflections.values
|
39
61
|
end
|
40
62
|
|
41
63
|
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
|
@@ -43,7 +65,30 @@ module ActiveRecord
|
|
43
65
|
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
|
44
66
|
#
|
45
67
|
def reflect_on_aggregation(aggregation)
|
46
|
-
|
68
|
+
aggregate_reflections[aggregation.to_s]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
|
72
|
+
#
|
73
|
+
# Account.reflections # => {"balance" => AggregateReflection}
|
74
|
+
#
|
75
|
+
def reflections
|
76
|
+
@__reflections ||= begin
|
77
|
+
ref = {}
|
78
|
+
|
79
|
+
_reflections.each do |name, reflection|
|
80
|
+
parent_reflection = reflection.parent_reflection
|
81
|
+
|
82
|
+
if parent_reflection
|
83
|
+
parent_name = parent_reflection.name
|
84
|
+
ref[parent_name.to_s] = parent_reflection
|
85
|
+
else
|
86
|
+
ref[name] = reflection
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
ref
|
91
|
+
end
|
47
92
|
end
|
48
93
|
|
49
94
|
# Returns an array of AssociationReflection objects for all the
|
@@ -57,8 +102,9 @@ module ActiveRecord
|
|
57
102
|
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
|
58
103
|
#
|
59
104
|
def reflect_on_all_associations(macro = nil)
|
60
|
-
association_reflections = reflections.values
|
61
|
-
|
105
|
+
association_reflections = reflections.values
|
106
|
+
association_reflections.select! { |reflection| reflection.macro == macro } if macro
|
107
|
+
association_reflections
|
62
108
|
end
|
63
109
|
|
64
110
|
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
@@ -67,64 +113,274 @@ module ActiveRecord
|
|
67
113
|
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
68
114
|
#
|
69
115
|
def reflect_on_association(association)
|
70
|
-
reflections[association
|
116
|
+
reflections[association.to_s]
|
117
|
+
end
|
118
|
+
|
119
|
+
def _reflect_on_association(association) #:nodoc:
|
120
|
+
_reflections[association.to_s]
|
71
121
|
end
|
72
122
|
|
73
123
|
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
|
74
124
|
def reflect_on_all_autosave_associations
|
75
125
|
reflections.values.select { |reflection| reflection.options[:autosave] }
|
76
126
|
end
|
127
|
+
|
128
|
+
def clear_reflections_cache # :nodoc:
|
129
|
+
@__reflections = nil
|
130
|
+
end
|
77
131
|
end
|
78
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
|
146
|
+
class AbstractReflection # :nodoc:
|
147
|
+
def through_reflection?
|
148
|
+
false
|
149
|
+
end
|
150
|
+
|
151
|
+
def table_name
|
152
|
+
klass.table_name
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns a new, unsaved instance of the associated class. +attributes+ will
|
156
|
+
# be passed to the class's constructor.
|
157
|
+
def build_association(attributes, &block)
|
158
|
+
klass.new(attributes, &block)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns the class name for the macro.
|
162
|
+
#
|
163
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
|
164
|
+
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
165
|
+
def class_name
|
166
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
167
|
+
end
|
168
|
+
|
169
|
+
JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
|
170
|
+
|
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)
|
213
|
+
end
|
214
|
+
|
215
|
+
def constraints
|
216
|
+
chain.flat_map(&:scopes)
|
217
|
+
end
|
218
|
+
|
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
|
229
|
+
end
|
230
|
+
|
231
|
+
def inverse_of
|
232
|
+
return unless inverse_name
|
233
|
+
|
234
|
+
@inverse_of ||= klass._reflect_on_association inverse_name
|
235
|
+
end
|
236
|
+
|
237
|
+
def check_validity_of_inverse!
|
238
|
+
unless polymorphic?
|
239
|
+
if has_inverse? && inverse_of.nil?
|
240
|
+
raise InverseOfAssociationNotFoundError.new(self)
|
241
|
+
end
|
242
|
+
end
|
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
|
79
318
|
|
80
|
-
|
319
|
+
def primary_key(klass)
|
320
|
+
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# Base class for AggregateReflection and AssociationReflection. Objects of
|
81
325
|
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
|
82
|
-
class MacroReflection
|
326
|
+
class MacroReflection < AbstractReflection
|
83
327
|
# Returns the name of the macro.
|
84
328
|
#
|
85
|
-
# <tt>composed_of :balance, :
|
329
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
|
86
330
|
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
|
87
331
|
attr_reader :name
|
88
332
|
|
89
|
-
|
90
|
-
#
|
91
|
-
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:composed_of</tt>
|
92
|
-
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
93
|
-
attr_reader :macro
|
333
|
+
attr_reader :scope
|
94
334
|
|
95
335
|
# Returns the hash of options used for the macro.
|
96
336
|
#
|
97
|
-
# <tt>composed_of :balance, :
|
98
|
-
# <tt>has_many :clients</tt> returns
|
337
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
|
338
|
+
# <tt>has_many :clients</tt> returns <tt>{}</tt>
|
99
339
|
attr_reader :options
|
100
340
|
|
101
341
|
attr_reader :active_record
|
102
342
|
|
103
343
|
attr_reader :plural_name # :nodoc:
|
104
344
|
|
105
|
-
def initialize(
|
106
|
-
@macro = macro
|
345
|
+
def initialize(name, scope, options, active_record)
|
107
346
|
@name = name
|
347
|
+
@scope = scope
|
108
348
|
@options = options
|
109
349
|
@active_record = active_record
|
350
|
+
@klass = options[:anonymous_class]
|
110
351
|
@plural_name = active_record.pluralize_table_names ?
|
111
352
|
name.to_s.pluralize : name.to_s
|
112
353
|
end
|
113
354
|
|
355
|
+
def autosave=(autosave)
|
356
|
+
@options[:autosave] = autosave
|
357
|
+
parent_reflection = self.parent_reflection
|
358
|
+
if parent_reflection
|
359
|
+
parent_reflection.autosave = autosave
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
114
363
|
# Returns the class for the macro.
|
115
364
|
#
|
116
|
-
# <tt>composed_of :balance, :
|
365
|
+
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
|
117
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.
|
118
378
|
def klass
|
119
|
-
@klass ||= class_name
|
379
|
+
@klass ||= compute_class(class_name)
|
120
380
|
end
|
121
381
|
|
122
|
-
|
123
|
-
|
124
|
-
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
|
125
|
-
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
126
|
-
def class_name
|
127
|
-
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
382
|
+
def compute_class(name)
|
383
|
+
name.constantize
|
128
384
|
end
|
129
385
|
|
130
386
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
@@ -133,12 +389,12 @@ module ActiveRecord
|
|
133
389
|
super ||
|
134
390
|
other_aggregation.kind_of?(self.class) &&
|
135
391
|
name == other_aggregation.name &&
|
136
|
-
other_aggregation.options &&
|
392
|
+
!other_aggregation.options.nil? &&
|
137
393
|
active_record == other_aggregation.active_record
|
138
394
|
end
|
139
395
|
|
140
|
-
def
|
141
|
-
|
396
|
+
def scope_for(relation, owner = nil)
|
397
|
+
relation.instance_exec(owner, &scope) || relation
|
142
398
|
end
|
143
399
|
|
144
400
|
private
|
@@ -147,64 +403,58 @@ module ActiveRecord
|
|
147
403
|
end
|
148
404
|
end
|
149
405
|
|
150
|
-
|
151
|
-
# 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
|
152
407
|
# Active Record class.
|
153
408
|
class AggregateReflection < MacroReflection #:nodoc:
|
409
|
+
def mapping
|
410
|
+
mapping = options[:mapping] || [name, name]
|
411
|
+
mapping.first.is_a?(Array) ? mapping : [mapping]
|
412
|
+
end
|
154
413
|
end
|
155
414
|
|
156
|
-
# Holds all the
|
415
|
+
# Holds all the metadata about an association as it was specified in the
|
157
416
|
# Active Record class.
|
158
417
|
class AssociationReflection < MacroReflection #:nodoc:
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
#
|
165
|
-
# Author.reflect_on_association(:books).klass
|
166
|
-
# # => Book
|
167
|
-
#
|
168
|
-
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
|
169
|
-
# a new association object. Use +build_association+ or +create_association+
|
170
|
-
# instead. This allows plugins to hook into association object creation.
|
171
|
-
def klass
|
172
|
-
@klass ||= active_record.send(:compute_type, class_name)
|
173
|
-
end
|
174
|
-
|
175
|
-
def initialize(macro, name, options, active_record)
|
176
|
-
super
|
177
|
-
@collection = macro.in?([:has_many, :has_and_belongs_to_many])
|
418
|
+
def compute_class(name)
|
419
|
+
if polymorphic?
|
420
|
+
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
421
|
+
end
|
422
|
+
active_record.send(:compute_type, name)
|
178
423
|
end
|
179
424
|
|
180
|
-
|
181
|
-
|
182
|
-
def build_association(*options, &block)
|
183
|
-
klass.new(*options, &block)
|
184
|
-
end
|
425
|
+
attr_reader :type, :foreign_type
|
426
|
+
attr_accessor :parent_reflection # Reflection
|
185
427
|
|
186
|
-
def
|
187
|
-
|
188
|
-
|
428
|
+
def initialize(name, scope, options, active_record)
|
429
|
+
super
|
430
|
+
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
|
431
|
+
@foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
|
432
|
+
@constructable = calculate_constructable(macro, options)
|
433
|
+
@association_scope_cache = Concurrent::Map.new
|
189
434
|
|
190
|
-
|
191
|
-
|
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
|
192
438
|
end
|
193
439
|
|
194
|
-
def
|
195
|
-
|
440
|
+
def association_scope_cache(conn, owner, &block)
|
441
|
+
key = conn.prepared_statements
|
442
|
+
if polymorphic?
|
443
|
+
key = [key, owner._read_attribute(@foreign_type)]
|
444
|
+
end
|
445
|
+
@association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
|
196
446
|
end
|
197
447
|
|
198
|
-
def
|
199
|
-
@
|
448
|
+
def constructable? # :nodoc:
|
449
|
+
@constructable
|
200
450
|
end
|
201
451
|
|
202
|
-
def
|
203
|
-
@
|
452
|
+
def join_table
|
453
|
+
@join_table ||= options[:join_table] || derive_join_table
|
204
454
|
end
|
205
455
|
|
206
|
-
def
|
207
|
-
@
|
456
|
+
def foreign_key
|
457
|
+
@foreign_key ||= options[:foreign_key] || derive_foreign_key.freeze
|
208
458
|
end
|
209
459
|
|
210
460
|
def association_foreign_key
|
@@ -220,74 +470,62 @@ module ActiveRecord
|
|
220
470
|
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
|
221
471
|
end
|
222
472
|
|
223
|
-
def counter_cache_column
|
224
|
-
if options[:counter_cache] == true
|
225
|
-
"#{active_record.name.demodulize.underscore.pluralize}_count"
|
226
|
-
elsif options[:counter_cache]
|
227
|
-
options[:counter_cache].to_s
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
def columns(tbl_name, log_msg)
|
232
|
-
@columns ||= klass.connection.columns(tbl_name, log_msg)
|
233
|
-
end
|
234
|
-
|
235
|
-
def reset_column_information
|
236
|
-
@columns = nil
|
237
|
-
end
|
238
|
-
|
239
473
|
def check_validity!
|
240
474
|
check_validity_of_inverse!
|
241
475
|
end
|
242
476
|
|
243
|
-
def
|
244
|
-
unless
|
245
|
-
|
246
|
-
|
247
|
-
|
477
|
+
def check_preloadable!
|
478
|
+
return unless scope
|
479
|
+
|
480
|
+
unless scope.arity == 0
|
481
|
+
raise ArgumentError, <<-MSG.squish
|
482
|
+
The association scope '#{name}' is instance dependent (the scope
|
483
|
+
block takes an argument). Preloading instance dependent scopes is
|
484
|
+
not supported.
|
485
|
+
MSG
|
248
486
|
end
|
249
487
|
end
|
488
|
+
alias :check_eager_loadable! :check_preloadable!
|
489
|
+
|
490
|
+
def join_id_for(owner) # :nodoc:
|
491
|
+
owner[join_foreign_key]
|
492
|
+
end
|
250
493
|
|
251
494
|
def through_reflection
|
252
495
|
nil
|
253
496
|
end
|
254
497
|
|
255
498
|
def source_reflection
|
256
|
-
|
499
|
+
self
|
257
500
|
end
|
258
501
|
|
259
502
|
# A chain of reflections from this one back to the owner. For more see the explanation in
|
260
503
|
# ThroughReflection.
|
261
|
-
def
|
504
|
+
def collect_join_chain
|
262
505
|
[self]
|
263
506
|
end
|
264
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
|
+
|
265
514
|
def nested?
|
266
515
|
false
|
267
516
|
end
|
268
517
|
|
269
|
-
|
270
|
-
|
271
|
-
# a hash, array, arel predicate, etc...)
|
272
|
-
def conditions
|
273
|
-
[[options[:conditions]].compact]
|
518
|
+
def has_scope?
|
519
|
+
scope
|
274
520
|
end
|
275
521
|
|
276
|
-
alias :source_macro :macro
|
277
|
-
|
278
522
|
def has_inverse?
|
279
|
-
|
280
|
-
end
|
281
|
-
|
282
|
-
def inverse_of
|
283
|
-
if has_inverse?
|
284
|
-
@inverse_of ||= klass.reflect_on_association(options[:inverse_of])
|
285
|
-
end
|
523
|
+
inverse_name
|
286
524
|
end
|
287
525
|
|
288
526
|
def polymorphic_inverse_of(associated_class)
|
289
527
|
if has_inverse?
|
290
|
-
if inverse_relationship = associated_class.
|
528
|
+
if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
|
291
529
|
inverse_relationship
|
292
530
|
else
|
293
531
|
raise InverseOfAssociationNotFoundError.new(self, associated_class)
|
@@ -295,61 +533,128 @@ module ActiveRecord
|
|
295
533
|
end
|
296
534
|
end
|
297
535
|
|
536
|
+
# Returns the macro type.
|
537
|
+
#
|
538
|
+
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
|
539
|
+
def macro; raise NotImplementedError; end
|
540
|
+
|
298
541
|
# Returns whether or not this association reflection is for a collection
|
299
542
|
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
300
543
|
# +has_and_belongs_to_many+, +false+ otherwise.
|
301
544
|
def collection?
|
302
|
-
|
545
|
+
false
|
303
546
|
end
|
304
547
|
|
305
548
|
# Returns whether or not the association should be validated as part of
|
306
549
|
# the parent's validation.
|
307
550
|
#
|
308
551
|
# Unless you explicitly disable validation with
|
309
|
-
# <tt
|
552
|
+
# <tt>validate: false</tt>, validation will take place when:
|
310
553
|
#
|
311
|
-
# * you explicitly enable validation; <tt
|
312
|
-
# * you use autosave; <tt
|
554
|
+
# * you explicitly enable validation; <tt>validate: true</tt>
|
555
|
+
# * you use autosave; <tt>autosave: true</tt>
|
313
556
|
# * the association is a +has_many+ association
|
314
557
|
def validate?
|
315
|
-
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true ||
|
558
|
+
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
|
316
559
|
end
|
317
560
|
|
318
561
|
# Returns +true+ if +self+ is a +belongs_to+ reflection.
|
319
|
-
def belongs_to
|
320
|
-
|
562
|
+
def belongs_to?; false; end
|
563
|
+
|
564
|
+
# Returns +true+ if +self+ is a +has_one+ reflection.
|
565
|
+
def has_one?; false; end
|
566
|
+
|
567
|
+
def association_class; raise NotImplementedError; end
|
568
|
+
|
569
|
+
def polymorphic?
|
570
|
+
options[:polymorphic]
|
321
571
|
end
|
322
572
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
573
|
+
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
574
|
+
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
|
575
|
+
|
576
|
+
def add_as_source(seed)
|
577
|
+
seed
|
578
|
+
end
|
579
|
+
|
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
|
591
|
+
|
592
|
+
private
|
593
|
+
|
594
|
+
def calculate_constructable(macro, options)
|
595
|
+
true
|
596
|
+
end
|
597
|
+
|
598
|
+
# Attempts to find the inverse association name automatically.
|
599
|
+
# If it cannot find a suitable inverse association name, it returns
|
600
|
+
# +nil+.
|
601
|
+
def inverse_name
|
602
|
+
unless defined?(@inverse_name)
|
603
|
+
@inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
|
338
604
|
end
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
605
|
+
|
606
|
+
@inverse_name
|
607
|
+
end
|
608
|
+
|
609
|
+
# returns either +nil+ or the inverse association name that it finds.
|
610
|
+
def automatic_inverse_of
|
611
|
+
if can_find_inverse_of_automatically?(self)
|
612
|
+
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
|
613
|
+
|
614
|
+
begin
|
615
|
+
reflection = klass._reflect_on_association(inverse_name)
|
616
|
+
rescue NameError
|
617
|
+
# Give up: we couldn't compute the klass type so we won't be able
|
618
|
+
# to find any associations either.
|
619
|
+
reflection = false
|
620
|
+
end
|
621
|
+
|
622
|
+
if valid_inverse_reflection?(reflection)
|
623
|
+
return inverse_name
|
624
|
+
end
|
344
625
|
end
|
345
626
|
end
|
346
|
-
end
|
347
627
|
|
348
|
-
|
628
|
+
# Checks if the inverse reflection that is returned from the
|
629
|
+
# +automatic_inverse_of+ method is a valid reflection. We must
|
630
|
+
# make sure that the reflection's active_record name matches up
|
631
|
+
# with the current reflection's klass name.
|
632
|
+
def valid_inverse_reflection?(reflection)
|
633
|
+
reflection &&
|
634
|
+
klass <= reflection.active_record &&
|
635
|
+
can_find_inverse_of_automatically?(reflection)
|
636
|
+
end
|
637
|
+
|
638
|
+
# Checks to see if the reflection doesn't have any options that prevent
|
639
|
+
# us from being able to guess the inverse automatically. First, the
|
640
|
+
# <tt>inverse_of</tt> option cannot be set to false. Second, we must
|
641
|
+
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
|
642
|
+
# Third, we must not have options such as <tt>:foreign_key</tt>
|
643
|
+
# which prevent us from correctly guessing the inverse association.
|
644
|
+
#
|
645
|
+
# Anything with a scope can additionally ruin our attempt at finding an
|
646
|
+
# inverse, so we exclude reflections with scopes.
|
647
|
+
def can_find_inverse_of_automatically?(reflection)
|
648
|
+
reflection.options[:inverse_of] != false &&
|
649
|
+
VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
|
650
|
+
!INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] } &&
|
651
|
+
!reflection.scope
|
652
|
+
end
|
653
|
+
|
349
654
|
def derive_class_name
|
350
|
-
class_name = name.to_s
|
655
|
+
class_name = name.to_s
|
351
656
|
class_name = class_name.singularize if collection?
|
352
|
-
class_name
|
657
|
+
class_name.camelize
|
353
658
|
end
|
354
659
|
|
355
660
|
def derive_foreign_key
|
@@ -362,27 +667,127 @@ module ActiveRecord
|
|
362
667
|
end
|
363
668
|
end
|
364
669
|
|
365
|
-
def
|
366
|
-
|
670
|
+
def derive_join_table
|
671
|
+
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
class HasManyReflection < AssociationReflection # :nodoc:
|
676
|
+
def macro; :has_many; end
|
677
|
+
|
678
|
+
def collection?; true; end
|
679
|
+
|
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)
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
class HasOneReflection < AssociationReflection # :nodoc:
|
694
|
+
def macro; :has_one; end
|
695
|
+
|
696
|
+
def has_one?; true; end
|
697
|
+
|
698
|
+
def association_class
|
699
|
+
if options[:through]
|
700
|
+
Associations::HasOneThroughAssociation
|
701
|
+
else
|
702
|
+
Associations::HasOneAssociation
|
367
703
|
end
|
704
|
+
end
|
705
|
+
|
706
|
+
private
|
707
|
+
|
708
|
+
def calculate_constructable(macro, options)
|
709
|
+
!options[:through]
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
class BelongsToReflection < AssociationReflection # :nodoc:
|
714
|
+
def macro; :belongs_to; end
|
715
|
+
|
716
|
+
def belongs_to?; true; end
|
717
|
+
|
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
|
728
|
+
end
|
729
|
+
|
730
|
+
def join_foreign_key
|
731
|
+
foreign_key
|
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
|
742
|
+
end
|
743
|
+
|
744
|
+
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
745
|
+
def macro; :has_and_belongs_to_many; end
|
746
|
+
|
747
|
+
def collection?
|
748
|
+
true
|
749
|
+
end
|
368
750
|
end
|
369
751
|
|
370
|
-
# Holds all the
|
752
|
+
# Holds all the metadata about a :through association as it was specified
|
371
753
|
# in the Active Record class.
|
372
|
-
class ThroughReflection <
|
373
|
-
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
374
|
-
:active_record_primary_key, :type, :to
|
754
|
+
class ThroughReflection < AbstractReflection #:nodoc:
|
755
|
+
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for,
|
756
|
+
:active_record_primary_key, :type, :get_join_keys, to: :source_reflection
|
757
|
+
|
758
|
+
def initialize(delegate_reflection)
|
759
|
+
@delegate_reflection = delegate_reflection
|
760
|
+
@klass = delegate_reflection.options[:anonymous_class]
|
761
|
+
@source_reflection_name = delegate_reflection.options[:source]
|
762
|
+
end
|
375
763
|
|
376
|
-
|
764
|
+
def through_reflection?
|
765
|
+
true
|
766
|
+
end
|
767
|
+
|
768
|
+
def klass
|
769
|
+
@klass ||= delegate_reflection.compute_class(class_name)
|
770
|
+
end
|
771
|
+
|
772
|
+
# Returns the source of the through reflection. It checks both a singularized
|
377
773
|
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
|
378
774
|
#
|
379
775
|
# class Post < ActiveRecord::Base
|
380
776
|
# has_many :taggings
|
381
|
-
# has_many :tags, :
|
777
|
+
# has_many :tags, through: :taggings
|
382
778
|
# end
|
383
779
|
#
|
780
|
+
# class Tagging < ActiveRecord::Base
|
781
|
+
# belongs_to :post
|
782
|
+
# belongs_to :tag
|
783
|
+
# end
|
784
|
+
#
|
785
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
786
|
+
# tags_reflection.source_reflection
|
787
|
+
# # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
|
788
|
+
#
|
384
789
|
def source_reflection
|
385
|
-
|
790
|
+
through_reflection.klass._reflect_on_association(source_reflection_name)
|
386
791
|
end
|
387
792
|
|
388
793
|
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
|
@@ -390,14 +795,15 @@ module ActiveRecord
|
|
390
795
|
#
|
391
796
|
# class Post < ActiveRecord::Base
|
392
797
|
# has_many :taggings
|
393
|
-
# has_many :tags, :
|
798
|
+
# has_many :tags, through: :taggings
|
394
799
|
# end
|
395
800
|
#
|
396
801
|
# tags_reflection = Post.reflect_on_association(:tags)
|
397
|
-
#
|
802
|
+
# tags_reflection.through_reflection
|
803
|
+
# # => <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @active_record=Post, @plural_name="taggings">
|
398
804
|
#
|
399
805
|
def through_reflection
|
400
|
-
|
806
|
+
active_record._reflect_on_association(options[:through])
|
401
807
|
end
|
402
808
|
|
403
809
|
# Returns an array of reflections which are involved in this association. Each item in the
|
@@ -406,64 +812,46 @@ module ActiveRecord
|
|
406
812
|
# The chain is built by recursively calling #chain on the source reflection and the through
|
407
813
|
# reflection. The base case for the recursion is a normal association, which just returns
|
408
814
|
# [self] as its #chain.
|
409
|
-
def chain
|
410
|
-
@chain ||= begin
|
411
|
-
chain = source_reflection.chain + through_reflection.chain
|
412
|
-
chain[0] = self # Use self so we don't lose the information from :source_type
|
413
|
-
chain
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
|
-
# Consider the following example:
|
418
815
|
#
|
419
|
-
# class
|
420
|
-
# has_many :
|
421
|
-
# has_many :
|
422
|
-
# end
|
423
|
-
#
|
424
|
-
# class Article
|
425
|
-
# has_many :comments
|
426
|
-
# has_many :comment_tags, :through => :comments, :source => :tags
|
816
|
+
# class Post < ActiveRecord::Base
|
817
|
+
# has_many :taggings
|
818
|
+
# has_many :tags, through: :taggings
|
427
819
|
# end
|
428
820
|
#
|
429
|
-
#
|
430
|
-
#
|
431
|
-
#
|
821
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
822
|
+
# tags_reflection.chain
|
823
|
+
# # => [<ActiveRecord::Reflection::ThroughReflection: @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection: @name=:tags...>,
|
824
|
+
# <ActiveRecord::Reflection::HasManyReflection: @name=:taggings, @options={}, @active_record=Post>]
|
432
825
|
#
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
# to an item in the #chain, and is itself an array of conditions from an arbitrary number
|
437
|
-
# of relevant reflections, plus any :source_type or polymorphic :as constraints.
|
438
|
-
def conditions
|
439
|
-
@conditions ||= begin
|
440
|
-
conditions = source_reflection.conditions.map { |c| c.dup }
|
441
|
-
|
442
|
-
# Add to it the conditions from this reflection if necessary.
|
443
|
-
conditions.first << options[:conditions] if options[:conditions]
|
444
|
-
|
445
|
-
through_conditions = through_reflection.conditions
|
826
|
+
def collect_join_chain
|
827
|
+
collect_join_reflections [self]
|
828
|
+
end
|
446
829
|
|
447
|
-
|
448
|
-
|
449
|
-
|
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
|
836
|
+
end
|
450
837
|
|
451
|
-
|
452
|
-
|
838
|
+
def scopes
|
839
|
+
source_reflection.scopes + super
|
840
|
+
end
|
453
841
|
|
454
|
-
|
455
|
-
|
456
|
-
end
|
842
|
+
def join_scopes(table, predicate_builder) # :nodoc:
|
843
|
+
source_reflection.join_scopes(table, predicate_builder) + super
|
457
844
|
end
|
458
845
|
|
459
|
-
|
460
|
-
|
461
|
-
|
846
|
+
def has_scope?
|
847
|
+
scope || options[:source_type] ||
|
848
|
+
source_reflection.has_scope? ||
|
849
|
+
through_reflection.has_scope?
|
462
850
|
end
|
463
851
|
|
464
852
|
# A through association is nested if there would be more than one join table
|
465
853
|
def nested?
|
466
|
-
|
854
|
+
source_reflection.through_reflection? || through_reflection.through_reflection?
|
467
855
|
end
|
468
856
|
|
469
857
|
# We want to use the klass from this reflection, rather than just delegate straight to
|
@@ -472,20 +860,43 @@ module ActiveRecord
|
|
472
860
|
def association_primary_key(klass = nil)
|
473
861
|
# Get the "actual" source reflection if the immediate source reflection has a
|
474
862
|
# source reflection itself
|
475
|
-
|
476
|
-
while source_reflection.source_reflection
|
477
|
-
source_reflection = source_reflection.source_reflection
|
478
|
-
end
|
479
|
-
|
480
|
-
source_reflection.options[:primary_key] || primary_key(klass || self.klass)
|
863
|
+
actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
|
481
864
|
end
|
482
865
|
|
483
|
-
# Gets an array of possible <tt>:through</tt> source reflection names
|
866
|
+
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
|
867
|
+
#
|
868
|
+
# class Post < ActiveRecord::Base
|
869
|
+
# has_many :taggings
|
870
|
+
# has_many :tags, through: :taggings
|
871
|
+
# end
|
484
872
|
#
|
485
|
-
#
|
873
|
+
# tags_reflection = Post.reflect_on_association(:tags)
|
874
|
+
# tags_reflection.source_reflection_names
|
875
|
+
# # => [:tag, :tags]
|
486
876
|
#
|
487
877
|
def source_reflection_names
|
488
|
-
|
878
|
+
options[:source] ? [options[:source]] : [name.to_s.singularize, name].uniq
|
879
|
+
end
|
880
|
+
|
881
|
+
def source_reflection_name # :nodoc:
|
882
|
+
return @source_reflection_name if @source_reflection_name
|
883
|
+
|
884
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
885
|
+
names = names.find_all { |n|
|
886
|
+
through_reflection.klass._reflect_on_association(n)
|
887
|
+
}
|
888
|
+
|
889
|
+
if names.length > 1
|
890
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
891
|
+
active_record.name,
|
892
|
+
macro,
|
893
|
+
name,
|
894
|
+
options,
|
895
|
+
source_reflection_names
|
896
|
+
)
|
897
|
+
end
|
898
|
+
|
899
|
+
@source_reflection_name = names.first
|
489
900
|
end
|
490
901
|
|
491
902
|
def source_options
|
@@ -501,34 +912,131 @@ module ActiveRecord
|
|
501
912
|
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
|
502
913
|
end
|
503
914
|
|
504
|
-
if through_reflection.
|
505
|
-
|
915
|
+
if through_reflection.polymorphic?
|
916
|
+
if has_one?
|
917
|
+
raise HasOneAssociationPolymorphicThroughError.new(active_record.name, self)
|
918
|
+
else
|
919
|
+
raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
|
920
|
+
end
|
506
921
|
end
|
507
922
|
|
508
923
|
if source_reflection.nil?
|
509
924
|
raise HasManyThroughSourceAssociationNotFoundError.new(self)
|
510
925
|
end
|
511
926
|
|
512
|
-
if options[:source_type] && source_reflection.
|
927
|
+
if options[:source_type] && !source_reflection.polymorphic?
|
513
928
|
raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
|
514
929
|
end
|
515
930
|
|
516
|
-
if source_reflection.
|
931
|
+
if source_reflection.polymorphic? && options[:source_type].nil?
|
517
932
|
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
|
518
933
|
end
|
519
934
|
|
520
|
-
if
|
935
|
+
if has_one? && through_reflection.collection?
|
521
936
|
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
|
522
937
|
end
|
523
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
|
+
|
524
947
|
check_validity_of_inverse!
|
525
948
|
end
|
526
949
|
|
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
|
963
|
+
|
964
|
+
def add_as_through(seed)
|
965
|
+
collect_join_reflections(seed + [self])
|
966
|
+
end
|
967
|
+
|
968
|
+
protected
|
969
|
+
def actual_source_reflection # FIXME: this is a horrible name
|
970
|
+
source_reflection.actual_source_reflection
|
971
|
+
end
|
972
|
+
|
527
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
|
983
|
+
end
|
984
|
+
|
985
|
+
def inverse_name; delegate_reflection.send(:inverse_name); end
|
986
|
+
|
528
987
|
def derive_class_name
|
529
988
|
# get the class_name of the belongs_to association of the through reflection
|
530
989
|
options[:source_type] || source_reflection.class_name
|
531
990
|
end
|
991
|
+
|
992
|
+
delegate_methods = AssociationReflection.public_instance_methods -
|
993
|
+
public_instance_methods
|
994
|
+
|
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
|
1038
|
+
|
1039
|
+
def all_includes; yield; end
|
532
1040
|
end
|
533
1041
|
end
|
534
1042
|
end
|