activerecord 6.0.0 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -25
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +171 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/binary.rb +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
data/lib/active_record/enum.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/core_ext/hash/slice"
|
|
3
4
|
require "active_support/core_ext/object/deep_dup"
|
|
4
5
|
|
|
5
6
|
module ActiveRecord
|
|
@@ -7,7 +8,7 @@ module ActiveRecord
|
|
|
7
8
|
# but can be queried by name. Example:
|
|
8
9
|
#
|
|
9
10
|
# class Conversation < ActiveRecord::Base
|
|
10
|
-
# enum status
|
|
11
|
+
# enum :status, [ :active, :archived ]
|
|
11
12
|
# end
|
|
12
13
|
#
|
|
13
14
|
# # conversation.update! status: 0
|
|
@@ -41,19 +42,33 @@ module ActiveRecord
|
|
|
41
42
|
# Conversation.where(status: [:active, :archived])
|
|
42
43
|
# Conversation.where.not(status: :active)
|
|
43
44
|
#
|
|
44
|
-
#
|
|
45
|
+
# Defining scopes can be disabled by setting +:scopes+ to +false+.
|
|
45
46
|
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
47
|
+
# class Conversation < ActiveRecord::Base
|
|
48
|
+
# enum :status, [ :active, :archived ], scopes: false
|
|
48
49
|
# end
|
|
49
50
|
#
|
|
50
|
-
#
|
|
51
|
+
# You can set the default enum value by setting +:default+, like:
|
|
52
|
+
#
|
|
53
|
+
# class Conversation < ActiveRecord::Base
|
|
54
|
+
# enum :status, [ :active, :archived ], default: :active
|
|
55
|
+
# end
|
|
51
56
|
#
|
|
52
|
-
#
|
|
57
|
+
# conversation = Conversation.new
|
|
58
|
+
# conversation.status # => "active"
|
|
59
|
+
#
|
|
60
|
+
# It's possible to explicitly map the relation between attribute and
|
|
53
61
|
# database integer with a hash:
|
|
54
62
|
#
|
|
55
63
|
# class Conversation < ActiveRecord::Base
|
|
56
|
-
# enum status
|
|
64
|
+
# enum :status, active: 0, archived: 1
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# Finally it's also possible to use a string column to persist the enumerated value.
|
|
68
|
+
# Note that this will likely lead to slower database queries:
|
|
69
|
+
#
|
|
70
|
+
# class Conversation < ActiveRecord::Base
|
|
71
|
+
# enum :status, active: "active", archived: "archived"
|
|
57
72
|
# end
|
|
58
73
|
#
|
|
59
74
|
# Note that when an array is used, the implicit mapping from the values to database
|
|
@@ -68,7 +83,7 @@ module ActiveRecord
|
|
|
68
83
|
#
|
|
69
84
|
# In rare circumstances you might need to access the mapping directly.
|
|
70
85
|
# The mappings are exposed through a class method with the pluralized attribute
|
|
71
|
-
# name, which return the mapping in a
|
|
86
|
+
# name, which return the mapping in a ActiveSupport::HashWithIndifferentAccess :
|
|
72
87
|
#
|
|
73
88
|
# Conversation.statuses[:active] # => 0
|
|
74
89
|
# Conversation.statuses["archived"] # => 1
|
|
@@ -78,14 +93,14 @@ module ActiveRecord
|
|
|
78
93
|
#
|
|
79
94
|
# Conversation.where("status <> ?", Conversation.statuses[:archived])
|
|
80
95
|
#
|
|
81
|
-
# You can use the +:
|
|
96
|
+
# You can use the +:prefix+ or +:suffix+ options when you need to define
|
|
82
97
|
# multiple enums with same values. If the passed value is +true+, the methods
|
|
83
98
|
# are prefixed/suffixed with the name of the enum. It is also possible to
|
|
84
99
|
# supply a custom value:
|
|
85
100
|
#
|
|
86
101
|
# class Conversation < ActiveRecord::Base
|
|
87
|
-
# enum status
|
|
88
|
-
# enum comments_status
|
|
102
|
+
# enum :status, [ :active, :archived ], suffix: true
|
|
103
|
+
# enum :comments_status, [ :active, :inactive ], prefix: :comments
|
|
89
104
|
# end
|
|
90
105
|
#
|
|
91
106
|
# With the above example, the bang and predicate methods along with the
|
|
@@ -96,64 +111,137 @@ module ActiveRecord
|
|
|
96
111
|
#
|
|
97
112
|
# conversation.comments_inactive!
|
|
98
113
|
# conversation.comments_active? # => false
|
|
99
|
-
|
|
114
|
+
#
|
|
115
|
+
# If you want to disable the auto-generated methods on the model, you can do
|
|
116
|
+
# so by setting the +:instance_methods+ option to false:
|
|
117
|
+
#
|
|
118
|
+
# class Conversation < ActiveRecord::Base
|
|
119
|
+
# enum :status, [ :active, :archived ], instance_methods: false
|
|
120
|
+
# end
|
|
121
|
+
#
|
|
122
|
+
# By default, an +ArgumentError+ will be raised when assigning an invalid value:
|
|
123
|
+
#
|
|
124
|
+
# class Conversation < ActiveRecord::Base
|
|
125
|
+
# enum :status, [ :active, :archived ]
|
|
126
|
+
# end
|
|
127
|
+
#
|
|
128
|
+
# conversation = Conversation.new
|
|
129
|
+
#
|
|
130
|
+
# conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
|
|
131
|
+
#
|
|
132
|
+
# If, instead, you want the enum value to be validated before saving, use the
|
|
133
|
+
# +:validate+ option:
|
|
134
|
+
#
|
|
135
|
+
# class Conversation < ActiveRecord::Base
|
|
136
|
+
# enum :status, [ :active, :archived ], validate: true
|
|
137
|
+
# end
|
|
138
|
+
#
|
|
139
|
+
# conversation = Conversation.new
|
|
140
|
+
#
|
|
141
|
+
# conversation.status = :unknown
|
|
142
|
+
# conversation.valid? # => false
|
|
143
|
+
#
|
|
144
|
+
# conversation.status = nil
|
|
145
|
+
# conversation.valid? # => false
|
|
146
|
+
#
|
|
147
|
+
# conversation.status = :active
|
|
148
|
+
# conversation.valid? # => true
|
|
149
|
+
#
|
|
150
|
+
# You may also pass additional validation options:
|
|
151
|
+
#
|
|
152
|
+
# class Conversation < ActiveRecord::Base
|
|
153
|
+
# enum :status, [ :active, :archived ], validate: { allow_nil: true }
|
|
154
|
+
# end
|
|
155
|
+
#
|
|
156
|
+
# conversation = Conversation.new
|
|
157
|
+
#
|
|
158
|
+
# conversation.status = :unknown
|
|
159
|
+
# conversation.valid? # => false
|
|
160
|
+
#
|
|
161
|
+
# conversation.status = nil
|
|
162
|
+
# conversation.valid? # => true
|
|
163
|
+
#
|
|
164
|
+
# conversation.status = :active
|
|
165
|
+
# conversation.valid? # => true
|
|
100
166
|
module Enum
|
|
101
167
|
def self.extended(base) # :nodoc:
|
|
102
168
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
|
103
169
|
end
|
|
104
170
|
|
|
105
|
-
def inherited(base) # :nodoc:
|
|
106
|
-
base.defined_enums = defined_enums.deep_dup
|
|
107
|
-
super
|
|
108
|
-
end
|
|
109
|
-
|
|
110
171
|
class EnumType < Type::Value # :nodoc:
|
|
111
172
|
delegate :type, to: :subtype
|
|
112
173
|
|
|
113
|
-
def initialize(name, mapping, subtype)
|
|
174
|
+
def initialize(name, mapping, subtype, raise_on_invalid_values: true)
|
|
114
175
|
@name = name
|
|
115
176
|
@mapping = mapping
|
|
116
177
|
@subtype = subtype
|
|
178
|
+
@_raise_on_invalid_values = raise_on_invalid_values
|
|
117
179
|
end
|
|
118
180
|
|
|
119
181
|
def cast(value)
|
|
120
|
-
return if value.blank?
|
|
121
|
-
|
|
122
182
|
if mapping.has_key?(value)
|
|
123
183
|
value.to_s
|
|
124
184
|
elsif mapping.has_value?(value)
|
|
125
185
|
mapping.key(value)
|
|
126
186
|
else
|
|
127
|
-
|
|
187
|
+
value.presence
|
|
128
188
|
end
|
|
129
189
|
end
|
|
130
190
|
|
|
131
191
|
def deserialize(value)
|
|
132
|
-
return if value.nil?
|
|
133
192
|
mapping.key(subtype.deserialize(value))
|
|
134
193
|
end
|
|
135
194
|
|
|
136
195
|
def serialize(value)
|
|
137
|
-
mapping.fetch(value, value)
|
|
196
|
+
subtype.serialize(mapping.fetch(value, value))
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def serializable?(value, &block)
|
|
200
|
+
subtype.serializable?(mapping.fetch(value, value), &block)
|
|
138
201
|
end
|
|
139
202
|
|
|
140
203
|
def assert_valid_value(value)
|
|
204
|
+
return unless @_raise_on_invalid_values
|
|
205
|
+
|
|
141
206
|
unless value.blank? || mapping.has_key?(value) || mapping.has_value?(value)
|
|
142
207
|
raise ArgumentError, "'#{value}' is not a valid #{name}"
|
|
143
208
|
end
|
|
144
209
|
end
|
|
145
210
|
|
|
211
|
+
attr_reader :subtype
|
|
212
|
+
|
|
146
213
|
private
|
|
147
|
-
attr_reader :name, :mapping
|
|
214
|
+
attr_reader :name, :mapping
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def enum(name = nil, values = nil, **options)
|
|
218
|
+
if name
|
|
219
|
+
values, options = options, {} unless values
|
|
220
|
+
return _enum(name, values, **options)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
|
224
|
+
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
|
225
|
+
|
|
226
|
+
definitions.each { |name, values| _enum(name, values, **options) }
|
|
227
|
+
|
|
228
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
|
229
|
+
Defining enums with keyword arguments is deprecated and will be removed
|
|
230
|
+
in Rails 8.0. Positional arguments should be used instead:
|
|
231
|
+
|
|
232
|
+
#{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
|
|
233
|
+
MSG
|
|
148
234
|
end
|
|
149
235
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
236
|
+
private
|
|
237
|
+
def inherited(base)
|
|
238
|
+
base.defined_enums = defined_enums.deep_dup
|
|
239
|
+
super
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
|
156
243
|
assert_valid_enum_definition_values(values)
|
|
244
|
+
assert_valid_enum_options(options)
|
|
157
245
|
# statuses = { }
|
|
158
246
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
|
159
247
|
name = name.to_s
|
|
@@ -166,73 +254,128 @@ module ActiveRecord
|
|
|
166
254
|
detect_enum_conflict!(name, name)
|
|
167
255
|
detect_enum_conflict!(name, "#{name}=")
|
|
168
256
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
257
|
+
attribute(name, **options)
|
|
258
|
+
|
|
259
|
+
decorate_attributes([name]) do |_name, subtype|
|
|
260
|
+
if subtype == ActiveModel::Type.default_value
|
|
261
|
+
raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
|
|
262
|
+
" backed by a database column or declared with an explicit type" \
|
|
263
|
+
" via `attribute`."
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
subtype = subtype.subtype if EnumType === subtype
|
|
267
|
+
EnumType.new(name, enum_values, subtype, raise_on_invalid_values: !validate)
|
|
172
268
|
end
|
|
173
269
|
|
|
270
|
+
value_method_names = []
|
|
174
271
|
_enum_methods_module.module_eval do
|
|
272
|
+
prefix = if prefix
|
|
273
|
+
prefix == true ? "#{name}_" : "#{prefix}_"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
suffix = if suffix
|
|
277
|
+
suffix == true ? "_#{name}" : "_#{suffix}"
|
|
278
|
+
end
|
|
279
|
+
|
|
175
280
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
|
176
281
|
pairs.each do |label, value|
|
|
177
|
-
if enum_prefix == true
|
|
178
|
-
prefix = "#{name}_"
|
|
179
|
-
elsif enum_prefix
|
|
180
|
-
prefix = "#{enum_prefix}_"
|
|
181
|
-
end
|
|
182
|
-
if enum_suffix == true
|
|
183
|
-
suffix = "_#{name}"
|
|
184
|
-
elsif enum_suffix
|
|
185
|
-
suffix = "_#{enum_suffix}"
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
value_method_name = "#{prefix}#{label}#{suffix}"
|
|
189
282
|
enum_values[label] = value
|
|
190
283
|
label = label.to_s
|
|
191
284
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
285
|
+
value_method_name = "#{prefix}#{label}#{suffix}"
|
|
286
|
+
value_method_names << value_method_name
|
|
287
|
+
define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
|
288
|
+
|
|
289
|
+
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
|
290
|
+
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
|
195
291
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
292
|
+
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
|
293
|
+
value_method_names << value_method_alias
|
|
294
|
+
define_enum_methods(name, value_method_alias, value, scopes, instance_methods)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
detect_negative_enum_conditions!(value_method_names) if scopes
|
|
199
299
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
300
|
+
if validate
|
|
301
|
+
validate = {} unless Hash === validate
|
|
302
|
+
validates_inclusion_of name, in: enum_values.keys, **validate
|
|
303
|
+
end
|
|
204
304
|
|
|
305
|
+
enum_values.freeze
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
class EnumMethods < Module # :nodoc:
|
|
309
|
+
def initialize(klass)
|
|
310
|
+
@klass = klass
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
private
|
|
314
|
+
attr_reader :klass
|
|
315
|
+
|
|
316
|
+
def define_enum_methods(name, value_method_name, value, scopes, instance_methods)
|
|
317
|
+
if instance_methods
|
|
318
|
+
# def active?() status_for_database == 0 end
|
|
319
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
|
320
|
+
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
|
321
|
+
|
|
322
|
+
# def active!() update!(status: 0) end
|
|
323
|
+
klass.send(:detect_enum_conflict!, name, "#{value_method_name}!")
|
|
324
|
+
define_method("#{value_method_name}!") { update!(name => value) }
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
if scopes
|
|
328
|
+
# scope :active, -> { where(status: 0) }
|
|
205
329
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
|
206
|
-
klass.scope value_method_name, -> { where(
|
|
330
|
+
klass.scope value_method_name, -> { where(name => value) }
|
|
207
331
|
|
|
332
|
+
# scope :not_active, -> { where.not(status: 0) }
|
|
208
333
|
klass.send(:detect_enum_conflict!, name, "not_#{value_method_name}", true)
|
|
209
|
-
klass.scope "not_#{value_method_name}", -> { where.not(
|
|
334
|
+
klass.scope "not_#{value_method_name}", -> { where.not(name => value) }
|
|
210
335
|
end
|
|
211
336
|
end
|
|
212
|
-
end
|
|
213
|
-
enum_values.freeze
|
|
214
337
|
end
|
|
215
|
-
|
|
338
|
+
private_constant :EnumMethods
|
|
216
339
|
|
|
217
|
-
private
|
|
218
340
|
def _enum_methods_module
|
|
219
341
|
@_enum_methods_module ||= begin
|
|
220
|
-
mod =
|
|
342
|
+
mod = EnumMethods.new(self)
|
|
221
343
|
include mod
|
|
222
344
|
mod
|
|
223
345
|
end
|
|
224
346
|
end
|
|
225
347
|
|
|
226
348
|
def assert_valid_enum_definition_values(values)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
349
|
+
case values
|
|
350
|
+
when Hash
|
|
351
|
+
if values.empty?
|
|
352
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
if values.keys.any?(&:blank?)
|
|
356
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
|
357
|
+
end
|
|
358
|
+
when Array
|
|
359
|
+
if values.empty?
|
|
360
|
+
raise ArgumentError, "Enum values #{values} must not be empty."
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
unless values.all?(Symbol) || values.all?(String)
|
|
364
|
+
raise ArgumentError, "Enum values #{values} must only contain symbols or strings."
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
if values.any?(&:blank?)
|
|
368
|
+
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
|
369
|
+
end
|
|
370
|
+
else
|
|
371
|
+
raise ArgumentError, "Enum values #{values} must be either a non-empty hash or an array."
|
|
232
372
|
end
|
|
373
|
+
end
|
|
233
374
|
|
|
234
|
-
|
|
235
|
-
|
|
375
|
+
def assert_valid_enum_options(options)
|
|
376
|
+
invalid_keys = options.keys & %i[_prefix _suffix _scopes _default _instance_methods]
|
|
377
|
+
unless invalid_keys.empty?
|
|
378
|
+
raise ArgumentError, "invalid option(s): #{invalid_keys.map(&:inspect).join(", ")}. Valid options are: :prefix, :suffix, :scopes, :default, :instance_methods, and :validate."
|
|
236
379
|
end
|
|
237
380
|
end
|
|
238
381
|
|
|
@@ -247,6 +390,8 @@ module ActiveRecord
|
|
|
247
390
|
raise_conflict_error(enum_name, method_name, type: "class")
|
|
248
391
|
elsif klass_method && method_defined_within?(method_name, Relation)
|
|
249
392
|
raise_conflict_error(enum_name, method_name, type: "class", source: Relation.name)
|
|
393
|
+
elsif klass_method && method_name.to_sym == :id
|
|
394
|
+
raise_conflict_error(enum_name, method_name)
|
|
250
395
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
|
251
396
|
raise_conflict_error(enum_name, method_name)
|
|
252
397
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
|
@@ -264,10 +409,16 @@ module ActiveRecord
|
|
|
264
409
|
}
|
|
265
410
|
end
|
|
266
411
|
|
|
267
|
-
def
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
412
|
+
def detect_negative_enum_conditions!(method_names)
|
|
413
|
+
return unless logger
|
|
414
|
+
|
|
415
|
+
method_names.select { |m| m.start_with?("not_") }.each do |potential_not|
|
|
416
|
+
inverted_form = potential_not.sub("not_", "")
|
|
417
|
+
if method_names.include?(inverted_form)
|
|
418
|
+
logger.warn "Enum element '#{potential_not}' in #{self.name} uses the prefix 'not_'." \
|
|
419
|
+
" This has caused a conflict with auto generated negative scopes." \
|
|
420
|
+
" Avoid using enum elements starting with 'not' where the positive form is also an element."
|
|
421
|
+
end
|
|
271
422
|
end
|
|
272
423
|
end
|
|
273
424
|
end
|